4

我正在尝试在 Delphi 2010 中的两个应用程序之间传递信息。

我正在使用我过去成功使用过的代码的简化版本(简化是因为我不需要发件人知道发送已成功)我已经将收到的发送归结为一对示例应用程序,其本质如下

发送

procedure TMF.SendString;
var
   copyDataStruct: TCopyDataStruct;
   s: AnsiString;
begin
   s := ebFirm.Text;
   copyDataStruct.cbData := 1 + length(s);
   copyDataStruct.lpData := PAnsiChar(s);
   SendData(copyDataStruct);
end;

procedure TMF.SendData(copyDataStruct: TCopyDataStruct);
var
   rh: THandle;
   res: integer;
begin
   rh := FindWindow(PChar('TMF'), PChar('Get Phone'));
   if rh = 0 then
   begin
      // Launch the target application
      ShellExecute(Handle, 'open', GetPhone, nil, nil, SW_SHOWNORMAL);
      // Give time for the application to launch  
      Sleep(3000);
      SendData(copyDataStruct); // RECURSION!
   end;
   SendMessage(rh, WM_COPYDATA, Integer(Handle), Integer(@copyDataStruct));
end;

接收申请

procedure TMF.WMCopyData(var Msg: TWMCopyData);
var
   s : AnsiString;
begin
   s := PAnsiChar(Msg.CopyDataStruct.lpData) ;
   jobstatus.Panels[1].Text := s;
end;

工作测试应用程序和我添加代码的应用程序之间的主要区别在于目标应用程序中正在进行大量额外活动。尤其是在启动时。

关于为什么 WMCopyData 过程似乎根本没有触发的任何建议?

干杯

4

4 回答 4

6

您的代码存在一些问题。

一,您没有为消息分配唯一的 ID。VCL 和各种第三方组件也使用WM_COPYDATA,因此您必须确保您实际处理的是您的消息,而不是其他人的消息。

第二,您可能没有等待足够长的时间来启动第二个应用程序。而不是Sleep()ShellExecuteEx()SEE_MASK_WAITFORINPUTIDLE标志一起使用(或使用CreateProcess()and WaitForInputIdle())。

第三,在启动第二个应用程序时,您的递归逻辑正在尝试第二次发送消息。如果碰巧失败,您将启动第三个应用程序,依此类推。你应该完全去掉递归,你不需要它。

试试这个:

var
  GetPhoneMsg: DWORD = 0;

procedure TMF.SendString;
var
  copyDataStruct: TCopyDataStruct;
  s: AnsiString;
begin
  if GetPhoneMsg = 0 then Exit;
  s := ebFirm.Text;
  copyDataStruct.dwData := GetPhoneMsg;
  copyDataStruct.cbData := Length(s);
  copyDataStruct.lpData := PAnsiChar(s);
  SendData(copyDataStruct);
end;

procedure TMF.SendData(copyDataStruct: TCopyDataStruct);
var
  rh: HWND;
  si: TShellExecuteInfo;
  res: Integer;
begin
  rh := FindWindow(PChar('TMF'), PChar('Get Phone'));
  if rh = 0 then
  begin
    // Launch the target application and give time to start
    ZeroMemory(@si, SizeOf(si));
    si.cbSize := SizeOf(si);
    si.fMask := SEE_MASK_WAITFORINPUTIDLE;
    si.hwnd := Handle;
    si.lpVerb := 'open';
    si.lpFile := GetPhone;
    si.nShow := SW_SHOWNORMAL;
    if not ShellExecuteEx(@si) then Exit;
    rh := FindWindow(PChar('TMF'), PChar('Get Phone'));
    if rh = 0 then Exit;
  end;
  SendMessage(rh, WM_COPYDATA, WParam(Handle), LParam(@copyDataStruct));
end;

initialization
  GetPhoneMsg := RegisterWindowMessage('TMF_GetPhone');

接收申请

var
  GetPhoneMsg: DWORD = 0;

procedure TMF.WMCopyData(var Msg: TWMCopyData);
var
  s : AnsiString;
begin
  if (GetPhoneMsg <> 0) and (Msg.CopyDataStruct.dwData = GetPhoneMsg) then
  begin
    SetString(s, PAnsiChar(Msg.CopyDataStruct.lpData), Msg.CopyDataStruct.cbData);
    jobstatus.Panels[1].Text := s;
  end else
    inherited;
end;

initialization
  GetPhoneMsg := RegisterWindowMessage('TMF_GetPhone');
于 2011-08-31T18:35:43.830 回答
3

我认为添加是一个好习惯

  copyDataStruct.dwData := Handle; 

in procedure TMF.SendString;- 如果您没有自定义标识符,则放置源 HWND 值将有助于在目标上进行调试(您可以在另一侧检查此值,从而避免误解广播的 WMCOPY_DATA 例如 - 是的,不应该有,但我见过一些!)。

    procedure WMCopyData(var Msg : TWMCopyData); message WM_COPYDATA;

TMF客户端类定义中,对吗?

嵌套调用应该有一个缺失exitelse之后:SendData

procedure TMF.SendData(copyDataStruct: TCopyDataStruct);
  (...)
      Sleep(3000);
      SendData(copyDataStruct);
   end else
     SendMessage(rh, WM_COPYDATA, NativeInt(Handle), NativeInt(@copyDataStruct));
end;

但这不会有太大变化。

检查rh := FindWindow()返回的句柄:它是TMF客户端表单的句柄,还是 Application.Handle?

于 2011-08-31T14:26:39.883 回答
0

如果您使用的是 Windows 7,则它不再起作用。如果您正在使用它,请查看此页面以查看如何添加例外:http: //msdn.microsoft.com/en-us/library/ms649011%28v= vs.85%29.aspx

于 2011-08-31T13:55:15.107 回答
0

如果需要启动应用程序,我认为 (rh) 句柄在调用时为 0 存在问题。但现在我看到 SendData 递归地调用自己。我为此在代码中添加了注释,因为它并不明显。但现在还有另一个问题。SendData 的第二个实例将具有正确的句柄。但是你会跳出那个,回到句柄仍然为 0 的第一个实例,然后你将再次调用 SendMessage,这次使用 0 句柄。这可能不是造成您麻烦的原因,但它是无意的、不必要的,而且完全是坏事。IMO,这是一个试图太聪明而使事情复杂化的案例。

于 2011-08-31T15:32:19.220 回答