Строка 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 вообще не срабатывает?

Ваше здоровье

Дэн


person Dan Kelly    schedule 31.08.2011    source источник
comment
Что вам говорит отладчик?   -  person David Heffernan    schedule 31.08.2011
comment
Голосование закрыто, потому что решение проблемы не имело ничего общего ни с чем, написанным в вопросе или каким-либо из ответов.   -  person Rob Kennedy    schedule 31.08.2011
comment
@RobKennedy - Почему вы закрываете правильный вопрос и такие хорошие ответы?   -  person Z80    schedule 14.11.2017


Ответы (4)


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

  copyDataStruct.dwData := Handle; 

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

И

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

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

После вложенного вызова SendData должно быть отсутствующее exit или else:

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?

person Arnaud Bouchez    schedule 31.08.2011
comment
Не вижу причин, по которым вам нужно copyDataStruct.dwData := Handle - person David Heffernan; 31.08.2011
comment
Спасибо. Оказывается, в коде обновления для целевого приложения была ошибка, которая копировала более старое (т.е. текущее работающее приложение) поверх копии разработки. Это происходило только тогда, когда целевое приложение вызывалось из приложения-отправителя. Никаких проблем при нормальной работе или из Delphi. - person Dan Kelly; 31.08.2011
comment
@Arnaud Не могли бы вы уточнить, что вы имеете в виду под своим первым предложением относительно dwData. Если я не прав, я хотел бы знать, и если я прав, вы можете удалить его и не вводить людей в заблуждение. - person David Heffernan; 31.08.2011
comment
@David, я изменил комментарий, чтобы указать цель этой строки. - person Arnaud Bouchez; 31.08.2011
comment
@David Это не бессмысленно. См. мое замечание о фильтрации рассылаемого сообщения WM_COPYDATA. У вас может быть это из-за плохо спроектированного стороннего компонента в вашем собственном приложении! - person Arnaud Bouchez; 02.09.2011
comment
он дублирует то, что передается в wParam, и поэтому не нужен - person David Heffernan; 02.09.2011
comment
Разве wParam не является пользовательским значением, когда вы транслируете сообщение? Такой параметр есть в соответствующий API AFAIK. В этом причина моей точности - из эксперимента. Какая-то внутренняя подпись не обязательна, но безопаснее. - person Arnaud Bouchez; 27.04.2013

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

Во-первых, вы не присваиваете сообщению уникальный идентификатор. 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');
person Remy Lebeau    schedule 31.08.2011
comment
Спасибо за информацию о ShellExecuteEx. Похоже, лучший способ, чем рекурсия. Как уже упоминалось Крису выше, в if rh = 0 должно быть else, чтобы SendMessage не вызывался дважды. - person Dan Kelly; 01.09.2011
comment
Идеальный код. Ни один другой код отправки сообщения из Интернета не работал, включая Zarko и несколько других российских сайтов. Я получал неполную строку. Разработчики SO также были заняты пояснением комментариев к этому столь востребованному фрагменту, а не публикацией полного рабочего кода. Возможно, это было связано с тем, что моя машина была Win7 64-битной. - person user30478; 21.01.2019

Он больше не работает, если вы используете Windows 7. Если вы используете его, посетите эту страницу, чтобы узнать, как добавить исключение: http://msdn.microsoft.com/en-us/library

person Whiler    schedule 31.08.2011
comment
Использую Windows XP, но спасибо за наводку на будущее. - person Dan Kelly; 31.08.2011
comment
-1 Прошу прощения, но это очень заблуждение. WM_COPYDATA отлично работает в Vista и более поздних версиях, если уровень целостности процесса-получателя равен или ниже, чем у процесса-отправителя. - person David Heffernan; 31.08.2011
comment
Я должен согласиться с Дэвидом. Я лично использовал WM_COPYDATA с Windows 7... - person Andreas Rejbrand; 31.08.2011
comment
Хорошо... Извините за непонимание... Вероятно, у меня была эта проблема с разными уровнями... - person Whiler; 01.09.2011
comment
В Vista+, если UIPI блокирует получение сообщения от процесса с более низкой целостностью, и вы не хотите, чтобы оно блокировалось, получатель может использовать ChangeWindowMessageFilter/Ex(), чтобы пропустить сообщение. - person Remy Lebeau; 21.01.2019

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

person Chris Thornton    schedule 31.08.2011
comment
Спасибо что подметил это. На самом деле у меня есть else в исходном коде перед сообщением об отправке, но в приведенном выше примере он пропущен. - person Dan Kelly; 01.09.2011