9

まず、COMは私にとって黒魔術のようなものです。しかし、私が取り組んでいる1つのプロジェクトでCOMdllを使用する必要があります。

そのため、開発中のDLLがあり、別のCOMDLLで使用できるいくつかの機能が必要です。Depends.exeを使用してCOMDLLを見ると、DllGetClassObject()やその他の関数などのメソッドが表示されますが、関心のある関数はありません。

私はCOMDLL(レガシー)ソースコードにアクセスできますが、それは混乱しているので、内部で何が起こっているのかわからない大きなブラックボックスのようにバイナリでCOMDLLを使用したいと思います。

では、LoadLibraryを使用してコードからCOM DLL関数を呼び出すにはどうすればよいですか?出来ますか?はい、その方法の例を教えていただけますか?

このプロジェクトではVisualStudio6を使用しています。

どうもありがとう!

4

6 に答える 6

22

一般に、直接アクセスするよりもCoCreateInstanceorを優先する必要があります。CoGetClassObjectDllGetClassObjectしかし、登録できない、または登録したくない DLL を扱っている場合は、これらの関数が舞台裏で何をするか (その一部) を以下に説明します。


CLSID をDllGetClassObject指定すると、インスタンスを作成できるクラス オブジェクトを取得できます (IClassFactory私の記憶が正しければ、インターフェイスを介して)。

手順の概要 (最後に COM に触れてからしばらく経っているので、明らかなエラーはご容赦ください):

  1. Call DllGetClassObject(clsid, IID_IClassFactory, &cf)、ここでclsidはクラス オブジェクトを取得する CLSID でありcf、もちろんクラス ファクトリです。
  2. Call cf->CreateInstance(0, iid, &obj)、ここでiidは使用するインターフェイスの IID でありobj、もちろんオブジェクトです。
  3. ???
  4. 利益!

(CoCreateInstanceステップ 1 と 2を実行します。ステップ 1 を実行します。ステップ 1 を毎回繰り返す必要がないように、同じクラスのインスタンスを多数作成する必要がある場合にCoGetClassObject使用します。)CoGetClassObject

于 2010-02-02T20:30:31.317 に答える
14

通常CoCreateInstance()、COMDLLからオブジェクトをインスタンス化するために使用します。これを行う場合、通常のDLLで行う必要があるように、最初にDLLをロードしてprocアドレスを取得する必要はありません。これは、Windowsが、COM DLLが実装するタイプ、それらが実装されているDLL、およびそれらをインスタンス化する方法を「認識」しているためです。(もちろん、COM DLLが登録されていると仮定します(通常は登録されています)。

使用したいIDogインターフェースを備えたCOMDLLがあるとします。その場合、

dog.idl

interface IDog : IUnknown
{
  HRESULT Bark();
};

coclass Dog
{
  [default] Interface IDog;
};

myCode.cpp

IDog* piDog = 0;
CoCreateInstance(CLSID_DOG, 0,  CLSCTX_INPROC_SERVER, IID_IDOG,  &piDog); // windows will instantiate the IDog object and place the pointer to it in piDog
piDog->Bark();  // do stuff
piDog->Release();  // were done with it now
piDog = 0;  // no need to delete it -- COM objects generally delete themselves

ただし、これらすべてのメモリ管理機能はかなり汚くなる可能性があり、ATLは、これらのオブジェクトのインスタンス化と管理のタスクを少し簡単にするスマートポインタを提供します。

CComPtr<IDog> dog;
dog.CoCreateInstance(CLSID_DOG);
dog->Bark();

編集:

私が上で言ったとき:

Windowsは、COMDLLが実装するタイプ[...および]それらが実装されているDLLについて「認識」しています。

...私はWindowsがこれをどのように知っているかを正確に理解しました。最初は少しオカルトっぽく見えるかもしれませんが、それは魔法ではありません。

COMライブラリには、ライブラリが提供するインターフェイスとCoClassをリストするタイプライブラリが付属しています。このタイプライブラリは、ハードドライブ上のファイルの形式です。多くの場合、ライブラリ自体と同じDLLまたはEXEに直接埋め込まれています。Windowsは、Windowsレジストリを調べることにより、タイプライブラリとCOMライブラリ自体の場所を認識しています。レジストリのエントリは、ハードドライブ上のDLLが配置されている場所をWindowsに通知します。

を呼び出すとCoCreateInstance、WindowsはWindowsレジストリでclsidを検索し、対応するDLLを見つけてロードし、COMオブジェクトを実装するDLLで適切なコードを実行します。

この情報はどのようにしてWindowsレジストリに取り込まれますか?COM DLLをインストールすると、登録されます。これは通常、zippy32.exeを実行することによって実行されます。これにより、DLLがメモリに読み込まれ、。という名前の関数が呼び出されDllRegisterServerます。COMサーバーに実装されているこの関数は、必要な情報をレジストリに追加します。ATLまたは別のCOMフレームワークを使用している場合、これはおそらく内部で行われているため、レジストリと直接インターフェイスする必要はありません。 DllRegisterServerインストール時に一度だけ呼び出す必要があります。

/プロセスCoCreateInstanceを介してまだ登録されていないCOMオブジェクトを呼び出そうとすると、次のようなエラーで失敗します。regsvr32DllRegisterServerCoCreateInstance

クラスは登録されていません

幸い、これに対する修正はregsvr32、COMサーバーを呼び出すだけで、再試行することです。

于 2010-02-02T21:31:21.753 に答える
7

COMライブラリでLoadLibrary()を直接使用しないでください。CoCreateInstance()は、この関数がまだ呼び出されていない場合は呼び出し、ライブラリに実装したクラスのインスタンスをヒープに新しく呼び出し、最後にそのオブジェクトへの生のポインターを返します。もちろん、プロセス中に失敗する可能性があるため、HRESULTなどのステータスを確認するためのメカニズムがあります。

簡単に使用できるように、COMライブラリは1)事前定義されたentry(main)関数、2)CoCreateInstance()などの事前定義された関数を呼び出して入力し、次のようなものであることを受け入れる必要がある共通のDLLと考えることができます。それはしなければならないからです。

于 2010-02-03T02:45:02.673 に答える
3

タイプ ライブラリが DLL に埋め込まれている場合は、それをプロジェクトにインポートできます。

#import "whatever.dll"

これにより、プロジェクトに含まれるヘッダー ファイルが自動生成され、エクスポートされたオブジェクトを使用できるようになります。

于 2010-02-03T04:40:36.870 に答える
2

クラス ファクトリを取得し、それを使用して COM オブジェクトを作成する方法を示すコードを次に示します。構造体を使用して、モジュール ハンドルと DllGetClassObject 関数ポインターを追跡します。COM オブジェクトの処理が完了するまで、モジュール ハンドルを保持する必要があります。

この関数を使用するには、ComModuleInfo 構造体のインスタンスを割り当て、szDLL を DLL ファイル名またはフル パス名に設定する必要があります。次に、その DLL から取得する COM オブジェクトのクラス ID とインターフェイス ID を使用して関数を呼び出します。

typedef struct {
   TCHAR   szDLL[MAX_PATH];
   HMODULE hModule;
   HRESULT (WINAPI *pfnGetFactory)(REFCLSID, REFIID, void**);
   } ComModuleInfo;

HRESULT CreateCOMObject(
   ComModuleInfo & mod,  // [in,out] 
   REFCLSID iidClass,    // [in] CLSID of the COM object to create
   REFIID iidInterface,  // [in] GUID of the interface to get
   LPVOID FAR* ppIface)  // [in] on success, interface to the COM object is returned
{
    HRESULT hr = S_OK;

    *ppIface = NULL; // in case we fail, make sure we return a null interface.

    // init the ComModuleInfo if this is the first time we have seen it.
    //
    if ( ! mod.pfnGetFactory)
    {     
       if ( ! mod.hModule)
       {
          mod.hModule = LoadLibrary(mod.szDLL);
          if ( ! mod.hModule)
             return HRESULT_FROM_WIN32(GetLastError());
       }
       mod.pfnGetFactory = (HRESULT (WINAPI *)(REFCLSID, REFIID, void**))GetProcAddress(mod.hModule, "DllGetClassObject");
       if ( ! mod.pfnGetFactory)
          return HRESULT_FROM_WIN32(GetLastError());
    }

    IClassFactory* pFactory = NULL;
    hr = mod.pfnGetFactory(iidClass, IID_IClassFactory, (void**)&pFactory);
    if (SUCCEEDED(hr))
    {
       hr = pFactory->CreateInstance(NULL, iidInterface, (void**)ppIface);
       pFactory->Release();
    }

    return hr;
}
于 2010-02-02T21:21:06.927 に答える
0

COM DLL の場合は、プロジェクトへの参照として追加するだけで、DLL 内の関数を呼び出すことができます。

はい、DLLGetClassObject のような低レベルの COM 関数を使用できますが、なぜ使用するのでしょうか?

于 2010-02-02T20:33:51.077 に答える