delphi - sistemas - Buscando una alternativa a los mensajes de Windows utilizados en la comunicación entre procesos
cooperacion entre procesos por comunicacion (7)
Tengo una aplicación multiproceso (MIDAS) que hace uso de los mensajes de Windows para comunicarse consigo mismo.
FORMA PRINCIPAL
El formulario principal recibe mensajes de Windows enviados por RDM LogData (''DataToLog'')
Debido a que se usan mensajes de Windows, tienen los siguientes atributos
- Los mensajes recibidos son indivisibles
- Los mensajes recibidos se ponen en cola en el orden en que se envían
PREGUNTA:
¿Puedes sugerir una manera mejor de hacerlo sin usar mensajes de Windows?
CÓDIGO 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 las cosas se ralentizan
¡Los mensajes de Windows PUEDEN aún ser utilizados en Windows Vista! El problema es que una tecnología en vista llamada Aislamiento de privilegios de interfaz de usuario (UIPI) impide que los procesos con un nivel de integridad inferior (IL) envíen mensajes a un proceso con un alto IL (por ejemplo, un servicio de Windows tiene un IL alto y las aplicaciones de modo tienen IL medio).
Sin embargo, esto puede pasarse por alto y las aplicaciones de IL medianas pueden enviar WM a procesos de alta IL.
UIPI no es un límite de seguridad , y no apunta a proteger contra todos los ataques de desintegración. Las aplicaciones de accesibilidad UI pueden eludir UIPI configurando su valor "uiAccess" en TRUE como parte de su archivo de manifiesto. Esto requiere que la aplicación esté en el directorio Archivos de programa o Windows, así como que esté firmada por una autoridad de firma de código válida, pero estos requisitos no impedirán necesariamente que el malware los respete.
Además, algunos mensajes todavía están permitidos, como WM_KEYDOWN , que permite que un proceso IL más bajo active la entrada a un símbolo del sistema elevado.
Finalmente, la función ChangeWindowMessageFilter permite que un proceso de IL medio (todos los procesos no elevados, excepto el Modo protegido de Internet Explorer) cambie los mensajes que un proceso de IL alto puede recibir de un proceso de IL más bajo. Esto efectivamente permite pasar por encima de UIPI, a menos que se ejecute desde Internet Explorer o uno de sus procesos secundarios.
Alguien en Delphi-PRAXIS (el enlace está en alemán. Usa Google para Traducir la página) ya ha abordado este problema y ha publicado su código usando ChangeWindowMessageFilter. Creo que su problema es que WM_COPYDATA no funcionaría en Vista hasta que modificaran su código para eludir UIPI para 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.
Los creadores de la biblioteca madExcept etc proporcionan 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 una notificación a otro programa, y aunque el protector de pantalla estaba activo, no pude conseguir que los mensajes de la ventana funcionaran entre las dos aplicaciones.
Lo reemplacé con la funcionalidad de IPC mencionada anteriormente.
Funcionó una delicia.
Opción 1: cola de mensajes personalizados
Puede crear una cola de mensajes personalizada y enviar mensajes a la cola, ordenar la cola en función de las reglas comerciales y extraer los mensajes de la cola de la secuencia principal para su procesamiento. Use una sección crítica para la sincronización.
Opción 2: retrollamadas
Use las devoluciones de llamada para enviar datos hacia adelante y hacia atrás desde los hilos. De nuevo, use una sección crítica para la sincronización.
Sí, Gabr puede usar los mensajes de Windows en un servicio.
===========================
Antes de Windows Vista, podría haber configurado su servicio para interactuar con el escritorio. Eso hace que el servicio se ejecute en el mismo escritorio que un usuario conectado, por lo que un programa que se ejecute como ese usuario podría enviar mensajes a las ventanas de su servicio. Sin embargo, Windows Vista aísla los servicios; ya no pueden interactuar con el escritorio de ningún usuario.
==========================
Una cita de Rob Kennedy responde a ''TService no procesará mensajes''
Pero no podré usar ''frmMain.Handle'' para publicar mensajes del RDM al formulario principal en Windows Vista.
Todo lo que necesito hacer es encontrar una forma diferente de publicar y recibir el mensaje
Use tuberías con nombre. Si no sabe cómo usarlos, entonces ahora es el momento de aprender.
Con las canalizaciones con nombre, puede enviar cualquier tipo de estructura de datos (siempre que tanto el servidor como el cliente sepan qué es esa estructura de datos). Usualmente utilizo una variedad de registros para enviar grandes colecciones de información de ida y vuelta. Muy útil.
Uso los componentes de tubería con nombre libre (y de código abierto) de Russell Libby. Viene con un TPipeServer y un componente visual TPipeClient. Hacen que el uso de las tuberías con nombre sea increíblemente fácil, y las tuberías con nombre son excelentes para la comunicación entre procesos (IPC).
Puedes obtener el componente aquí . La descripción de la fuente es: // Descripción: Conjunto de componentes de canal con nombre de cliente y servidor para Delphi, como // bien un componente de redirección de canal de consola.
Además, Russell me ayudó en Experts-Exchange con el uso de una versión anterior de este componente para trabajar en una aplicación de consola para enviar / recibir mensajes por conductos específicos. Esto puede ayudar como una guía para ponerlo en funcionamiento con el uso de sus componentes. Tenga en cuenta que en una aplicación o servicio VCL, no necesita 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.
Uso esta biblioteca para IPc (usa memoria compartida + mutex): http://17slon.com/gp/gp/gpsync.htm
Tiene TGpMessageQueueReader y TGpMessageQueueWriter. Use "Global /" delante del nombre, para que pueda usarlo para comunicarse entre un Servicio de Windows y un "Servicio GUI Helper" cuando un usuario inicia sesión. (El prefijo Global / es necesario para Vista debido a los anillos de seguridad de la sesión, pero también para Windows XP / 2003 entre sesiones de usuario).
Es muy rápido, multiproceso, etc. Utilizaría este en lugar de WM_COPYDATA (lento y muy elevado si lo usa mucho, pero para cosas pequeñas los mensajes pueden estar bien)
OmniThreadLibrary contiene cola de mensajes muy eficiente en la unidad OtlComm.pas
.
La documentación no es muy buena en este momento ( comience aquí ) pero siempre puede usar el foro .