10

我有一个多线程应用程序 (MIDAS),它使用 Windows 消息与自身通信。

主要形式

主窗体接收 RDM LogData('DataToLog') 发送的 windows 消息

因为使用了 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消息缓冲区已满,速度变慢
4

7 回答 7

10
于 2008-12-11T20:46:32.277 回答
2

选项 1:自定义消息队列

您可以构建自定义消息队列,将消息推送到队列中,根据业务规则对队列进行排序,并从主线程从队列中弹出消息进行处理。使用临界区进行同步。

选项 2:回调

使用回调从线程来回发送数据。同样,使用临界区进行同步。

于 2008-12-11T18:05:43.817 回答
2

OmniThreadLibrary在单元中包含非常高效的消息队列OtlComm.pas

目前文档不是很好(从这里开始),但您可以随时使用论坛

于 2008-12-11T19:39:22.647 回答
2
于 2008-12-11T20:37:01.500 回答
0

Windows 消息仍然可以在 Windows Vista 中使用!手头的问题是 vista 中称为用户界面特权隔离 (UIPI) 的技术可防止具有较低完整性级别 (IL) 的进程将消息发送到具有高 IL 的进程(例如,Windows 服务具有高 IL 和用户模式应用程序具有中等 IL)。

但是,这可以被绕过,并且可以允许中等 IL 应用程序将 wm 发送到高 IL 进程。

维基百科说得最好:

UIPI 不是安全边界,并不旨在防止所有粉碎攻击。UI 可访问性应用程序可以通过将其“uiAccess”值设置为 TRUE 作为其清单文件的一部分来绕过 UIPI。这要求应用程序位于 Program Files 或 Windows 目录中,并由有效的代码签名机构签名,但这些要求不一定会阻止恶意软件尊重它们。

此外,仍然允许通过某些消息,例如 WM_KEYDOWN,它允许较低的 IL 进程将输入驱动到提升的命令提示符。

最后,函数 ChangeWindowMessageFilter 允许中等 IL 进程(除 Internet Explorer 保护模式外的所有非提升进程)更改高 IL 进程可以从较低 IL 进程接收的消息。这有效地允许绕过 UIPI, 除非从 Internet Explorer 或其子进程之一运行。

Delphi-PRAXIS 的某个人(链接是德语。使用 Google 翻译页面)已经解决了这个问题,并使用 ChangeWindowMessageFilter 发布了他们的代码。我相信他们的问题是 WM_COPYDATA 在他们修改代码以绕过 WM_COPYDATA 的 UIPI 之前无法在 Vista 上运行。

原始链接(德语)

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.
于 2008-12-12T12:27:36.190 回答
0

madExcept 库等的创建者提供了可用于代替 Windows 消息的 IPC 功能。

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

我在一个阶段开发了一个 Windows 屏幕保护程序,我想让我的屏幕保护程序向另一个程序发送一些通知,当屏幕保护程序处于活动状态时,我无法让窗口消息在两个应用程序之间工作。

我用上面提到的 IPC 功能替换了它。

工作了一个款待。

于 2009-06-21T22:47:23.250 回答
0

我将此库用于 IPc(使用共享内存 + 互斥锁):http: //17slon.com/gp/gp/gpsync.htm

它有 TGpMessageQueueReader 和 TGpMessageQueueWriter。在名称前使用“Global\”,以便在用户登录时使用它在 Windows 服务和“Service GUI Helper”之间进行通信。(由于会话安全环,Vista 需要 Global\ 前缀,也适用于用户会话之间的 Windows XP/2003)。

它非常快,多线程等。我会使用它而不是 WM_COPYDATA (如果你经常使用它会很慢而且开销很大,但对于小事消息可以)

于 2009-06-22T07:01:06.563 回答