Строка WM_COPYDATA не отображается в целевом приложении - PullRequest
4 голосов
/ 31 августа 2011

Я пытаюсь передать информацию между двумя моими приложениями в 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, похоже, не запускается вообще?

Приветствия

Dan

Ответы [ 4 ]

6 голосов
/ 31 августа 2011

Есть несколько проблем с вашим кодом.

Во-первых, вы не присваиваете уникальный идентификатор сообщению.VCL и различные сторонние компоненты также используют WM_COPYDATA, поэтому вам необходимо убедиться, что вы действительно обрабатываете ВАШЕ сообщение, а не сообщение НЕКОТОРЫХ-ЛИБО.второе приложение для запуска.Вместо 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');
3 голосов
/ 31 августа 2011

Я думаю, это хорошая привычка добавлять

  copyDataStruct.dwData := Handle; 

in procedure TMF.SendString; - если у вас нет пользовательского идентификатора, установка исходного значения HWND поможет отладку в месте назначения (вы можете проверить это значение на другой стороне и, следовательно, избежать неправильного понимания широковещательной передачи WMCOPY_DATA, например - да не должно быть, но я видел некоторых!).

А

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

в TMF определении класса клиента, верно?

Должен быть пропущен exit или else после вложенного 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?

0 голосов
/ 31 августа 2011

Я думал, что была проблема с дескриптором (rh), равным 0, когда вы вызываете его, если необходимо запустить приложение. Но теперь я вижу, что SendData вызывает себя рекурсивно. Я добавил комментарий в код для этого, так как это было неочевидно. Но теперь есть другая проблема. Второй экземпляр SendData будет иметь правильный дескриптор. Но затем вы увидите, что вернетесь к первому экземпляру, где дескриптор все еще равен 0, и затем вы снова вызовете SendMessage, на этот раз с дескриптором 0. Это, вероятно, не является причиной вашей проблемы, но она непреднамеренная, ненужная и в целом плохая. ИМО, это тот случай, когда все усложняется попыткой быть слишком умным.

0 голосов
/ 31 августа 2011

Это больше не работает, если вы используете Windows 7. Если вы используете его, проверьте эту страницу, чтобы увидеть, как добавить исключение: http://msdn.microsoft.com/en-us/library/ms649011%28v=vs.85%29.aspx

...