Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WM_COPYDATA string not appearing in target application

I'm trying to pass information between two of my applications in Delphi 2010.

I'm using a simplified version of code that I've used successfully in the past (simplified because I don't need the sender to know that the send has been successful) I've boiled down the send received to a pair of example applications, which in essence are as follows

Send

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;

Receive Application

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

The major difference between the working test applications and the application I am adding the code to is that there is a lot of extra activity going on in target application. Especially on startup.

Any suggestions on why the WMCopyData procedure seems not to be firing at all?

CHeers

Dan

like image 288
Dan Kelly Avatar asked Jan 19 '23 20:01

Dan Kelly


1 Answers

There are a few problems with your code.

One, you are not assigning a unique ID to the message. The VCL, and various third-party components, also use WM_COPYDATA, so you have to make sure you are actually processing YOUR message and not SOMEONE ELSE'S message.

Two, you may not be waiting long enough for the second app to start. Instead of Sleep(), use ShellExecuteEx() with the SEE_MASK_WAITFORINPUTIDLE flag (or use CreateProcess() and WaitForInputIdle()).

Third, when starting the second app, your recursive logic is attempting to send the message a second time. If that happened to fail, you would launch a third app, and so on. You should take out the recursion altogether, you don't need it.

Try this:

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');

Receive Application

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');
like image 150
Remy Lebeau Avatar answered Jan 28 '23 10:01

Remy Lebeau