Я пытаюсь использовать CoRegisterClassObject, чтобы настроить способ загрузки DLL, в которых есть объекты com. Я пробую что-то, что решит проблему, с которой я столкнулся, когда тип квартиры потока не соответствовал объекту 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, мне нужно было вручную создать многопоточные объекты квартиры в STA вместо текущего потока MTA, и наоборот. Я заметил, что, когда CoRegisterClassObject не используется, CoGetClassObject порождает новый поток и вызывает DllGetClassObject в этом потоке, поэтому я решил, что фабрику классов просто нужно создать в STA, и тогда объекты будут жить там.
Проблема, которую я вижу, заключается в том, что в приведенном выше примере идентификаторы потоков не всегда выглядят так, как я ожидал. Если FactoryThread инициализирован как Многопоточный, а основной поток как многопоточный, то THREAD_ID_2 == THREAD_ID_3 == THREAD_ID_4! = THREAD_ID_1, как и ожидалось (фабрика создает эти объекты, и они могут жить в потоке фабрики). Если эти модели потоковой передачи переключаются, тогда thread_id_3 == thread_id_4, но они отличаются от thread_id_2 и thread_id_1, даже если объекты com могут быть созданы в потоке 2.
Это кажется непоследовательным и может вызвать нежелательное поведение в ситуациях, когда задействован другой поток. Если полагаться только на реестр и не использовать coregisterclassobject, если я создал объект со свободным потоком в STA, объект будет создан в другом потоке, порожденном com, который был в MTA, а затем, если я создал третий поток, который также был в STA, создание там объекта поместило бы его в первый поток MTA, порожденный com, а не новый (то же самое было бы, если бы модель потока объекта и типы апартаментов потоков были поменяны местами). Однако, если бы я использовал coregisterclassobject для создания моей собственной фабрики, как указано выше, и объект был многопоточным, но поток находился в STA, то каждый новый поток, создавший эти многопоточные объекты, порождал бы новый поток MTA, что кажется расточительным и непоследовательным. с тем, что обычно бывает.