我有使用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() (他们之间有什么区别?),这也没有帮助。我试图改善一整天的情况,但不知道如何解决它。请帮我解决任何想法。
答案 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。
一旦确定要向正确的窗口发送消息,然后,您就可以担心消息未正确操作进度条的原因。我至少可以看到两个原因:
当DLL函数正在运行时,调用DLL的主程序不是。它正在等待DLL代码返回。这意味着您的主程序不处理任何消息。 DLL正在发布一堆消息,但它们尚未得到处理。在程序返回其消息循环之前,它们不会被处理。
Windows消息队列的默认大小为10,000。在发布之前,您将向队列发送10次以上的消息并且不处理任何消息,因此即使队列在开始之前完全为空(这不太可能,因为您可能从键盘或鼠标输入触发此功能) ,你只得到消息的十分之一。当队列已满时,PostMessage
只会丢弃该消息。而且,由于您使用消息发送的值是0到100之间的整数,因此当只有101个消息包含有意义的信息时,发送100,000个消息是毫无意义的。
答案 2 :(得分:0)
如果对FindWindow的相同调用返回不同的窗口,则必须有多个名为Form1的窗口。尝试给这些不同的窗口提供不同的名称,以便可以唯一地识别它们。
未使用的问题有点不清楚。也许你的意思是编译器注意到分配给tHWND的值从未被使用过,因此毫无意义。
我会最后评论这个问题是不精确的,这可能是你问题的一部分。例如,您说所有变量都未使用,但我们并不清楚您的意思。如果您更精确,更有条理,您将在调试方面取得更大成功。
答案 3 :(得分:0)