10

私は、Windows メッセージを使用してそれ自体と通信するマルチスレッド アプリケーション (MIDAS) を持っています。

メインフォーム

メイン フォームは、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

名前付きパイプを使用します。それらの使い方がわからない場合は、今が学ぶ時です。

名前付きパイプを使用すると、任意のタイプのデータ構造を送信できます (サーバーとクライアントの両方がそのデータ構造が何であるかを認識している限り)。私は通常、レコードの配列を使用して、大量の情報のコレクションをやり取りします。とても便利な。

私は Russell Libby の無料 (およびオープンソース) の名前付きパイプ コンポーネントを使用しています。TPipeServer および TPipeClient ビジュアル コンポーネントが付属しています。名前付きパイプは非常に簡単に使用でき、名前付きパイプはプロセス間通信 (IPC) に最適です。

コンポーネントはこちらから入手できます。ソースからの説明は次のとおりです。 // 説明 : Delphi 用のクライアントとサーバーの名前付きパイプ コンポーネントのセット。// コンソール パイプ リダイレクト コンポーネント。

また、Russell は 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.
于 2008-12-11T20:46:32.277 に答える
2

OmniThreadLibraryには、ユニット内に非常に効率的なメッセージキューが含まれていますOtlComm.pas

現時点ではドキュメントはあまり良くありませんが(ここから始めてください)、いつでもフォーラムを使用できます。

于 2008-12-11T19:39:22.647 に答える
2
于 2008-12-11T20:37:01.500 に答える
2

オプション 1: カスタム メッセージ キュー

カスタム メッセージ キューを構築し、メッセージをキューにプッシュし、ビジネス ルールに基づいてキューをソートし、処理のためにメイン スレッドからキューからメッセージをポップできます。同期にはクリティカル セクションを使用します。

オプション 2: コールバック

コールバックを使用して、スレッドとの間でデータを送受信します。ここでも、同期にクリティカル セクションを使用します。

于 2008-12-11T18:05:43.817 に答える
0

WindowsメッセージはWindowsVistaでも引き続き使用できます。当面の問題は、ユーザーインターフェイス特権の分離(UIPI)と呼ばれるビスタのテクノロジが、整合性レベル(IL)の低いプロセスがILの高いプロセスにメッセージを送信できないようにすることです(たとえば、WindowsサービスのILとユーザーは高いです。モードアプリのILは中程度です)。

ただし、これはバイパスでき、中程度のILアプリはwmを高ILプロセスに送信できます。

ウィキペディアはそれを最もよく言います:

UIPIはセキュリティ境界ではなく、すべてのシャッター攻撃から保護することを目的とはしていません。UIアクセシビリティアプリケーションは、マニフェストファイルの一部として「uiAccess」値をTRUEに設定することにより、UIPIをバイパスできます。これには、アプリケーションがプログラムファイルまたはWindowsディレクトリにあり、有効なコード署名機関によって署名されている必要がありますが、これらの要件は必ずしもマルウェアがそれらを尊重することを妨げるものではありません。

さらに、WM_KEYDOWNなどの一部のメッセージは引き続き許可されます。これにより、下位ILプロセスが上位コマンドプロンプトへの入力を駆動できるようになります。

最後に、関数ChangeWindowMessageFilterを使用すると、中程度のILプロセス(Internet Explorer保護モードを除くすべての非昇格プロセス)が、高ILプロセスが低ILプロセスから受信できるメッセージを変更できます。 これにより、Internet Explorerまたはその子プロセスの1つから実行しない限り、UIPIを効果的にバイパスできます。

Delphi-PRAXIS(リンクはドイツ語です。Googleを使用してページを翻訳してください)の誰かがすでにこの問題に取り組み、ChangeWindowMessageFilterを使用してコードを投稿しています。彼らの問題は、WM_COPYDATAのUIPIをバイパスするようにコードを変更するまで、WM_COPYDATAが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 スクリーンセーバーを開発しましたが、スクリーンセーバーに通知を別のプログラムに送信させたいと思っていましたが、スクリーンセーバーがアクティブである間、2 つのアプリ間でウィンドウ メッセージを機能させることができませんでした。

上記の IPC 機能に置き換えました。

御馳走を働いた。

于 2009-06-21T22:47:23.250 に答える
0

私はこのライブラリを IPc に使用します (共有メモリ + ミューテックスを使用): http://17slon.com/gp/gp/gpsync.htm

TGpMessageQueueReader と TGpMessageQueueWriter があります。名前の前に「Global\」を使用すると、ユーザーがログインしたときに Windows サービスと「サービス GUI ヘルパー」との間で通信するために使用できます。ユーザー セッション間の Windows XP/2003 にも適用されます)。

非常に高速で、マルチスレッドなどです。私は WM_COPYDATA の代わりにこれを使用します (頻繁に使用すると遅く、オーバーヘッドが大きくなりますが、小さなことであればメッセージは問題ありません)。

于 2009-06-22T07:01:06.563 に答える