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