2012-03-12 17 views
4

私はクロスプラットフォームを目指すアプリケーションを開発中です。以前はWindowsメッセージを使用していましたが、今は取り除いています。私はメッセージをコールバックに置き換えましたが、私はさまざまな技術を使用することができますが、私はWindowsのメッセージを使用していないときには異なる可能性を認識していません。dllからメインアプリへのイベント通知

私はメインのexeアプリケーションといくつかのdllプラグインを持っています。私はいくつかのオブジェクトとスレッドをdllに持っており、DLLがデータ構造に対して行ったいくつかの変更についてメインアプリケーションに通知したいと思います。

私は現在、いくつかのコールバックを扱っています。異なる言語(C++、VB、C#)との互換性を提供するために、私は非オブジェクト型のコールバックを持っています。他の言語がオブジェクトのコールバックをサポートしているかどうかはわかりません。

だから私の質問は以下のとおりです。

  • Windowsメッセージの代替(クロスプラットフォーム)は何ですか?コールバックでメッセージを置き換えることはできますか?
  • オブジェクトのコールバックを他の言語でサポートしていますか?
  • 他の言語はメッセージの代わりに異なる技術を持っていると思いますか?

答えて

2

私の質問は次のとおりです。 Windowsメッセージの代替(クロスプラットフォーム)は何ですか?コールバックでメッセージを置き換えることはできますか?

はい、メッセージをコールバックで置き換えることができます。

オブジェクトのコールバックをサポートしていますか?

コールバックとしてオブジェクトメソッドを使用しないでください。ポータブルコードでは一般的には、ハンドルの使用(呼び出し規約を通知)である:

DLLソース:

type 
    THandle = LongWord; 
    {$IF SizeOf(THandle) < SizeOf(Pointer))} 
    {$MESSAGE Error 'Invallid handle type'} 
    {$ENDIF} 

    TCallback = procedure(const aHandle: THandle); cdecl; 

    var 
     gCallback: record 
     Routine: TCallback; 
     Obj: TObject; 
     Info: string 
    end; 

    function Object2Handle(const aObj: TObject): THandle; 
    begin 
    Result:= THandle(Pointer(aObj)) 
    end; 

    function Handle2Object(const aHandle: THandle; out aObj: TObject): Boolean; 
    begin 
    if gCallback.Obj <> nil then 
     if aHandle = Object2Handle(gCallback.Obj) then 
     begin 
     aObj:= gCallback.Obj; 
     Result:= true; 
     Exit // WARRNING: program flow disorder 
     end; 

    aObj:= nil; 
    Result:= false 
    end; 

procedure DoCallback(); 
begin 
    if Assigned(gCallback.Routine) then 
    gCallback.Routine(Object2Handle(gCallback.Obj)) 
end; 

procedure SetupCallback(const aCallback: TCallback); cdecl; 
begin 
    gCallback.Routine:= aCallback; 
end; 

procedure DoSomething(const aHandle: THandle; out aInfo: string); cdecl; 
var 
    O: TObject; 
begin 
    if Handle2Object(aHandle, O) then 
    aInfo:= Format('%s class object %s', [O.ClassName(), gCallback.Info]) 
end; 

procedure Test(); 
begin 
    gCallback.Obj:= TStream.Create(); 
    try 
    gCallback.Info:= 'created'; 
    DoCallback(); 
    finally 
    FreeAndNil(gCallback.Obj) 
    end; 
    gCallback.Obj:= TMemoryStream.Create(); 
    try 
    gCallback.Info:= 'will be freed'; 
    DoCallback(); 
    finally 
    FreeAndNil(gCallback.Obj) 
    end 
end; 

exports 
    SetupCallback, 
    DoSomething, 
    Test; 

実行可能なソース:

procedure Cb(const aHandle: THandle); cdecl; 
const 
    STUPID: THandle = 1; 
    EQUALLY_STUPID = $DEAD; 
var 
    S: string; 
begin 
    DoSomething(STUPID, S); 
    DoSomething(aHandle, S); 
    DoSomething(EQUALLY_STUPID, S) 
end; 

begin 
    SetupCallback(@Cb); 
    Test() 
end. 

編集:あなたはあなたの足で自分自身を撮影することはできません今。

他の言語はメッセージの代わりに異なる技術を持っていると思いますか?

OSにはいくつかのメッセージがあります。しかし、多くの本当に移植可能です。

また、使用することができます。この場合は

  • (IMO大きすぎる

    • ソケットを、?)準備ができているメッセージングシステム(私のお気に入り0MQ
  • +0

    DLLがEXEがhandleパラメータに格納するオブジェクトの型を知っていて、ハンドルパラメータがそのタイプである場合、ハンドルのポイントを見落としてしまいました。ハンドルを提供するモジュールは、ハンドルプロバイダーにそれを戻す以外は何らかの方法でそれを使用するハンドルのコンシューマーの恐れなしに、*任意の*データ型をその中に格納することが許されるべきです。あなたの例では、EXEは有効なTObject参照を格納しなければなりません。そうでなければ、DoSomethingがクラッシュします。このTHandleの代わりにTObjectを直接渡すこともできます。 –

    +0

    そして、TObjectリファレンス "異なる言語との互換性を提供する"と移植性を渡しますか?私は、可能な限りシンプルなハンドル実装を示しました。 – g2mk

    +0

    私の問題は非常に簡単です。オブジェクトを渡す必要はありません。メインアプリケーションに変更を通知する必要があるため、GUIをリフレッシュできます。私はクロスプラットフォームであり、他の言語でも使用できるソリューションを探しています。私はコールバックでそれをやると思います。私はちょうどいくつかの承認と私の問題のさまざまな側面が必要でした。ありがとう。 – Nix

    3

    私は間違いなくコールバックを使用します。メインアプリケーションは必要に応じてコールするためにDLLにコールバック関数を与えることができ、必要に応じてコールバック関数自体がウィンドウメッセージをアプリに送ることができます。

    4

    メッセージの代わりにコールバック関数を使うことができます。コールバックのメソッドを使用することはできません。DelphiとC++ BuilderのみがDelphiのメソッドポインタを呼び出す方法を理解しているからです。ただし、コールバックオブジェクトをCOMをサポートする言語で使用することはできます。アプリケーションにデータ構造が変更されたことを通知するプラグインの例を次に示します。

    1. インターフェイスを定義します。

      type 
          IDataStructureChanged = interface 
          ['{GUID}'] 
          procedure Call; stdcall; 
          end; 
      

      プラグインは、データ構造がどのように変化するかをを伝える、またはプラグインが通知を行っているかを示すいくつかの値を渡すことができるようにあなたが方法にいくつかのパラメータを追加することができます。

    2. アプリケーションで実装します。あなたはそのクラスをインスタンス化するとき、あなたはそれをあなたのプログラムのメインフォーム、または任意のプラグインでは、最終的にCallメソッドを呼び出したときにアクションを取ることができるようにあなたのプログラムが必要になり、他の情報への参照を渡すことができ

      type 
          TDataStructureChangedListener = class(TInterfacedObject, IDataStructureChanged) 
          private 
          FForm: TForm; 
          procedure Call; stdcall; 
          public 
          constructor Create(Form: TForm); 
          end; 
      

      Callを実装して、データ構造が変更されたときに必要な処理をアプリケーションに実行させます。

    3. 初期化するときに各プラグインへの参照を渡します。

      ​​

      プラグインは、リスナーオブジェクトへの参照を格納する必要があり、および場合のデータ構造の変更、それはあなたのアプリケーションに通知するCallメソッドを呼び出すことができます。私はここで説明した内容

    は、一般的にイベントシンクとして知られているものです。あなたはあなたのプログラムに複数を持つことができます。処理するイベントが複数ある場合は、イベントの種類ごとに別々のインターフェースを持つこともできますし、すべてを単一のインターフェースにグループ化して、イベントごとに異なるメソッドを持つこともできます。各プラグインごとに異なるシンクオブジェクトを持たせることもできますし、各プラグインに同じシンクオブジェクトへの参照を渡して、プラグインIDパラメータを渡すこともできます。

    3

    私はRemy、(!)に同意します。単純なコールバックを使用すると、ハンドラは任意の種類のそれ以上の通信を実装できます。メッセージを送信したり、パラメータをキューにプッシュしたりすることができます。クロスプラットフォームになりたいなら、シンプルな型を渡すことに頼らざるを得なくなります。コールバックが設定されているときには、通常、 'ユーザコンテキスト'ポインタを渡します。コールバックは、このポインタをハンドラに渡します。これにより、呼び出し元はコンテキストオブジェクトをポインタ/ intとして渡し、ハンドラ内でポインタ/ intをオブジェクトにキャストして戻すことができます。ハンドラは、それがDelphi、C++などであっても、コンテキスト上のメソッドを呼び出すことができます。

    関連する問題