Search code examples
c++delphidllpostmessage

Problems with PostMessage from C++ .dll to Delphi Forms application


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.


Solution

  • 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;