WM_COPYDATA字符串未出现在目标应用程序中

时间:2011-08-31 13:37:19

标签: delphi delphi-2010 wm-copydata

我正在尝试在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 个答案:

答案 0 :(得分:6)

您的代码存在一些问题。

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

二,你可能没有等到第二个应用程序启动的时间。使用带有Sleep()标记的ShellExecuteEx()代替SEE_MASK_WAITFORINPUTIDLE,或使用CreateProcess()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');

答案 1 :(得分:3)

我认为添加

是个好习惯
  copyDataStruct.dwData := Handle; 
procedure TMF.SendString;中的

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

并且

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

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

嵌套exit电话后应该缺少elseSendData

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?

答案 2 :(得分:0)

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

答案 3 :(得分:0)

如果应用程序需要启动,我认为当你调用它时(rh)句柄为0是个问题。但现在我看到SendData以递归方式调用自身。我在代码中添加了注释,因为它不明显。但现在又出现了另一个问题。 SendData的第二个实例将具有正确的句柄。但是然后你会弹出那个,回到句柄仍为0的第一个实例,然后你将再次调用SendMessage,这次使用0句柄。这可能不是你的麻烦的原因,但它是无意的,不必要的,完全是坏的。国际海事组织,这是一个通过试图太聪明而使事情复杂化的案例。