Строка 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 ответа

Решение

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

  copyDataStruct.dwData := Handle; 

в 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?

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

Во-первых, вы не назначаете уникальный идентификатор сообщения. 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');

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

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

Другие вопросы по тегам