Поиск альтернативы сообщениям windows, используемым в межпроцессном взаимодействии


У меня есть многопоточное приложение (MIDAS), которое использует сообщения windows для связи с самим собой.

ОСНОВНАЯ ФОРМА

Основная форма принимает сообщения windows, отправленные RDM LogData (’DataToLog')

Поскольку используются сообщения windows, они имеют следующие атрибуты

  1. полученные сообщения Неделимы
  2. полученные сообщения помещаются в очередь в том порядке, в котором они были отправлены

Вопрос:

Можете ли вы предложить лучший способ сделать это без использования сообщений 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 10

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 (медленно и много накладных расходов, если вы используете его много, но для небольших вещей сообщения могут быть в порядке)