首页 > 解决方案 > 运行应用程序并获取其窗口的句柄。我的代码在 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) 中延迟一些时间,但这并没有帮助。

谢谢爱德华多

笔记:

使用的代码:步骤 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;


标签: delphiwindows-10handlesendmessagecreateprocess

解决方案


而不是 AWaitForInputIdle()根本使用(有警告!请参阅thisthis),只需让 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 句柄特别有用。


推荐阅读