Delphi 运行应用程序并获取其窗口的句柄。我的代码没有';t无法在Windows 10 x64中正常工作
我的主应用程序(A)使用与数据库接口的配套应用程序(B)获取数据并写回结果(每个不同数据库的模式都需要不同的程序B)。 这两个应用程序以这种方式通信: 步骤1)A启动B通信它自己的句柄(A_WindowHandle)并冻结(?)来自B的等待响应 步骤2)B(在FormCreate中)将自己的B_WindowHandle发送回ADelphi 运行应用程序并获取其窗口的句柄。我的代码没有';t无法在Windows 10 x64中正常工作,delphi,windows-10,handle,sendmessage,createprocess,Delphi,Windows 10,Handle,Sendmessage,Createprocess,我的主应用程序(A)使用与数据库接口的配套应用程序(B)获取数据并写回结果(每个不同数据库的模式都需要不同的程序B)。 这两个应用程序以这种方式通信: 步骤1)A启动B通信它自己的句柄(A_WindowHandle)并冻结(?)来自B的等待响应 步骤2)B(在FormCreate中)将自己的B_WindowHandle发送回A //unit Global var ModoLnc : char; //Visible/Invisible/other(schedule
//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;
注意)现在每个应用程序都知道另一个应用程序的windowHandle,并且可以通过SendMessage进行通信
步骤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;
这两个应用程序都是在Windows XP中诞生的,它们一直工作得很好,直到现在它们第一次安装在Windows 10 X64中(它们在Windows 7 X64中工作)
问题是在Windows10x64之前,程序_A立即收到了B_窗口句柄;现在,来自B的消息延迟到达,或者更确切地说,A恢复执行得太早,假设B_WindowHandle很好,但是这个值仍然为零
我一直认为我的代码不是很好,因为(参见步骤1)程序被保证在收到CreateProcess、WaitForInputIdle的响应之前处于冻结状态;可能还不足以从B获得第一条消息(步骤2)
但它总是有效的,我忘记了,我必须注意以正确的方式编写代码。但哪种方式才是正确的呢
我一直在尝试推迟项目A,sleep(5000),就在项目B启动之后,但没有任何帮助
谢谢
埃多尔多
注:
- A和B是32位应用程序。A用Delphi 5编写,B用Delphi 7编写
- A和B安装在同一个文件夹_X中,该文件夹直接位于C下。我知道它不是建议的位置,但在该文件夹_X中有许多不同类型的子文件夹和文件,我喜欢将它们放在一起以便于使用(程序应该可以移植,但还没有)
- 运行的A和B实例从不超过1个
- 程序B可以通过不同的方式启动:通过程序A、计划、手动启动;这里只有第一种模式让我们感兴趣
- 当B启动时,它创建两个数据模块和主窗体(按此顺序)
- 执行A时,可以多次启动和关闭B(有时B窗体可见,有时不可见);每次A需要数据库中的数据或必须写入数据库时,都会检查B是否正在执行,如果没有,则启动它
- B不总是运行有两个原因:第一,有时B必须向用户显示某些内容,因此它必须是可见的,有时它不需要(我不知道如何将其从可见切换到不可见,反之亦然);其次,A和B处理大量数据,20年前,有一次,可用内存不足,最好在不使用B时关闭B
- 除了第一条消息迟到外,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;
与其使用A的
WaitForInputIdle()
(其中有警告!请参见和),不如让B在实际准备好通信之前不要将其HWND发送给A。例如,通过发布自己的私有消息,然后在B的消息循环处理该消息时将其HWND发送给a。并且不要假设在WaitForInputIdle()
退出时已收到WMsg01\u wpManigliaServer
。实际上,在尝试使用B的HWND之前,请等待WMsg01\u wpManigliaServer
到达
您也没有将HWND娱乐考虑在内。如果A或B的HWND在运行时发生更改(这可能会发生!),则需要再次交换新的HWND。我不会来回传递HWND,而是将这两个HWND存储在一个共享内存块中,该内存块由CreateFileMapping()
+MapViewOfFile()
分配,然后每当HWND中的任何一个发生更改时广播一条已注册的消息,并让a和B都侦听该消息。每当A或B需要向另一方发送消息时,使用该方当前存储在共享内存块中的HWND
或者更好的办法是,一开始就不要将HWNDs用于A和B之间的通信。改用更直接的通道,如管道、套接字、ActiveX/COM等。管道在
CreateProcess()
作为重定向的STDIN/STDOUT句柄时尤其有用,以便B用于与A通信。假设所有其他工作都可以尝试添加命名互斥体或其他东西,我在WaitForInputIdle
state上的注释:只等待一次进程的任何线程变为空闲;随后的调用会立即返回,将其分解为最简单的示例并编译这些示例,而无需任何其他代码。最好还是32位可执行文件。这将确保发送消息是罪魁祸首,而不是您在示例代码中删除的所有其他任务。