PostMessage从C ++ .dll到Delphi Forms应用程序的问题

时间:2012-06-07 14:36:41

标签: c++ delphi dll postmessage

我有使用MFC编写的Delphi 7和C ++ .dll编写的Windows窗体应用程序。

目前我正在尝试实现从.dll到主要可执行文件的基本消息发布,以显示进度条上的用户计算过程,但是面临几个问题。

让我先描述一下我的方法。我在我的Delphi应用程序中注册了简单的消息,如:

WM_MSG := RegisterWindowMessage('WM_MSG');

并在图书馆部分做同样的事情:

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

没关系:调试时我可以看到双方都有相同的值。

我的库函数看起来像这样(只是一个测试进度条的虚拟示例):

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

可执行 OnMessage 事件:

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 ++函数调用:

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

第一个问题是 tHWND hWnd 变量值莫名其妙地不同。经过一些研究,我发现了3种情况: 1.消极或积极的巨大 hWnd 2.零 hWnd 3.未定义('???')

在所有情况下,变量 hWnd 都标记为 unused ,我不知道这是什么意思。最有趣的是,如果我以非常简单的Delphi形式(只有一个单元)测试它,代码就能工作。这个简单的Delphi表单适用于真实的C ++ .dll代码,可以计算实际数据。但是当我使用我的一般Delphi应用程序(许多单元但仍然是一种形式)时,似乎主应用程序OnMessage事件没有捕获来自C ++ DLL的任何事件。

所以,有两个问题: 1.为什么 hWnd 值始终不同,为什么它们“未使用”? 2.如何强制我的主应用程序与进度条一起正常工作?

我一直在使用不同的方法来解决这个问题。例如将 Application.Handle Form1.Handle 作为函数参数传递给C ++库。他们都没有工作甚至没有说过通过时参数值的变化(我想这应该是单独的问题)。我也尝试使用 :: FindWindow() :: PostMessage()而不是 FindWindow() PostMessage() (他们之间有什么区别?),这也没有帮助。我试图改善一整天的情况,但不知道如何解决它。请帮我解决任何想法。

4 个答案:

答案 0 :(得分:3)

除了其他人所说的,更好的设计是让EXE直接将HWND传递给DLL,然后DLL不必去寻找它。这有一个额外的好处,EXE可以决定DLL应该发布哪个HWND消息。我会使用AllocateHWnd()为其创建一个专用窗口。

试试这个:

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 :(得分:1)

FindWindow的结果可以是零或非零。句柄值不在数字行上。它们只是不同的值,因此将不等式运算符应用于它们是没有意义的。换句话说,句柄值可能看起来是负数,因此不要假设有效句柄总是大于而不是零。

如果窗口句柄值不匹配,那么毫无疑问没有其他工作。由于您甚至不确定是否将它们发送到正确的窗口,因此您无法调试消息的功能。专注于先解决问题。

仅使用FindWindow作为最后的手段。它没有提供检测何时有多个窗口符合您的搜索条件的工具。它总是返回一个结果。如果可能,请避免搜索。相反,告诉发件人确切地向哪个窗口发送消息。你说你试过这个并且失败了,但我敦促你再去追求那条道路。你遇到的问题可能是一个不匹配的调用约定。确保DLL和主机应用程序都使用stdcall。


一旦确定要向正确的窗口发送消息,然后,您就可以担心消息未正确操作进度条的原因。我至少可以看到两个原因:

  1. 当DLL函数正在运行时,调用DLL的主程序不是。它正在等待DLL代码返回。这意味着您的主程序不处理任何消息。 DLL正在发布一堆消息,但它们尚未得到处理。在程序返回其消息循环之前,它们不会被处理。

  2. Windows消息队列的默认大小为10,000。在发布之前,您将向队列发送10次以上的消息并且不处理任何消息,因此即使队列在开始之前完全为空(这不太可能,因为您可能从键盘或鼠标输入触发此功能) ,你只得到消息的十分之一。当队列已满时,PostMessage只会丢弃该消息。而且,由于您使用消息发送的值是0到100之间的整数,因此当只有101个消息包含有意义的信息时,发送100,000个消息是毫无意义的。

答案 2 :(得分:0)

如果对FindWindow的相同调用返回不同的窗口,则必须有多个名为Form1的窗口。尝试给这些不同的窗口提供不同的名称,以便可以唯一地识别它们。

未使用的问题有点不清楚。也许你的意思是编译器注意到分配给tHWND的值从未被使用过,因此毫无意义。

我会最后评论这个问题是不精确的,这可能是你问题的一部分。例如,您说所有变量都未使用,但我们并不清楚您的意思。如果您更精确,更有条理,您将在调试方面取得更大成功。

答案 3 :(得分:0)

好吧,好吧,我似乎已经解决了这个问题...... 我尝试了Remy's提案来声明导出的函数

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

以cdecl调用约定为David建议。我以前的函数声明看起来像这样

TMyFunction = function (fHWND: HWND): Integer;

我认为这就是问题所在。谢谢大家的帮助!

P.S。现在,我该如何解决这个问题?