CoRegisterClassObject を使用して、com オブジェクトを含む dll をロードする方法をカスタマイズしようとしています。スレッドのアパートメント タイプが com オブジェクトのものと一致しないときに発生していた問題を解決する方法を試しています。基本的な考え方は、coregisterclassobject を使用すると com オブジェクトの作成時にレジストリが無視されるため、STA オブジェクトが STA スレッドで作成されていることを確認する必要があり、MTA オブジェクトについても同様であるということです。これは、常に期待どおりに動作するとは限らない概念実証として作成したサンプルです。
LPSTREAM factory_stream = NULL; //GLOBAL VARIABLE FOR TEST
DWORD __stdcall FactoryThread(LPVOID param)
{
CoInitialize(NULL);
//CoInitializeEx(NULL, COINIT_MULTITHREADED);
cout << GetCurrentThreadId(); //THREAD_ID_2
CustomClassFactory *factory = new CustomClassFactory();
factory->AddRef();
CoMarshalInterThreadInterfaceInStream(IID_IClassFactory, (IClassFactory*)factory, &factory_stream);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
factory->Release();
CoUninitialize();
return 0;
}
そして、これが私の主な機能の関連部分です。
//CoInitialize(NULL);
CoInitializeEx(NULL, COINIT_MULTITHREADED);
cout << GetCurrentThreadId(); //THREAD_ID_1
HANDLE regThread = CreateThread(NULL, 0, FactoryThread, NULL, 0, NULL);
Sleep(5000); //ensures that the factory is registered
IClassFactory *factory = NULL;
CoGetInterfaceAndReleaseStream(factory_stream, IID_IClassFactory, (void**)&factory);
DWORD regNum = 0;
HRESULT res = CoRegisterClassObject(clsid, factory, CLSCTX_INPROC_SERVER, REGCLS_MULTI_SEPARATE, ®Num);
{
TestComObjLib::ITestComObjPtr ptr;
HRESULT hr = ptr.CreateInstance(__uuidof(TestComObjLib::TestComObjCoClass), NULL);
ptr->OutputOwningThreadId(); //THREAD_ID_3 is just from cout << GetCurrentThreadId()
TestComObjLib::ITestComObjPtr ptr2;
HRESULT hr = ptr2.CreateInstance(__uuidof(TestComObjLib::TestComObjCoClass), NULL);
ptr2->OutputOwningThreadId(); //THREAD_ID_4
}
CoRevokeClassObject(regNum);
CoUninitialize();
CoRegisterClassObject でレジストリを使用するべきではないため、現在の MTA スレッドではなく STA でアパートメント スレッド オブジェクトを手動で作成する必要があり、その逆も同様であるという考えでした。CoRegisterClassObject を使用していない場合、CoGetClassObject は新しいスレッドを生成し、そのスレッドで DllGetClassObject を呼び出すことに気付きました。そのため、クラス ファクトリを STA で作成するだけで、オブジェクトがそこに存在することがわかりました。
私が見ている問題は、上記の例では、スレッド ID が常に期待どおりに表示されるとは限らないことです。FactoryThread がアパートメント スレッドとして初期化され、メイン スレッドがマルチスレッドとして初期化されている場合、期待どおりに THREAD_ID_2 == THREAD_ID_3 == THREAD_ID_4 != THREAD_ID_1 になります (ファクトリはこれらのオブジェクトを作成しており、ファクトリのスレッドに存在できます)。ただし、これらのスレッド モデルが切り替えられると、thread_id_3 == thread_id_4 になりますが、com オブジェクトはスレッド 2 で作成できますが、thread_id_2 および thread_id_1 とは異なります。
これは一貫性がないように思われ、別のスレッドが関与している状況では望ましくない動作を引き起こす可能性があります。レジストリのみに依存し、coregisterclassobject を使用しない場合、STA でフリースレッド オブジェクトを作成すると、そのオブジェクトは、MTA にある com によって生成された別のスレッドで作成されます。も STA にあり、そこでオブジェクトを作成すると、新しいスレッドではなく、com によって生成された最初の MTA スレッドに配置されます (オブジェクトの threadingmodel とスレッドのアパートメント タイプが逆の場合も同様です)。ただし、上記のように coregisterclassobject を使用して独自のファクトリを作成し、オブジェクトがマルチスレッド化されていてスレッドが STA にある場合、これらのマルチスレッド化オブジェクトを作成した新しいスレッドごとに新しい MTA スレッドが生成されます。