2

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

目前我正在尝试实现从 .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;

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

在所有情况下,变量hWnd都被标记为未使用,我不知道这是什么意思。最有趣的是,如果我以非常简单的 Delphi 形式(只有一个单元)对其进行测试,代码确实可以工作。这个简单的 Delphi 表单与我计算真实数据的真实 C++ .dll 代码配合得很好。但是当我使用我的一般 Delphi 应用程序(许多单元但仍然是一种形式)时,似乎主应用程序 OnMessage 事件没有从 C++ dll 捕获任何事件。

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

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

4

4 回答 4

3

除了其他人所说的,更好的设计是让 EXEHWND直接将其传递到 DLL 中,然后 DLL 不必去寻找它。这有一个额外的好处,即 EXE 可以决定HWNDDLL 应该将其消息发布到哪个。我会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;   
于 2012-06-07T19:44:54.890 回答
1

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

如果窗口句柄值不匹配,那么难怪没有其他方法起作用。您无法调试消息的功能,因为您甚至不确定是否将它们发送到正确的窗口。专注于首先解决这个问题。

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


一旦您确定将消息发送到正确的窗口,您可以担心为什么您的消息没有正确操作进度条。我至少可以看到两个原因:

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

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

于 2012-06-07T18:50:42.820 回答
0

如果对 FindWindow 的相同调用返回不同的窗口,那么您必须有多个名称为 Form1 的窗口。尝试为这些不同的窗口赋予不同的名称,以便可以唯一标识它们。

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

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

于 2012-06-07T16:10:17.760 回答
0

好吧,看来我已经解决了问题...我尝试了Remy 的声明导出函数的建议

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

正如大卫建议的那样,使用 cdecl 调用约定。我之前的函数声明是这样的

TMyFunction = function (fHWND: HWND): Integer;

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

PS现在,我该如何结束这个问题?

于 2012-06-09T10:01:00.920 回答