从C++ .dll到德尔福表单应用程序的PostMessage问题
Problems with PostMessage from C++ .dll to Delphi Forms application
我有在Delphi 7上编写的Windows Forms应用程序,C++ .dll使用MFC编写的。
目前,我正在尝试实现从.dll到主可执行文件的基本消息发布,以在进度条上显示用户计算过程,但遇到了几个问题。
让我先描述一下我的方法。我在德尔福应用程序中注册了简单的消息,例如:
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;
}
可执行消息事件:
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.负或正巨大hWnd2. 零 hWnd3. 未定义("???")
在所有情况下,变量 hWnd 都被标记为未使用,我不知道这是什么意思。最有趣的是,如果我以非常简单的 Delphi 形式(只有一个单元)测试它,代码确实有效。这个简单的 Delphi 表单与计算真实数据的真实C++ .dll代码配合得很好。但是当我使用我的一般 Delphi 应用程序(许多单元但仍然是一个表单)时,似乎主应用程序 OnMessage 事件不会从 dll C++捕获任何事件。
所以,有两个问题:1. 为什么hWnd值总是不同的,为什么它们是"未使用"的?2. 如何强制我的主应用程序与进度条一起正常工作?
我一直在使用不同的方法来解决这个问题。例如将 Application.Handle 或 Form1.Handle 作为函数参数传递给C++库。他们都没有工作,甚至没有说在传递时更改了参数值(我想这应该是单独的问题)。我也尝试使用::FindWindow()和::P ostMessage()而不是FindWindow()和PostMessage()(它们之间有什么区别?),这也无济于事。我已经在尝试改善一整天的定位,但不知道如何解决它。请帮我提出任何想法。
除了其他人所说的之外,更好的设计是让 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;
FindWindow
的结果可以是零,也可以是非零。句柄值不在数字行上。它们只是不同的值,因此对它们应用不等式运算符是没有意义的。换句话说,句柄值可能显示为负数,因此不要假设有效句柄将始终大于零。
如果窗口句柄值不匹配,那么难怪其他方法都不起作用。您无法调试消息的功能,因为您甚至不确定是否将它们发送到正确的窗口。首先专注于解决这个问题。
仅将FindWindow
用作最后的手段。它不提供检测何时有多个窗口符合您的搜索条件的功能。它总是只返回一个结果。如果可能,请完全避免搜索。相反,请准确告诉发件人要将消息发送到哪个窗口。你说你尝试过这个,但失败了,但我敦促你再追求这条路。您遇到的问题可能是不匹配的调用约定。确保 DLL 和主机应用程序都使用 stdcall。
确定将消息
发送到正确的窗口后,您可以担心为什么消息无法正确操作进度条。我至少可以看到两个原因:
当 DLL 函数运行时,称为 DLL 的主程序不是。它正在等待 DLL 代码返回。这意味着您的主程序不处理任何消息。DLL 正在发布一堆消息,但它们尚未得到处理。在程序返回到其消息循环之前,它们不会得到处理。
Windows 消息队列的默认大小为 10,000。您向队列发布的消息数量是队列的 10 倍,并且在停止之前没有处理任何消息,因此即使队列在开始之前完全为空(这不太可能,因为您可能从键盘或鼠标输入触发此功能),您也只能收到十分之一的消息。当队列已满时,
PostMessage
只是丢弃该消息。由于您随消息一起发送的值是 0 到 100 之间的整数,因此当其中只有 101 个包含有意义的信息时,发送 100,000 个是毫无意义的。
如果对 FindWindow 的相同调用返回不同的窗口,则必须具有多个名为 Form1 的窗口。尝试为这些不同的窗口指定不同的名称,以便可以唯一地标识它们。
未使用的问题有点不清楚。也许你的意思是编译器已经注意到分配给tHWND的值从未被使用过,因此毫无意义。
我想最后评论一下,这个问题不准确,这可能是你问题的一部分。例如,您说所有变量都未使用,但我们不清楚您的意思。如果您更精确和有条不紊,您将在调试中取得更大的成功。
好吧,看来我已经解决了问题...我尝试了 Remy 声明导出函数的建议
function MyFunction (fHWND: HWND): Integer; cdecl; external 'My.dll'
按照大卫的建议,与 cdecl 召集惯例。我以前的函数声明如下所示
TMyFunction = function (fHWND: HWND): Integer;
我想这就是问题所在。谢谢大家的帮助!
附言现在,我该如何关闭问题?
- 试图在visual studio上用C++创建一个桌面应用程序
- FFmpeg:制作一个应用程序比直接使用ffmepg更好吗
- 在C应用程序中运行C++(带有STL)函数
- 使用VerQueryValue检索应用程序的文件描述
- Qt C++静态thread_local QNetworkAccessManager是线程应用程序的好选择吗
- 使用调试/崩溃报告将应用程序部署到客户端
- C++控制台应用程序阻止退出
- 码头化的C++应用程序是否向后兼容早期的内核版本
- 将应用程序从32位移植到64位时出现问题
- 如何改变c++应用程序的视觉效果
- WM_CTLCOLORSTATIC从未在WIN32应用程序中触发
- PC中的程序和PHONE中的本机描述应用程序之间的数据连接
- 应用程序崩溃并显示"symbol _ZdlPvm, version Qt_5 not defined in file libQt5Core.so.5 with link time reference"
- 示例外壳应用程序显示的 V8 "segmentation fault (core dumped)"错误
- phytec phyBOARD iMX-6在从闪存而不是SD卡运行qt5 opengles应用程序时表现不佳(FPS减半
- 为什么导入Mixed native/CLR lib.dll的本机C++应用程序没有在Mixed lib.dll中的外部变
- 如何从Windows应用程序输出到标准?
- C++应用程序 MySQL odbc 数据库连接错误:在引发"otl_tmpl_exception<>"实例后终止调用
- 如何在 64 位 vb.net Windows 应用程序中引用 32 位 dll
- 如果整个应用程序是虚拟映射的,为什么 new 会进行系统调用?