Cercando un'alternativa a windows messaggi di inter-process communication
-
21-08-2019 - |
Domanda
Ho una applicazione multithread (MIDAS) che si avvale di messaggi di windows per comunicare con se stesso.
FORM PRINCIPALE
La forma principale riceve i messaggi di windows inviato da RDM LogData(‘DataToLog’)
Perché i messaggi di windows vengono utilizzati sono i seguenti attributi
- I messaggi ricevuti sono Indivisibili
- I messaggi ricevuti sono in Coda nell'ordine in cui sono inviati
DOMANDA:
Si può Suggerire un modo migliore di fare questo senza l'utilizzo di messaggi di windows ?
MODULO PRINCIPALE CODICE
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;
CODICE 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:
Perché voglio sbarazzarsi dei messaggi di windows:
- Vorrei trasformare la domanda in un servizio di windows
- Quando il sistema è occupato – il messaggio di windows buffer si riempie e cose rallenta
Soluzione
Utilizzare Le Named Pipe.Se non sai come utilizzarli, quindi ora è il momento di imparare.
Con le named pipe, è possibile inviare qualsiasi tipo di struttura dati (purché sia il server che il client di sapere ciò che struttura dati è).Io di solito uso un array di record per inviare grandi raccolte di informazioni avanti e indietro.Molto utile.
Io uso Russell Libby libero (open-source) named pipe componenti.Viene fornito con un TPipeServer e un TPipeClient componente visiva.Fanno tramite named pipe incredibilmente facile, e named pipe sono grandi per inter-process communication (IPC).
È possibile ottenere il componente qui.La descrizione della fonte:// Descrizione :Set di client e server named pipe componenti per Delphi, come // una console tubo componente reindirizzamento.
Inoltre, Russell mi ha aiutato sul Esperti di Scambio con l'utilizzo di una versione precedente di questo componente a lavorare in un'applicazione di console per inviare/ricevere messaggi tramite named pipe.Questo può aiutare una guida per ottenere installato e funzionante con usando le sue componenti.Si prega di notare che in un VCL app o un servizio, non è necessario scrivere il proprio ciclo di messaggi, come ho fatto in questa applicazione console.
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.
Altri suggerimenti
Opzione 1:Messaggio Personalizzato Coda
È possibile creare un messaggio personalizzato coda di messaggi push e alla coda, ordinare la coda sulla base di regole di business, e pop i messaggi dalla coda dal thread principale per l'elaborazione.Utilizzare una sezione critica per la sincronizzazione.
Opzione 2:Richiamate
Utilizzare i callback per inviare i dati avanti e indietro dal thread.Ancora una volta, utilizzare una sezione critica per la sincronizzazione.
OmniThreadLibrary contiene molto efficiente coda di messaggi in OtlComm.pas
unità.
La documentazione non è molto buono per il momento (inizia da qui ma si può sempre utilizzare il forum.
Sì – Gabr è possibile utilizzare i messaggi di windows in un servizio.
==============================
Prima di Windows Vista, si potrebbe avere configurato il servizio di interagire con il desktop.Che rende il servizio in esecuzione sullo stesso computer come un utente registrato, quindi, un programma in esecuzione come utente potrà inviare messaggi di servizio di windows.Windows Vista isolati servizi, anche se,non possono interagire con qualsiasi utente più il desktop.
=============================
Una Citazione da Rob Kennedy risposta ‘TService non elaborare i messaggi’
Ma non sarà in grado di utilizzare " frmMain.Maniglia " per inviare messaggi dal RDM al form principale in windows Vista.
Tutto quello che devo fare è trovare un modo diverso di invio e ricezione del messaggio
I Messaggi di Windows PUÒ ancora essere utilizzato in Windows Vista!Il problema è che una tecnologia in vista chiamato Interfaccia Utente Isolamento dei Privilegi a livello impedisce i processi con un basso livello di integrità (IL) dall'invio di messaggi a un processo con un alto IL (ad es.un servizio di windows ha un alto IL e in modalità utente in applicazioni di media IL).
Tuttavia, questo può essere ignorato e IL medio apps può essere consentito l'invio di wm alta IL processi.
UIPI non è un limite di sicurezza, e non ha lo scopo di proteggere contro tutti in frantumi attacchi.Accessibilità dell'interfaccia utente Le applicazioni possono ignorare UIPI da impostazione delle loro "uiAccess" valore VERO come parte della loro file manifesto.Questo richiede l'applicazione per essere in Programma di File o di directory di Windows, come essere firmato da un codice valido diritto di firma, ma questi requisiti non necessariamente interrompere il malware dal loro rispetto.
Inoltre, alcuni messaggi sono ancora ammessi, per esempio WM_KEYDOWN, che consente un minor IL processo per unità di input di un elevato prompt dei comandi.
Infine, la funzione ChangeWindowMessageFilter permette un medio IL processo (tutti non elevata processi, ad eccezione di Internet Explorer La Modalità Protetta) per modificare i messaggi che un alto IL processo può ricevere inferiore IL processo.Questo efficacemente consente di bypassare UIPI, a meno che l'esecuzione di Internet Explorer o di uno dei suoi processi figli.
Qualcuno oltre a Delfi, la PRASSI (il link è in lingua tedesca.Usare Google per Tradurre la pagina) ha già affrontato questo problema e pubblicato il loro codice utilizzando ChangeWindowMessageFilter.Credo che il loro problema è che WM_COPYDATA non funzionare su Vista fino a quando hanno modificato il loro codice di bypass UIPI per 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.
I creatori del madExcept biblioteca ecc fornire IPC funzionalità che può essere utilizzato invece di messaggi di Windows.
http://help.madshi.net/IPC.htm
Ho sviluppato uno screensaver per Windows in uno stadio, e ho voluto ottenere il mio screensaver inviare una notifica a un altro programma, e mentre lo screensaver è attivo, sono stato in grado di ottenere i messaggi di finestra di lavoro tra le due applicazioni.
L'ho sostituita con l'IPC funzionalità di cui sopra.
Lavorato trattamento.
Io uso questa libreria per IPc (utilizza la memoria condivisa + mutex):http://17slon.com/gp/gp/gpsync.htm
Ha TGpMessageQueueReader e TGpMessageQueueWriter.Utilizzare "Globale\" davanti al nome, in modo che si può utilizzare per comunicare tra un Servizio di Windows e di un "Servizio GUI Helper" quando un utente si connette.(il Global\ prefisso è necessario per la Vista a causa della sessione di sicurezza, anelli, ma anche per Windows XP/2003 tra le sessioni utente).
È molto veloce, il multithreading, etc.Vorrei usare questo invece di WM_COPYDATA (lenta e molto di testa se si utilizza un sacco, ma per le piccole cose, i messaggi possono essere OK)