【发布时间】:2020-07-31 13:23:49
【问题描述】:
我的主应用程序 (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;
【问题讨论】:
-
假设所有其他工作都尝试添加一个命名互斥锁或其他东西,我在
WaitForInputIdlestate 上的注释:只等待一次进程的任何线程空闲;后续调用立即返回 -
将其缩减为简约示例并编译这些示例,无需任何其他代码。最好还是 32 位可执行文件。这将确保发送消息是罪魁祸首,而不是您在示例代码中删除的所有其他任务。
标签: delphi windows-10 handle sendmessage createprocess