【问题标题】:Run an application and get the handle to its window. My code doesn't work properly in Windows 10 x64运行应用程序并获取其窗口的句柄。我的代码在 Windows 10 x64 中无法正常工作
【发布时间】: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;


【问题讨论】:

  • 假设所有其他工作都尝试添加一个命名互斥锁或其他东西,我在WaitForInputIdle state 上的注释:只等待一次进程的任何线程空闲;后续调用立即返回
  • 将其缩减为简约示例并编译这些示例,无需任何其他代码。最好还是 32 位可执行文件。这将确保发送消息是罪魁祸首,而不是您在示例代码中删除的所有其他任务。

标签: delphi windows-10 handle sendmessage createprocess


【解决方案1】:

A 根本不使用WaitForInputIdle()(有一些警告!请参阅thisthis),只是让B 在B 真正准备好进行通信之前不要将其HWND 发送给A。例如,发布一条私人消息,然后在 B 的消息循环处理该消息时将其 HWND 发送给 A。并且不要假设WMsg01_wpManigliaServerWaitForInputIdle() 退出时已经收到。实际上在尝试使用 B 的 HWND 之前让 A 等待WMsg01_wpManigliaServer 到达。

您也没有考虑 HWND 娱乐。如果 A 或 B 的 HWND 在运行时发生变化(可能发生!),则需要再次交换新的 HWND。我不会来回传递 HWND,而是将两个 HWND 存储在分配有 CreateFileMapping()+MapViewOfFile() 的共享内存块中,然后在任一 HWND 更改时广播一条注册消息,并让 A 和 B 都监听信息。每当 A 或 B 需要向另一方发送消息时,请使用该方当前存储在共享内存块中的 HWND。

或者更好的是,一开始就不要在 A 和 B 之间的通信中使用 HWND。改用更直接的通道,例如管道、套接字、ActiveX/COM 等。管道对于 CreateProcess() 尤其有用,作为 B 用来与 A 通信的重定向 STDIN/STDOUT 句柄。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-12
    • 1970-01-01
    • 2020-10-04
    • 2021-06-17
    相关资源
    最近更新 更多