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

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

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

Основная форма получает сообщения Windows, отправленные RD 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 заполняется, и все замедляется
11.12.2008 18:00:56
Какую проблему вызывают сообщения Windows? IOW, что вы пытаетесь решить?
gabr 11.12.2008 19:32:21
Вы можете использовать сообщения Windows в написанной на Delphi службе Windows.
gabr 11.12.2008 20:02:33
Migrate2Lazarus see my profile 1.02.2019 14:13:52
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.
10
24.03.2016 15:46:28

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

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

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

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

2
11.12.2008 18:05:43

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

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

2
15.03.2012 11:35:32
Я бы не предложил добавлять новую библиотеку потоков просто для очереди сообщений. Используйте OmniThreadLibrary, если вы ищете другую библиотеку потоков (и она очень высокого качества).
Mick 12.12.2008 18:19:23
Согласен. Но вам не нужно использовать библиотеку потоков OTL, чтобы использовать коммуникационную подсистему. Модуль OtlComm в значительной степени автономен и может использоваться с любой моделью потоков, а также в однопоточных приложениях.
gabr 23.12.2008 11:23:28
Я этого не знал. Хорошо знать!
Mick 1.01.2009 17:58:56
Какие плюсы в использовании очереди сообщений по сравнению с другими решениями?
menjaraz 15.03.2012 11:37:23

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

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

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

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

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

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

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

2
23.05.2017 12:00:31

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

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

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

UIPI не является границей безопасности и не нацелен на защиту от всех разрушительных атак. Специальные возможности пользовательского интерфейса Приложения могут обойти UIPI, установив для их значения «uiAccess» значение ИСТИНА в составе своего файла манифеста. Для этого необходимо, чтобы приложение находилось в каталоге Program Files или 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.
0
12.12.2008 12:27:36

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

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

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

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

Работал лакомство

0
21.06.2009 22:47:23

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

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

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

0
22.06.2009 07:01:06