delphi - 运行应用程序并获取其窗口的句柄。我的代码在 Windows 10 x64 中无法正常工作
问题描述
我的主应用程序 (A) 使用与数据库接口的配套应用程序 (B) 获取数据并写回结果(每个不同的数据库架构都需要不同的程序 B)。这两个应用程序以这种方式通信:
第 1 步)A 启动 B 通信它自己的句柄(A_WindowHandle)并冻结(?)等待来自 B 的响应
步骤 2)B(在 FormCreate 中)将自己的 B_WindowHandle 发送回 A
注意)现在每个应用程序都知道另一个应用程序的 windowHandle 并且可以通过 SendMessage 进行通信。
步骤 3) A 恢复工作,假设已收到 B_WindowHandle(在 Win10 X64 中不正确)并立即 A 将其第一个请求发送给 B
这两个应用程序诞生于 Windows XP 中,直到现在它们首次安装在 Windows 10 X64 中时它们一直运行良好(它们在 Windows 7 X64 中运行)。
问题是在 Windows 10 X64 之前,Program_A 立即收到了 B_WindowHandle;现在来自 B 的消息延迟到达,或者更好的说法是,假设有一个好的 B_WindowHandle,A 恢复执行过早,但这个值仍然为零。
我一直认为我的代码不是很好,因为(参见步骤 1)program_A 保证会冻结,直到它得到 CreateProcess、WaitForInputIdle 的响应;可能不足以从 B 获得第一条消息(步骤 2)。
但它总是有效,我忘记了,我必须注意以正确的方式编写这段代码。但哪种方法是正确的?
在 Program_B 启动之后,我一直在尝试在 program_A、sleep(5000) 中延迟一些时间,但这并没有帮助。
谢谢爱德华多
笔记:
- A 和 B 是 32 位应用程序。A 用 Delphi 5 编写,B 用 Delphi 7 编写。
- A 和 B 安装在 C 下的同一个文件夹_X 中。我知道这不是建议的位置,但是在这个文件夹_X 中有很多不同类型的子文件夹和文件,我喜欢将它们放在一起以方便使用(程序应该变得可移植,但还没有)。
- 运行的 A 和 B 实例永远不会超过 1 个。
- 程序 B 可以以树的不同方式启动:通过 program_A、计划、手动;这里只有第一种模式让我们感兴趣。
- 当 B 启动时,它会创建两个数据模块和主窗体(按此顺序)。
- 在执行A时可以多次启动和关闭B(有时B窗体可见,有时不可见);每次 A 需要数据库中的数据或必须写入数据时,检查 B 是否正在执行,如果没有,则启动它。
- B 并不总是运行有两个原因:首先,有时 B 必须向用户显示一些东西,因此它必须是可见的,有时它不需要它(我不知道如何将它从可见切换到不可见,反之亦然) ; 其次,A 和 B 处理大量数据,有一次,20 年前,可用内存不多,最好在不使用时关闭 B。
- 除了第一条消息迟到之外,A 和 B 之间的所有通信都运行良好。
使用的代码:步骤 1 A 创建 B 与他的句柄通信
//in Program A:
...
try
//SAETXXXX : constant with the name program B
//ModLnc : String value: "V"/"I" Visible/Invisible
//Handle: : Windows Handle of A
RunCmd.RunCommand(SAETXXXX, ModLnc + #32 + IntToStr(Handle));
//here program_A supposes having received the message with <<<<<<
// handle of program_B which in Win10 X64 is not true <<<<<<
// See step 3
is_SaetXXXX_Vivo:= true; //i.e.: is_B_running:= true
except
raise EAnomalia.Create(MSG_MANCAPGMINTERFC);
end;
...
Procedure RunCommand(const Cmd, Params: String);
var
SI: TStartupInfo;
PI: TProcessInformation;
CmdLine: String;
begin
//Fill record with zero byte values
FillChar(SI, SizeOf(SI), 0);
//Set mandatory record field
SI.cb := SizeOf(SI);
//Ensure Windows mouse cursor reflects launch progress
SI.dwFlags := StartF_ForceOnFeedback;
//Set up command line
CmdLine := Cmd;
if Length(Params) > 0 then
CmdLine := CmdLine + #32 + Params;
//Try and launch child process. Raise exception on failure
Win32Check(
CreateProcess(
nil, PChar(CmdLine), nil, nil, False, 0, nil, nil, SI, PI));
//Wait until process has started its main message loop
WaitForInputIdle(PI.hProcess, Infinite);
//Close process and thread handles
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
步骤 2)B(在 FormCreate 中)将自己的 B_WindowHandle 发送回 A
//unit Global var
ModoLnc : char; //Visible/Invisible/other(scheduled launch)
HWNDApplCli: HWND; //A_WindowHandle
WMsg01 : Cardinal; //Windows idMessage (got from registration)
...
procedure TfmProgramB.FormCreate(Sender: TObject);
begin
is_FormVisibile:= true; //unit global var form must be visible (for the moment)
...
self.Caption:= TIT_PGM_CONN; //caption depends from connected DB
//creates some objects (TLists)
//reads Inifile and sets form position
//sets some date
....
try
NArgRic := NumberOfArguments(CmdLine, true);
if NArgRic = 1 then begin
//a* manually launched
.....
end else begin
//a* Launched by A .OR. Windows scheduled launch
....
ModoLnc:= ExtractArgument(CmdLine, 1, true)[1];
if (ModoLnc = MODLNC_VISIB) or
(ModoLnc = MODLNC_INVIS) then begin
//a* Launched by A
if (ModoLnc = MODLNC_INVIS) then begin
Application.ShowMainForm:= false;
is_FormVisibile:= false;
end;
//creates a MapFile used for future communication with program_A
hMapFile := CreateFileMapping (....
//m* get the same idMessage used by Program_A
WMsg01 := RegisterWindowMessage(MIOWMSG_01);
//a* gets A_WindowHandle
HWNDApplCli:= StrToInt(ExtractArgument(CmdLine, 2, true));
if HWNDApplCli <> 0 then begin
//HWNDApplCli: A_WindowHandle
//WMsg01 : idMessage
//WMsg01_wpManigliaServer: tells program_A, next prm is B_WindowHandle
//Self.Handle: B_WindowHandle
SendMessage(HWNDApplCli, WMsg01, WMsg01_wpManigliaServer,
Integer(Self.Handle));
end;
end else begin
//a* Windows scheduled launch
....
end;
end;
except
raise;
end;
end;
步骤 3) A 恢复工作,假设已收到 B_WindowHandle(在 Win10 X64 中不正确)并立即 A 将其第一个请求发送给 B
// here how program_A receives B_WindowHandle (but late)
procedure TfmProgramA.WndProc(Var TheMsg: TMessage);
begin
if TheMsg.Msg = WMsg01 then begin
case TheMsg.wParam of
WMsg01_wpManigliaServer :
begin
is_ManigliaServerRicevuta:= true; //is_B_Handle_received
...
HWNDApplSrv:= TheMsg.lParam;
end;
.....
end;
Inherited WndProc(TheMsg);
end;
解决方案
而不是 AWaitForInputIdle()
根本使用(有警告!请参阅this和this),只需让 B 在 B 实际准备好进行通信之前不要将其 HWND 发送给 A。例如,通过向自己发布一条私人消息,然后在 B 的消息循环处理该消息时将其 HWND 发送给 A。并且不要让 A假设在退出时WMsg01_wpManigliaServer
已经收到。WaitForInputIdle()
实际上在尝试使用 B 的 HWND 之前让 A 等待到达。WMsg01_wpManigliaServer
您也没有考虑 HWND 娱乐。如果 A 或 B 的 HWND 在运行时发生变化(这可能发生!),则需要再次交换新的 HWND。我不会来回传递 HWND,而是将两个 HWND 存储在一个使用CreateFileMapping()
+分配的共享内存块中MapViewOfFile()
,然后在任何一个 HWND 更改时广播一条注册消息,并让 A 和 B 都侦听该消息。每当 A 或 B 需要向另一方发送消息时,请使用该方当前存储在共享内存块中的 HWND。
或者更好的是,一开始就不要将 HWND 用于 A 和 B 之间的通信。改用更直接的通道,例如管道、套接字、ActiveX/COM 等。管道CreateProcess()
对于 B 用来与 A 通信的重定向 STDIN/STDOUT 句柄特别有用。
推荐阅读
- arrays - Strange array from .mat file. How to access the data?
- python - 如何减少给定 Python 代码的执行时间?
- audio - 显示来自立体声麦克风的信号
- many-to-many - 如何在 SPSS 中执行多对多或(至少)外连接
- javascript - “包装”一个函数是什么意思?
- javascript - 如何通过 KEY ASC 订购 localStorage?
- c# - Azure 计时器触发函数依赖注入问题
- javascript - 如何重新定位tradingview图表,以免离开屏幕左侧
- terraform-provider-gcp - 如何在 GCP Cloud Shell 中升级到最新的 terraform 版本
- c++ - 相对于相机校准的平移和旋转坐标是什么?