2016-03-23 11 views
3

DelphiからC++コードを呼び出す必要があります。 C++コードは、代わりにDelphiコードにコールバックできる必要があります。ここに示した例はCalling a callback function in Delphi from a C++ DLLです。しかし、C++にコールバックとして単一のDelphi関数を渡すのではなく、インタフェースを実装するDelphiオブジェクトを渡したいと思います。DelphiおよびC++クラスのVMTは互換性がありますか?

編集:私はC++の用語を参照しています。これは純粋な仮想関数を持つクラスです。これは必ずしもDelphi interfaceキーワードで定義されたタイプではありません。換言すれば、以下のクラスは、私はC++から呼び出したいインターフェイスを定義:

ICallable = class 
    procedure callMe stdcall; virtual; abstract; 
    procedure CallMeAgain stdcall; virtual; abstract; 
end; 

次のようにICallableインタフェースが順番にDelphiで実装される:C++側

MyCallable = class(ICallable) 
    procedure callMe override; 
    procedure callMeAgain override; 
end; 

procedure MyCallable.callMe 
begin 
    WriteLine('I was called'); 
end; 

procedure MyCallable.callMeAgain 
begin 
    WriteLine('I was called again'); 
end; 

をこれは、私は次のようにICallableインタフェースを定義したい、DLLとしてコンパイルされています

class ICallable{ 
public: 
    virtual void callMe()=0; 
    virtual void callMeAgain()=0; 
} 

そして、それは、Delphiで呼び出すことができるように、次のDLL関数をエクスポート:

#define DllExport extern "C" __declspec(dllexport) 

DLLExport bool Callback(ICallable* callable){ 
    callable->callMe(); 
    callable->callMeAgain(); 
    return true; 
} 

そして最終的に戻って、Delphiで:

function Callback(myCallable: ICallable) : Boolean cdecl; external 'dllname' 

質問:

  • C++とDelphiは、同じように自分の仮想メソッドテーブルを実装する場合にのみを働かせることができます。これは本当ですか?
+2

デルファイのインターフェイスはCOM規則に従います。ですから、C++のCOM規約に従う必要があります。http://rvelthuis.de/articles/articles-cppobjs.htmlとhttp://www.scritub.com/stiinta/tutorials/visual-c-en/Sharing-コードとオブジェクト-Betwe18279520.php – Johan

+0

DelとDelのインタフェースは 'interface'キーワードを使って宣言します。非COMインタフェースを宣言することを主張するなら、あなたは '抽象クラス'を使います。インターフェースの実装は 'TInterfacedObject'から派生しています。 – Johan

+0

@Johan私はここでCOMマシン全体を明示的に回避しようとしています。また、私の場合は、C++ COMコンポーネントを設計していません。私はC++と同じ "インタフェース"を "共有"するDelphiオブジェクトへのコールバックができるようにしたいと考えています。本質的には、仮想抽象メソッドを持つDelphiクラスが、純粋な仮想関数を持つ対応するC++クラスにマップされているかどうかを知りたい場合 – BigONotation

答えて

11

これはC++のみで動作し、Delphiは同じ方法で仮想メソッドテーブルを実装します。それは事実ですか?

元々、Delphiクラスには、C++クラスと互換性のあるVMTはありません。私は、すべてのDelphiクラスが仮想メソッドを宣言するTObjectから派生しているからだと思いました。これらの仮想メソッドはVMTに表示されますが、私はこれらのメソッドがVMTの最初に現れると考えました。しかし、コンパイラは、組み込みの仮想メソッドTObjectがVMTに負のインデックスを持つように手配します。これは、ユーザ定義の仮想メソッド(サブクラスTObjectで定義されているもの)がインデックス0から始まることを意味します。

これは、問題のコード内のDelphiおよびC++クラスが実際に互換性のあるVMTを持つことを意味します。 Delphiの以前のバージョンのCOMをサポートするために、この設計が選択されたと私は信じています。私の主張を裏付けるために、documentationが私の強調したところで言及します:

VMTのレイアウトを次の表に示します。 32ビットプラットフォームでは、正のオフセットで、VMTは32ビットのメソッドポインタのリスト(64ビットプラットフォーム上の64ビットメソッドポインタ)で構成されます。これは、クラスタイプのユーザー定義仮想メソッドごとに1つです。宣言の順序で。各スロットには、仮想メソッドの対応するエントリポイントのアドレスが格納されます。このレイアウトはで、C++ v-tableおよびCOMと互換性があります。負のオフセットでは、VMTにはDelphiの実装の内部にあるいくつかのフィールドが含まれています。レイアウトは将来のDelphi言語の実装で変更される可能性があるため、アプリケーションはTObjectで定義されたメソッドを使用してこの情報を照会する必要があります。

C++標準では、VMTを実装する方法が少なくても、仮想メソッドにVMTを使用することは義務付けられていないことを強調する必要があります。実際には、COMをサポートするために、主流のWindowsコンパイラにはこのように実装されたVMTがあります。

このような実装の詳細に頼るのではなく、Delphiインターフェイスを使用できます。しかし、あなたが知っているように、これらはCOMインターフェイスなので、IUnknownを実装する必要があります。あなたはCOMの機械を避けたいと言いますが、追加する必要があるのはIUnknownです。それは私の見解では特に厄介ではありません。 CLSIDを登録したり、クラスファクトリを実装したりする必要があると思います。あなたはそうしない。 IUnknownを実装するだけです。とにかく

、あなたが本当にIUnknownを避けるに設定されている場合、あなたはDelphiのインターフェースを使用することはできませんし、これまでのところ、私が言うことができるように2つのオプションがあります。

  1. は、あなたのDelphiコードで手動でVMTを実装します。 VMTは単なる関数ポインタの配列です。これは、COMがCで行う方法を見せるコードにつながります。完璧に可能ですが、正確には心地よくありません。
  2. 質問に記載されているアプローチを使用して、TObjectが組み込みの仮想メソッドに対して負のVMTインデックスを使用する実装の詳細に頼ります。

    デルファイ

    {$APPTYPE CONSOLE} 
    
    type 
        ICallable = class 
        public 
        procedure CallMe cdecl; virtual; abstract; 
        procedure CallMeAgain cdecl; virtual; abstract; 
        end; 
    
        MyCallable = class(ICallable) 
        public 
        procedure CallMe; override; 
        procedure CallMeAgain; override; 
        end; 
    
    procedure MyCallable.CallMe; 
    begin 
        Writeln('CallMe'); 
    end; 
    
    procedure MyCallable.CallMeAgain; 
    begin 
        Writeln('CallMeAgain'); 
    end; 
    
    const 
        dllname = 'C:\Users\heff\Desktop\Win32Project1\Debug\Win32Project1.dll'; 
    
    function Callback(Callable: ICallable): Boolean; cdecl; external dllname; 
    
    var 
        Callable: ICallable; 
    
    begin 
        Callable := MyCallable.Create; 
        Writeln(Callback(Callable)); 
        Callable.Free; 
        Readln; 
    end. 
    

    C++

    #include <Windows.h> 
    
    BOOL APIENTRY DllMain(HMODULE hModule, 
             DWORD ul_reason_for_call, 
             LPVOID lpReserved 
            ) 
    { 
        switch (ul_reason_for_call) 
        { 
        case DLL_PROCESS_ATTACH: 
        case DLL_THREAD_ATTACH: 
        case DLL_THREAD_DETACH: 
        case DLL_PROCESS_DETACH: 
         break; 
        } 
        return TRUE; 
    } 
    
    class ICallable 
    { 
    public: 
        virtual void CallMe() = 0; 
        virtual void CallMeAgain() = 0; 
    }; 
    
    extern "C" __declspec(dllexport) bool Callback(ICallable* callable) 
    { 
        callable->CallMe(); 
        callable->CallMeAgain(); 
        return true; 
    } 
    

    出力

    0:コードオプション2のための

コンパイル可能で、このようになります。

 
CallMe 
CallMeAgain 
TRUE 
+1

これは私の一日を作った!あなたの答えを受け入れる。 Delphi/C++ VMTに関するすべての詳細を説明してくれてありがとう – BigONotation

関連する問題