Delphi 运行应用程序并获取其窗口的句柄。我的代码没有';t无法在Windows 10 x64中正常工作

Delphi 运行应用程序并获取其窗口的句柄。我的代码没有';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

我的主应用程序(A)使用与数据库接口的配套应用程序(B)获取数据并写回结果(每个不同数据库的模式都需要不同的程序B)。 这两个应用程序以这种方式通信:

步骤1)A启动B通信它自己的句柄(A_WindowHandle)并冻结(?)来自B的等待响应

步骤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;

注意)现在每个应用程序都知道另一个应用程序的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之间的所有通信都很好
使用的代码: 第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;

与其使用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位可执行文件。这将确保发送消息是罪魁祸首,而不是您在示例代码中删除的所有其他任务。