2
votes

I have Windows Forms Application written on Delphi 7 and C++ .dll written using MFC.

Currently I'm trying to implement basic message posting from .dll to main executable to show user calculation process on progressbar, but several problems were faced.

Let me describe my approach first. I register simple message in my Delphi application like:

WM_MSG := RegisterWindowMessage('WM_MSG');

and do the same at the library part:

UINT nMsgID = RegisterWindowMessage(_T("WM_MSG"));

This is OK: I can see same values on both sides when debugging.

My library function looks like this (just a dummy example to test progress bar):

extern "C" __declspec(dllexport) int MyFunction() {  
  UINT nMsgID = RegisterWindowMessage(_T("WM_MSG"));
  HWND hWnd = FindWindow(NULL, "Form1");
  if (hWnd > 0)
    for (int i = 0; i < 100000; i++) {
      int param = ceil(100 * (double) i / (double) 100000);
      PostMessage(hWnd, nMsgID, param, NULL);
    }
  return 1;
}

Executable OnMessage event:

procedure TForm1.OnMessageEvent(var Msg: tagMSG; var Handled: Boolean);
begin
  Handled := True;
  if Msg.message = WM_MSG then
    ProgressBar1.Position := Msg.wParam
  else Handled := False;
end;

C++ function call from executable:

procedure TMyFunctionDLL.Execute;
var
  i: Integer;
  tHWND: HWND;
begin
  tHWND := FindWindow(nil, 'mainF');
  i := Func;
end;

First problem is that tHWND and hWnd variables values are inexplicably different. After some research I've discovered 3 situations: 1. Negative or positive huge hWnd 2. Zero hWnd 3. Undefined ('???')

In all cases variable hWnd is marked as unused and I don't know what does that mean. The most interesting thing is that code DOES work if I test it in very simple Delphi form (with only one unit). That simple Delphi form works well with my real C++ .dll code where real data is calculated. But when I use my general Delphi application (many units but still one form) it seems main application OnMessage event doesn't catch any events from C++ dll.

So, there are 2 questions: 1. why are hWnd values are always different and why are they 'unused'? 2. how can I force my main application to work correctly with progressbar?

I've been using different approaches to resolve this. Such as passing Application.Handle or Form1.Handle as function parameter to C++ library. None of them worked not even saying about parameter value changed while passing (I guess that should be separate question). Also I've tried using ::FindWindow() and ::PostMessage() instead of FindWindow() and PostMessage() (what is difference between them?), that didn't helped either. I'm trying to improve situtuation for whole day already but have no idea how to solve it. Help me with any ideas please.

4
FindWindow returns 0 when it fails. Negative HWND is no problem. One function looks for Form1 and the other for mainF so of course you get different window handles.David Heffernan
Sorry, I forgot to replace 'mainF' by 'Form1'. 'mainF' is my general application form name, 'Form1' is my test application form name. Think of 'mainF' as 'Form1' here please, I mean, errors occurs anyway.Rail Suleymanov

4 Answers

3
votes

In addition to what others have stated, a better design would be to have the EXE pass its HWND into the DLL directly, then the DLL does not have to go hunting for it. This has the added benefit that the EXE can then decide which HWND the DLL should post its messages to. I would use AllocateHWnd() to create a dedicated window for that.

Try this:

UINT nMsgID = RegisterWindowMessage(_T("WM_MSG")); 

extern "C" __declspec(dllexport) int __stdcall MyFunction(HWND hWnd) {   
    if ((nMsgID != 0) && (hWnd != NULL)) {
        for (int i = 0; i < 100000; i++) { 
            int param = ceil(100 * (double) i / (double) 100000); 
            PostMessage(hWnd, nMsgID, param, 0); 
        } 
    }
    return 1; 
} 

.

unit Unit1;

interface

...

var
  DllWnd: HWND = 0;

implementation

var
  WM_MSG: UINT = 0;

procedure TForm1.FormCreate(Sender: TObject); 
begin 
  DllWnd := AllocateHWnd(DllWndProc);
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
  if DllWnd <> 0 then
  begin
    DeallocateHWnd(DllWnd);
    DllWnd := 0;
  end;
end; 

procedure TForm1.DllWndProc(var Message: TMessage); 
begin 
  if (Message.Msg = WM_MSG) and (WM_MSG <> 0) then 
    ProgressBar1.Position := Message.WParam
  else
    Message.Result := DefWindowProc(DllWnd, Message.Msg, Message.WParam, Message.LParam); 
end; 

...

initialization
  WM_MSG := RegisterWindowMessage('WM_MSG');     

end.

.

uses
  Unit1;

function DllFunc(Wnd: HWND): Integer; stdcall; external 'My.dll' name 'MyFunction'; 

procedure TMyFunctionDLL.Execute;   
var   
  i: Integer;   
begin   
  i := DllFunc(DllWnd);   
end;   
1
votes

Results from FindWindow can either be zero or non-zero. Handle values don't lie on the number line. They're just distinct values, so it makes no sense to apply inequality operators to them. Put another way, handle values can appear to be negative, so don't assume that valid handles will always be greater than zero.

If the window-handle values don't match, then it's no wonder nothing else works. You're in no position to be debugging the functionality of your messages since you're not even sure you're sending them to the right window. Focus on fixing that first.

Only use FindWindow as a last resort. It has offers no facility for detecting when there are multiple windows that fit your search criteria. It always returns exactly one result. When possible, avoid searching at all. Instead, tell the sender exactly what window to send messages to. You say you tried this and failed, but I urge you to pursue that avenue some more. The problem you had was probably a mismatched calling convention. Make sure both the DLL and the host application use stdcall.


Once you're sure you're sending messages to the right window, then you can worry about why your messages aren't operating the progress bar correctly. I can see at least two reasons:

  1. While the DLL function is running, your main program, which called the DLL, is not. It's waiting for the DLL code to return. That means your main program is not handling any messages. The DLL is posting a bunch of messages, but they're not getting handled yet. They won't get handled until the program gets back to its message loop.

  2. The default size of the Windows message queue is 10,000. You're posting 10 times that many messages to the queue and not handling any before you stop, so even if the queue was completely empty before you started (which is unlikely, since you're probably triggering this functionality from keyboard or mouse input), you'd only get one-tenth the messages. When the queue is full, PostMessage just discards the message. And since the value you're sending with the message is an integer between 0 and 100, it's rather pointless to send 100,000 of them when only 101 of them will hold meaningful information.

0
votes

If identical calls to FindWindow return different windows then you must have multiple windows with the name Form1. Try giving these different windows different names so that they can be uniquely identified.

The unused question is a little unclear. Perhaps you mean that the compiler has noticed that the value assigned to tHWND is never used and is thus pointless.

I would make a final comment that the question is imprecise and this is probably part of your problem. For example you say that all the variables are unused but we have no clear idea what you mean. You will have more success in debugging if you are more precise and methodical.

0
votes

Allright, seems I've resolved the problem... I tried Remy's proposal to declare exported function

function MyFunction (fHWND: HWND): Integer; cdecl; external 'My.dll'

with cdecl calling convention as David adviced. My previous function declaration looked like this

TMyFunction = function (fHWND: HWND): Integer;

and I suppose that was the problem. Thank you all for your help!

P.S. Now, how can I close the question?