LinuxへのCOMの移植

COMテクノロジーが好きです。 しかし、私たちはCOMの技術、賞賛、欠点についてではなく、Linuxでの移植と実装の経験について話します。 自転車? 便宜? これに集中しないようにしましょう。



WikiおよびMSDNリンク
コンポーネントオブジェクトモデル

MSDNコンポーネントオブジェクトモデル(COM)



COMオブジェクト(1)



一般的に、少なくとも1つのCOMインターフェイスを実装するクラスのオブジェクト。 オブジェクトの実装は、主にCOMサーバーと呼ばれる動的に接続されたライブラリに隠されており(2) 、インターフェースは公開され、使用のために配布されます。







COMインターフェイス。純粋に仮想的な関数のみを含む抽象クラス。 特別なIUnknownインターフェイスが強調表示され、COMオブジェクトはこのインターフェイスを実装する必要があります。







各COMインターフェイスには、独自の識別子が含まれている必要があります。 COMでは、GUIDの構造によって決定され、ここでCOMの最初の欠点に直面します。 GUIDは理解不能であり、読み取り可能ではありません。 同じ必要がありますが、より読みやすく理解しやすい方法(uiidと呼びましょう)です。







IUnknownおよびuiid



#define define_uiid(name) \ inline static const std::string& guid() { const static std::string idn(dom_guid_pre_name #name); return idn; } namespace Dom { using uiid = std::string; using clsuid= std::string; struct IUnknown { virtual long AddRef() = 0; virtual long Release() = 0; virtual bool QueryInterface(const uiid&, void **ppv) = 0; define_uiid(Unknown) }; }
      
      





インターフェイス識別子に加えて、オブジェクトの作成に必要なクラス識別子(clsuid)も割り当てられます。 私たちの場合、 これは本質を判断できる読みにくい識別子であり、今のところそれらの公開を忘れることができます(これは良くないかもしれません)。







まとめ

COMオブジェクト。単一のクラス識別子が含まれます。 少なくとも1つのCOMインターフェイスを実装します-IUnknown(すべてのCOMインターフェイスには一意のインターフェイス識別子があります)。 COMオブジェクトの異なる実装は、同じクラス識別子を持つことができます(例:リリースとデバッグバージョン)。









COMサーバー(2)



少なくとも1つのCOMオブジェクトを実装するダイナミックリンクライブラリ(Linuxの場合、共有オブジェクトです)。 サーバーは、特定の機能セットをエクスポートする必要があります。







 extern "C" bool DllCreateInstance(const uiid& iid, void** ppv)
      
      



オブジェクトが正常に作成されるたびに、clsuidによってクラスオブジェクトを作成し、そのオブジェクトへの参照の数を増やします。 IUnknown :: AddRefの呼び出しもそのための参照カウントを増やし、IUnknown :: Releaseは減らす必要があります。



 extern "C" bool DllCanUnloadNow()
      
      





SOへの参照の数が0の場合、ライブラリをアンロードできます。



 extern "C" bool DllRegisterServer(IUnknown* unknown)
      
      





すべてのclsuidサーバーを「レジストリ」に登録します。 COMサーバーのインストール中に1回だけ呼び出されます。



 extern "C" bool DllUnRegisterServer(IUnknown* unknown)
      
      





登録済みのclsuidサーバーに関する「レジストリ」エントリから削除します。 COMサーバーをアンインストールするときに1回呼び出されます。



SimpleHelloの例では、IHelloインターフェイスを宣言します。



 struct IHello : public virtual Dom::IUnknown { virtual void Print() = 0; define_uiid(Hello) };
      
      





インターフェースの実装:



 /* COM- */ class SimpleHello : public Dom::Implement<SimpleHello, IHello> { public: SimpleHello() { printf("%s\n", __PRETTY_FUNCTION__); } ~SimpleHello() { printf("%s\n", __PRETTY_FUNCTION__); } virtual void Print() { printf("Hello from %s\n",__PRETTY_FUNCTION__); } define_clsuid(SimpleHello) }; /* COM- */ namespace Dom { DOM_SERVER_EXPORT_BEGIN EXPORT_CLASS(SimpleHello) DOM_SERVER_EXPORT_END DOM_SERVER_INSTALL(IUnknown* unknown) { Interface<IRegistryServer> registry; if (unknown->QueryInterface(IRegistryServer::guid(), registry)) { //      } return true; } DOM_SERVER_UNINSTALL(IUnknown* unknown) { Interface<IRegistryServer> registry; if (unknown->QueryInterface(IRegistryServer::guid(), registry)) { //      } return true; } }
      
      





マクロのセットは、より構造化された宣言とロジックを提供することにより、関数の実装を隠します。







Dom ::実装<SimpleHello、IHello>-IUnknownインターフェイスメソッドの実装を非表示にし、オブジェクト(C ++ 11および可変長テンプレート)によって実装されたインターフェイスを宣言するときに「シュガー」を追加します。









 template <typename T, typename ... IFACES> struct Implement : virtual public IUnknown, virtual public IFACES… { ... };
      
      





IRegistryServerインターフェイス-COMサーバーの「レジストリ」を操作するための一連のメソッドを定義します。







COMサーバーの「登録」 (3)



レジストリの重要性は過小評価されていますが、おそらくCOMの主要な柱です。 Microsoftはレジストリに書き込み、インターフェイスとその属性(idl)を記述するための複雑な構造を作成しますが、私は少し違った方法を取りました。







実装では、レジストリはファイルシステムに基づいています。

どんなパン? 理解しやすさ、シンプルさ、回復機能、サーバー登録時の特別なパン、ある種の名前空間(サーバーオブジェクトが登録されるベースレジストリに関連するディレクトリ)を設定できます。これにより、このテクノロジーを使用してアプリケーションの整合性とバージョン管理を実装できます。







欠点のうち、考えられるセキュリティ問題、オブジェクト実装の代替。







使用方法、サンプルアプリケーション(4)



すべてを機能させるには、小さな「ライブラリ」と小さな「プログラム」が必要です。







「ライブラリ」は、レジストリを操作し、SOをロード/アンロードし、オブジェクトを作成し、すべてを1つの全体に実装および収集するラッパーにすぎません。

これは、アプリケーションをビルドするときに指定する必要がある唯一のものです。 他のすべて、「信じたい」、彼女は自分でやります。







Programka -regsrvは、実際には同じアクションを実行するMicrosoft RegSrv32プログラムの類似物です(+名前空間を指定する機能、登録済みのclsuidおよびCOMサーバーのリストを取得する機能)。









サンプル



 #include "../include/dom.h" #include "../../skel/ihello.h" int main() { Dom::Interface<Dom::IUnknown> unkwn; Dom::Interface<IHello> hello; if (Dom::CreateInstance(Dom::clsid("SimpleHello"), unkwn)) { unkwn->QueryInterface(IHello::guid(), hello); hello->Print(); } else { printf("[WARNING] Class `SimpleHello` not register.\nFirst execute command\n\tregsrv <fullpath>/libskel.so\n... and try again."); } return 0; }
      
      





ドム(5)



Dom(動的オブジェクトモデル)、Linux用の私の実装。



git clone







ありがとう



All Articles