Pregunta

Tengo una aplicación multiproceso (MIDAS) que utiliza mensajes de Windows para comunicarse consigo mismo.

FORMA PRINCIPAL

El formulario principal recibe mensajes de Windows enviados por RDM LogData ('Datatatolog')

Debido a que se utilizan mensajes de Windows, tienen los siguientes atributos

  1. Los mensajes recibidos son indivisibles.
  2. Los mensajes recibidos se ponen en cola en el orden en que se envían.

PREGUNTA:

¿Puede sugerir una mejor manera de hacer esto sin usar mensajes de Windows?

CÓDIGO DEL FORMULARIO PRINCIPAL

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;

CÓDIGO 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;

EDITAR:

Por qué quiero deshacerme de los mensajes de Windows:

  • Me gustaría convertir la aplicación en un servicio de Windows.
  • Cuando el sistema está ocupado, el búfer de mensajes de Windows se llena y todo se ralentiza
¿Fue útil?

Solución

El uso de canalizaciones con nombre. Si usted no sabe cómo usarlos, entonces ahora es el momento de aprender.

Con canalizaciones con nombre, puede enviar cualquier tipo de estructura de datos (siempre y cuando el servidor y el cliente sabe lo que es la estructura de datos). Yo suelo usar una tabla de registros para enviar grandes colecciones de información de ida y vuelta. Muy práctico.

utilizo componentes libres (y de código abierto) de canalización con nombre de Russell Libby. Viene con una TPipeServer y un componente visual TPipeClient. Hacen uso de canalizaciones con nombre increíblemente fácil, y canalizaciones con nombre son ideales para la comunicación entre procesos (IPC).

Usted puede obtener el componente aquí . La descripción de la fuente es: // Descripción: Conjunto de cliente y componentes de tuberías servidor llamado para Delphi, como // bien un componente de consola tubo de redirección.

Además, Russell me ayudó en Experto-Intercambio con el uso de una versión anterior de este componente para trabajar en una aplicación de consola para enviar / recibir mensajes a través de canalizaciones con nombre. Esto puede ayudar como guía para conseguir que en funcionamiento con el uso de sus componentes. Tenga en cuenta, que en una aplicación o servicio VCL, no es necesario escribir su propio bucle de mensajes como lo hice en esta aplicación de consola.

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.

Otros consejos

Opción 1: Custom Message Queue

Se puede construir una cola de mensajes personalizados, y empujar mensajes a la cola, ordenar la cola sobre la base de reglas de negocio, y mensajes emergentes de la cola desde el hilo principal para su procesamiento. Utilice una sección crítica para la sincronización.

Opción 2: devoluciones de llamada

Utilice las devoluciones de llamada para enviar datos de ida y vuelta de las roscas. Una vez más, utilizar una sección crítica para la sincronización.

OmniThreadLibrary contiene cola de mensajes muy eficiente en OtlComm.pas unidad.

La documentación no es muy buena en el momento ( comenzar aquí ) pero siempre se puede utilizar el foro .

Sí -. Gabr puede utilizar mensajes de ventanas en un servicio

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

Antes de Windows Vista, podría haber configurado el servicio para interactuar con el escritorio. Eso hace que el servicio que se ejecuta en el mismo escritorio como un usuario registrado, por lo que un programa que se ejecuta como el usuario puede enviar mensajes a las ventanas de su servicio. Windows Vista aísla los servicios, sin embargo; no pueden interactuar con el escritorio de cualquier usuario más.

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

Una cita de respuesta Rob Kennedy a 'TService no procesará los mensajes de los

Pero no será capaz de utilizar 'frmMain.Handle' para publicar mensajes del RDM a la forma principal en Windows Vista.

Todo lo que necesito hacer es encontrar una forma diferente de la fijación y recibir el mensaje

Los mensajes de Windows todavía se puede utilizar en Windows Vista! El problema en cuestión es que una tecnología en la vista llamada Interfaz de usuario de privilegios de aislamiento (UIPI) impide que los procesos con un nivel de integridad inferior (IL) de envío de mensajes a una proccess con una alta IL (por ejemplo, un servicio de ventanas tiene una alta IL y el usuario aplicaciones modo tienen medio IL).

Sin embargo, esto se puede omitir y aplicaciones IL medio puede ser permitido enviar wm de a procesos de alta IL.

Wikipedia lo dice mejor:

  

UIPI no es un límite de seguridad , y no tiene por objeto proteger contra   todos los ataques añicos. Accesibilidad de interfaz de usuario   Las aplicaciones pueden pasar por alto por UIPI   establecer su valor "uiAccess" TRUE   como parte de su archivo de manifiesto. Esta   requiere la aplicación para estar en el   Archivos de programa o el directorio de Windows, como se   así como para ser firmado por un código válido   la firma de la autoridad, pero estos   requisitos no serán necesariamente detener   respetando el malware de ellos.

     

Además, algunos mensajes son todavía se permite a través de, como   WM_KEYDOWN , que permite una IL inferior   proceso a la unidad de entrada a un elevado   símbolo del sistema.

     

Por último, la función   ChangeWindowMessageFilter permite una   proceso IL medio (todos los no-elevado   procesos excepto Internet Explorer   El modo protegido) para cambiar los mensajes   que un proceso de alta IL puede recibir   de un proceso de IL inferior. Esta   efectivamente permite pasar UIPI,   a menos que utilice Internet Explorer   o uno de sus procesos hijos.

Alguien más en Delphi-PRAXIS (el enlace está en alemán. Utilice Google para traducir la página) ya ha abordado este problema y publicado su código usando ChangeWindowMessageFilter. Creo que su problema es que WM_COPYDATA no funcionaría en Vista hasta que modificaron su código para evitar UIPI para WM_COPYDATA.

Enlace original (alemán)

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.

Los creadores de la biblioteca madExcept, etc., proporcionan la funcionalidad IPC que se puede utilizar en lugar de los mensajes de Windows.

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

Desarrollé un protector de pantalla de Windows en una etapa y quería que mi protector de pantalla enviara alguna notificación a otro programa, y ​​mientras el protector de pantalla estaba activo, no pude hacer que los mensajes de la ventana funcionaran entre las dos aplicaciones.

Lo reemplacé con la funcionalidad IPC mencionada anteriormente.

Funcionó de maravilla.

Yo uso esta biblioteca para IPC (+ utiliza la memoria compartida mutex): http://17slon.com/gp/gp/gpsync.htm

Tiene TGpMessageQueueReader y TGpMessageQueueWriter. Use "Global \" delante del nombre, por lo que se puede utilizar para la comunicación entre un servicio de Windows y un "Servicio GUI ayudante" cuando un usuario inicie sesión. (Se necesita el Global \ prefijo para Vista debido a los anillos de seguridad de la sesión, sino también para Windows XP / 2003 entre sesiones de usuario).

Es muy rápido, multiproceso, etc. Me gustaría utilizar éste en lugar de WM_COPYDATA (sobrecarga lenta y mucho más si lo usa mucho, pero para las cosas pequeñas mensajes puede estar bien)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top