Ищите альтернативу сообщениям Windows, используемым в межпроцессном взаимодействии - PullRequest
10 голосов
/ 11 декабря 2008

У меня есть многопоточное приложение (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;

EDIT:

Почему я хочу избавиться от сообщений Windows:

  • Я хотел бы преобразовать приложение в службу Windows
  • Когда система занята - буфер сообщений Windows переполняется и все замедляется

Ответы [ 7 ]

10 голосов
/ 11 декабря 2008

Использовать именованные каналы. Если вы не знаете, как их использовать, то сейчас самое время научиться.

Используя именованные каналы, вы можете отправлять любой тип структуры данных (если сервер и клиент знают, что это за структура данных). Я обычно использую массив записей для отправки больших коллекций информации туда и обратно. Очень удобно.

Я использую бесплатные (и с открытым исходным кодом) компоненты именованных каналов Рассела Либби. Поставляется с TPipeServer и визуальным компонентом TPipeClient. Они делают использование именованных каналов невероятно простым, а именованные каналы отлично подходят для межпроцессного взаимодействия (IPC).

Вы можете получить компонент здесь . Описание из источника: // Описание: набор компонентов именованных каналов клиента и сервера для Delphi, as // хорошо компонент перенаправления консольной трубы.

Кроме того, Рассел помог мне в 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.
2 голосов
/ 11 декабря 2008

Да - Габр, вы можете использовать сообщения Windows в сервисе.

==============================

До выхода Windows Vista вы могли настроить службу для взаимодействия с рабочим столом. Это заставляет службу работать на том же рабочем столе, что и вошедший в систему пользователь, поэтому программа, работающая под этим пользователем, может отправлять сообщения в окна вашей службы. Windows Vista изолирует сервисы; они больше не могут взаимодействовать с рабочим столом любого пользователя.

=============================

Цитата из ответа Роба Кеннеди на "TService не будет обрабатывать сообщения"

Но я не смогу использовать 'frmMain.Handle' для отправки сообщений из RDM в основную форму в Windows Vista.

Все, что мне нужно сделать, это найти другой способ отправки и получения сообщения

2 голосов
/ 11 декабря 2008

OmniThreadLibrary содержит очень эффективную очередь сообщений в OtlComm.pas блоке.

Документация на данный момент не очень хороша ( начинайте здесь ), но вы всегда можете использовать форум .

2 голосов
/ 11 декабря 2008

Вариант 1: Пользовательская очередь сообщений

Вы можете создать собственную очередь сообщений и помещать сообщения в очередь, сортировать очередь на основе бизнес-правил и извлекать сообщения из очереди из основного потока для обработки. Используйте критический раздел для синхронизации.

Вариант 2: Обратные вызовы

Используйте обратные вызовы для отправки данных из потоков. Опять же, используйте критическую секцию для синхронизации.

0 голосов
/ 22 июня 2009

Я использую эту библиотеку для IPc (использует общую память + мьютекс): http://17slon.com/gp/gp/gpsync.htm

Он имеет TGpMessageQueueReader и TGpMessageQueueWriter. Используйте «Global \» перед именем, чтобы вы могли использовать его для связи между службой Windows и «помощником по графическому интерфейсу службы», когда пользователь входит в систему. но также и для Windows XP / 2003 между сеансами пользователя).

Это очень быстрый, многопоточный и т. Д. Я бы использовал этот вместо WM_COPYDATA (медленный и много накладных расходов, если вы часто его используете, но для мелких вещей сообщения могут быть в порядке)

0 голосов
/ 22 июня 2009

Создатели библиотеки madExcept и т. Д. Предоставляют функциональность IPC, которую можно использовать вместо сообщений Windows.

http://help.madshi.net/IPC.htm

Я разработал заставку Windows на одном этапе, и я хотел, чтобы заставка заставляла отправлять некоторые уведомления другой программе, и, пока заставка была активной, я не мог получать сообщения окна для работы между двумя приложениями.

Я заменил его на функциональность IPC, упомянутую выше.

работал лакомством.

0 голосов
/ 12 декабря 2008

Сообщения Windows могут все еще использоваться в Windows Vista! Проблема в том, что технология Vista под названием «Изоляция привилегий пользовательского интерфейса» (UIPI) не позволяет процессам с более низким уровнем целостности (IL) отправлять сообщения процессу с высоким IL (например, служба Windows имеет высокий IL и пользовательский интерфейс). приложения в режиме имеют средний IL).

Однако это можно обойти, и приложениям среднего IL можно разрешить отправлять wm-файлы процессам с высоким IL.

Википедия говорит, что лучше всего:

UIPI не является границей безопасности и не направлен на защиту от все разрушающие атаки. Доступность пользовательского интерфейса Приложения могут обойти UIPI путем устанавливая их значение "uiAccess" в TRUE как часть их файла манифеста. это требует, чтобы приложение находилось в Программные файлы или каталог Windows, как а также быть подписанным действительным кодом право подписи, но эти требования не обязательно прекратятся вредоносное ПО от уважения к ним.

Кроме того, некоторые сообщения по-прежнему пропускаются, например: WM_KEYDOWN , что позволяет снизить IL Процесс для ввода ввода к повышенным командная строка.

Наконец, функция ChangeWindowMessageFilter позволяет средний процесс IL (все без повышенных процессы кроме Internet Explorer Защищенный режим) для изменения сообщений что процесс с высоким IL может получить от более низкого процесса 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.
...