Поиск альтернативы сообщениям windows, используемым в межпроцессном взаимодействии
У меня есть многопоточное приложение (MIDAS), которое использует сообщения windows для связи с самим собой.
ОСНОВНАЯ ФОРМА
Основная форма принимает сообщения windows, отправленные RDM LogData (’DataToLog')
Поскольку используются сообщения windows, они имеют следующие атрибуты
- полученные сообщения Неделимы
- полученные сообщения помещаются в очередь в том порядке, в котором они были отправлены
Вопрос:
Можете ли вы предложить лучший способ сделать это без использования сообщений windows ?
ОСНОВНОЙ КОД ФОРМЫ
const
UM_LOGDATA = WM_USER+1002;
type
TLogData = Record
Msg : TMsgNum;
Src : Integer;
Data : String;
end;
PLogData = ^TLogData;
TfrmMain = class(TForm)
//
private
procedure LogData(var Message: TMessage); message UM_LOGDATA;
public
//
end;
procedure TfrmMain.LogData(var Message: TMessage);
var LData : PLogData;
begin
LData := PLogData(Message.LParam);
SaveData(LData.Msg,LData.Src,LData.Data);
Dispose(LData);
end;
КОД RDM
procedure TPostBoxRdm.LogData(DataToLog : String);
var
WMsg : TMessage;
LData : PLogData;
Msg : TMsgNum;
begin
Msg := MSG_POSTBOX_RDM;
WMsg.LParamLo := Integer(Msg);
WMsg.LParamHi := Length(DataToLog);
new(LData);
LData.Msg := Msg;
LData.Src := 255;
LData.Data := DataToLog;
WMsg.LParam := Integer(LData);
PostMessage(frmMain.Handle, UM_LOGDATA, Integer(Msg), WMsg.LParam);
end;
Правка:
Почему я хочу избавиться от сообщений windows:
- я хотел бы преобразовать приложение в службу windows
- когда система занята-буфер сообщений windows заполняется и все замедляется
7 ответов:
Используйте Именованные Каналы. Если вы не знаете, как ими пользоваться, то сейчас самое время научиться.
С помощью именованных каналов можно передавать любой тип структуры данных (если и сервер, и клиент знают, что это за структура данных). Я обычно использую массив записей для отправки больших коллекций информации туда и обратно. Очень удобно.
Я использую свободные (и с открытым исходным кодом) именованные компоненты канала Рассела Либби. Поставляется с TPipeServer и визуальным компонентом TPipeClient. Они делают использование именованных трубы невероятно просты, а именованные трубы отлично подходят для межпроцессной коммуникации (IPC).
Вы можете получить компонент здесь . Описание из источника: / / описание : набор клиентских и серверных именованных компонентов канала для Delphi, как // ну компонент перенаправления консольной трубы.
Кроме того, Рассел помог мне на Experts-Exchange с использованием более старой версии этого компонента для работы в консольном приложении для отправки / получения сообщений по именованным каналам. Это может помочь в качестве руководства в получении вас и работает с использованием его компонентов. Обратите внимание, что в приложении или сервисе VCL вам не нужно писать свой собственный цикл сообщений, как я сделал в этом консольном приложении.program CmdClient; {$APPTYPE CONSOLE} uses Windows, Messages, SysUtils, Pipes; type TPipeEventHandler = class(TObject) public procedure OnPipeSent(Sender: TObject; Pipe: HPIPE; Size: DWORD); end; procedure TPipeEventHandler.OnPipeSent(Sender: TObject; Pipe: HPIPE; Size: DWORD); begin WriteLn('On Pipe Sent has executed!'); end; var lpMsg: TMsg; WideChars: Array [0..255] of WideChar; myString: String; iLength: Integer; pcHandler: TPipeClient; peHandler: TPipeEventHandler; begin // Create message queue for application PeekMessage(lpMsg, 0, WM_USER, WM_USER, PM_NOREMOVE); // Create client pipe handler pcHandler:=TPipeClient.CreateUnowned; // Resource protection try // Create event handler peHandler:=TPipeEventHandler.Create; // Resource protection try // Setup clien pipe pcHandler.PipeName:='myNamedPipe'; pcHandler.ServerName:='.'; pcHandler.OnPipeSent:=peHandler.OnPipeSent; // Resource protection try // Connect if pcHandler.Connect(5000) then begin // Dispatch messages for pipe client while PeekMessage(lpMsg, 0, 0, 0, PM_REMOVE) do DispatchMessage(lpMsg); // Setup for send myString:='the message I am sending'; iLength:=Length(myString) + 1; StringToWideChar(myString, wideChars, iLength); // Send pipe message if pcHandler.Write(wideChars, iLength * 2) then begin // Flush the pipe buffers pcHandler.FlushPipeBuffers; // Get the message if GetMessage(lpMsg, pcHandler.WindowHandle, 0, 0) then DispatchMessage(lpMsg); end; end else // Failed to connect WriteLn('Failed to connect to ', pcHandler.PipeName); finally // Show complete Write('Complete...'); // Delay ReadLn; end; finally // Disconnect event handler pcHandler.OnPipeSent:=nil; // Free event handler peHandler.Free; end; finally // Free pipe client pcHandler.Free; end; end.
Вариант 1: Пользовательская Очередь Сообщений
Вы можете построить пользовательскую очередь сообщений и отправлять сообщения в очередь, сортировать очередь на основе бизнес-правил и отправлять сообщения из очереди из основного потока для обработки. Используйте критический Раздел для синхронизации.
Вариант 2: Обратные Вызовы
Используйте обратные вызовы для отправки данных из потоков. Опять же, используйте критический Раздел для синхронизации.
OmniThreadLibrary содержит очень эффективную очередь сообщений в блоке
OtlComm.pas
.Документация на данный момент не очень хороша (начните здесь ), но вы всегда можете использовать форум .
Yes-Gabr сообщения windows можно использовать в службе.
==============================
до появления Windows Vista можно было настроить службу для взаимодействия с рабочим столом. Это позволяет службе работать на том же рабочем столе, что и вошедший в систему пользователь, поэтому программа, работающая под этим именем, может отправлять сообщения в окна службы. Однако Windows Vista изолирует службы; они не могут взаимодействовать с рабочим столом любого пользователя больше не.
=============================
Цитата из ответа Роба Кеннеди на "TService не будет обрабатывать сообщения"
Но я не смогу использовать ' frmMain.Handle ' для отправки сообщений из RDM в основную форму в windows Vista.
Все, что мне нужно сделать, это найти другой способ отправки и получить сообщение
Сообщения Windows все еще можно использовать в Windows Vista! Проблема заключается в том, что технология в vista под названием User Interface Privilege Isolation (UIPI) не позволяет процессам с более низким уровнем целостности (IL) отправлять сообщения в процесс с высоким IL (например, служба windows имеет высокий IL, а приложения пользовательского режима-средний IL).
Однако это можно обойти, и приложениям среднего уровня IL можно разрешить отправлять wm в процессы высокого уровня IL.
UIPI не является границей безопасности и не ставит своей целью защиту от все атаки вдребезги. Доступность пользовательского интерфейса Приложения могут обойти UIPI путем установка значения "uiAccess" в TRUE как часть их файла манифеста. Этот требует, чтобы приложение находилось в Файлы программы или каталог Windows, как а также быть подписанным действительным кодом полномочия на подписание, но эти требования не обязательно прекратятся вред от уважения к ним.
Кроме того, некоторые сообщения все еще разрешены, такие как WM_KEYDOWN , что позволяет снизить IL процесс для того чтобы управлять входным сигналом к повышенному командная строка.
Наконец, функция ChangeWindowMessageFilter позволяет средний процесс IL (все не повышенные процессы, кроме Internet Explorer Защищенный режим) для изменения сообщений что высокий процесс IL может получить от более низкого процесса Ил. Этот эффективно позволяет обойти UIPI, если только не работает из Internet Explorer или один из его дочерних процессов.
Кто-то из Delphi-PRAXIS (ссылка на немецком языке. Используйте Google для перевода страницы) уже решил эту проблему и опубликовал свой код с помощью ChangeWindowMessageFilter. Я считаю, что их проблема заключается в том, что WM_COPYDATA не будет работать на Vista, пока они не изменят свой код, чтобы обойти UIPI для WM_COPYDATA.
Оригинальная Ссылка (Немецкий)
unit uMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, uallHook, uallProcess, uallUtil, uallKernel; type TfrmMain = class(TForm) lbl1: TLabel; tmrSearchCondor: TTimer; mmo1: TMemo; procedure FormCreate(Sender: TObject); procedure tmrSearchCondorTimer(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private-Deklarationen } fCondorPID : DWord; fInjected : Boolean; fDontWork : Boolean; procedure SearchCondor; procedure InjectMyFunctions; procedure UnloadMyFunctions; function GetDebugPrivileges : Boolean; procedure WriteText(s : string); procedure WMNOTIFYCD(var Msg: TWMCopyData); message WM_COPYDATA; public { Public-Deklarationen } end; var frmMain: TfrmMain; ChangeWindowMessageFilter: function (msg : Cardinal; dwFlag : Word) : BOOL; stdcall; implementation {$R *.dfm} type Tmydata = packed record datacount: integer; ind: boolean; end; const cCondorApplication = 'notepad.exe'; cinjComFuntionsDLL = 'injComFunctions.dll'; var myData : TMydata; procedure TfrmMain.WMNOTIFYCD(var Msg: TWMCopyData); begin if Msg.CopyDataStruct^.cbData = sizeof(TMydata) then begin CopyMemory(@myData,Msg.CopyDataStruct^.lpData,sizeof(TMyData)); WriteText(IntToStr(mydata.datacount)) end; end; procedure TfrmMain.WriteText(s : string); begin mmo1.Lines.Add(DateTimeToStr(now) + ':> ' + s); end; procedure TfrmMain.InjectMyFunctions; begin if not fInjected then begin if InjectLibrary(fCondorPID, PChar(GetExeDirectory + cinjComFuntionsDLL)) then fInjected := True; end; end; procedure TfrmMain.UnloadMyFunctions; begin if fInjected then begin UnloadLibrary(fCondorPID, PChar(GetExeDirectory + cinjComFuntionsDLL)); fInjected := False; end; end; procedure TfrmMain.SearchCondor; begin fCondorPID := FindProcess(cCondorApplication); if fCondorPID <> 0 then begin lbl1.Caption := 'Notepad is running!'; InjectMyFunctions; end else begin lbl1.Caption := 'Notepad isn''t running!'; end; end; procedure TfrmMain.FormDestroy(Sender: TObject); begin UnloadMyFunctions; end; function TfrmMain.GetDebugPrivileges : Boolean; begin Result := False; if not SetDebugPrivilege(SE_PRIVILEGE_ENABLED) then begin Application.MessageBox('No Debug rights!', 'Error', MB_OK); end else begin Result := True; end; end; procedure TfrmMain.FormCreate(Sender: TObject); begin @ChangeWindowMessageFilter := GetProcAddress(LoadLibrary('user32.dll'), 'ChangeWindowMessageFilter'); ChangeWindowMessageFilter(WM_COPYDATA, 1); fInjected := False; fDontWork := not GetDebugPrivileges; tmrSearchCondor.Enabled := not fDontWork; end; procedure TfrmMain.tmrSearchCondorTimer(Sender: TObject); begin tmrSearchCondor.Enabled := False; SearchCondor; tmrSearchCondor.Enabled := True; end; end.
Создатели библиотеки madExcept и т. д. предоставляют функциональность IPC, которую можно использовать вместо сообщений Windows.
Http://help.madshi.net/IPC.htm
Я разработал заставку Windows на одном этапе, и я хотел, чтобы моя заставка отправляла какое-то уведомление другой программе, и пока заставка была активна, я не мог заставить сообщения окна работать между двумя приложениями.
Я заменил его на упомянутую функциональность IPC выше.
Сработала.
Я использую эту библиотеку для IPc (использует общую память + мьютекс): http://17slon.com/gp/gp/gpsync.htm
Он имеет TGpMessageQueueReader и TGpMessageQueueWriter. Используйте "Global\ "перед именем, чтобы вы могли использовать его для связи между службой Windows и" вспомогательным графическим интерфейсом службы", когда пользователь входит в систему. (глобальный префикс\ необходим для Vista из-за колец безопасности сеанса, но также и для Windows XP/2003 между сеансами пользователя).
Он очень быстрый, многопоточный и т. д. Я будет использовать этот вместо WM_COPYDATA (медленно и много накладных расходов, если вы используете его много, но для небольших вещей сообщения могут быть в порядке)