2013-07-24 12 views
0

CoRegisterClassObject를 사용하여 com 객체가있는 dll을로드하는 방식을 사용자 정의하려고합니다. 스레드의 아파트 유형이 com 객체와 일치하지 않을 때 발생하는 문제를 해결할 수있는 방법을 찾고 있습니다. 기본 개념은 com 개체를 만들 때 coregisterclassoject가 레지스트리를 무시하기 때문에 STA 개체가 STA 스레드에서 만들어지고 MTA 개체에서 만들어 지는지 확인해야합니다. 다음은 내가 예상 한대로 항상 행동하지는 않는 개념 증명으로 쓴 샘플입니다.Com 정렬 된 공장과 일치하지 않는 스레딩/아파트 동작

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, &regNum); 
{ 
    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에서 클래스 팩토리를 생성해야만 객체가 생성된다는 사실을 알게되었습니다.

위의 예에서 볼 수있는 문제는 스레드 ID가 항상 예상대로 끝나지 않는다는 것입니다. FactoryThread가 아파트 스레드로 초기화되고 주 스레드가 다중 스레드로 초기화 된 경우 THREAD_ID_2 == THREAD_ID_3 == THREAD_ID_4! = THREAD_ID_1 (예상되는대로 공장이 이러한 객체를 생성하며 팩토리의 스레드에있을 수 있음). 그 스레딩 모델은 thread_id_3 == thread_id_4하지만 전환 있지만 COM 개체가 스레드에서 생성 될 수 있지만, thread_id_2 및 thread_id_1 다른 경우 2.

이 일치하지 않는 것, 그리고 상황에서 원치 않는 동작이 발생할 수 있습니다 다른 스레드가 관련되어 있습니다. 레지스트리에만 의존하고 coregisterclassobject를 사용하지 않는 경우 STA에서 자유 스레드 객체를 만든 경우 객체가 MTA에있는 com에 의해 생성 된 다른 스레드에 만들어집니다. 그러면 세 번째 스레드가 생성됩니다. 또한 STA에 있었기 때문에 객체를 생성 할 때 새로운 MTA 스레드가 아닌 최초의 MTA 스레드에 객체를 넣을 수있었습니다 (객체의 threadingmodel과 스레드의 아파트 유형이 반대로 된 경우에도 마찬가지입니다). 그러나 coregisterclassobject를 사용하여 위와 같은 자체 공장을 만들고 개체가 다중 스레드되었지만 STA에 스레드가있는 경우 이러한 다중 스레드 개체를 만든 각 새 스레드가 새 MTA 스레드를 생성하므로 낭비적이고 일관성이 없습니다. 정상적으로 일어나는 일.

답변

3

다중 스레드 아파트에서 클래스 팩토리를 만들면 여러 스레드에서 호출 할 수 있습니다. 따라서 이름은 "multi-threaded"입니다. 왜 당신은 그것이 놀라운 것을 정확히 발견합니까?

특히 COM 런타임은 MTA에 아파트 간 호출을 실행하는 스레드 풀을 유지 관리합니다. 멀티 쓰레드로 선언 된 객체는 그 쓰레드에서 호출 될 수 있습니다. 내가 을 만드는 STA도 있었다 세 번째 스레드를, 양산

다음 경우 객체는 첫 번째 COM-양산 MTA 스레드에없는 새로운

이 그것을 거기에 둘 것 진술은별로 의미가 없습니다. 다중 스레드 객체는 특정 스레드에 속하지 않으므로 "객체 ... 넣기 ... MTA 스레드"라는 의미가 명확하지 않습니다. 다중 스레드 개체는 MTA에 가입 한 모든 스레드 (프로그램이 명시 적으로 만든 스레드인지 또는 COM 런타임에 의해 생성 된 스레드인지)에 따라 만들어지고 호출 될 수 있습니다. 그러한 thread에 의해 동시에 호출 할 수가 있습니다.

관찰 된 동작의 차이는이 사실 때문입니다. 아파트 간 호출은 창 메시지 형태로 STA 스레드에 전달됩니다. STA 스레드는 GetMessage을 호출하여 들어오는 호출을 수락 할 준비가되었음을 알립니다. 반면 MTA에 대한 아파트 간 호출은 창 메시지를 사용하지 않고 문서화되지 않은 일부 지정되지 않은 메커니즘을 사용합니다. 이러한 호출은 COM 작성 스레드 풀의 스레드에서만 제공 할 수 있습니다. COM 런타임은 명시 적으로 작성한 스레드를 명령 할 수 없습니다. 주어진 시간에 스레드가 수행하는 작업을 알지 못하기 때문입니다. 스레드가 "임의의 COM 호출을 받아 들일 준비가되었습니다."라고 말할 수있는 API는 없습니다. COM의 스레드 풀에 실제로 참여할 수 있습니다.

이 점을 염두에두고 시나리오를 살펴 보겠습니다. 사례 A : ThreadingModel=Free으로 등록 된 일반 COM 개체가 있습니다. 사용자 지정 클래스 팩토리에서는 재미있는 업무가 없습니다. STA 스레드는 해당 개체의 CLSID을 사용하여 CoCreateInstance을 호출합니다. COM은 레지스트리에서 정보를 읽고 개체가 다중 스레드임을 확인하고 MTA 스레드 풀의 스레드 중 하나에 대한 호출을 마샬링하여 개체를 만들고 해당 인터페이스 포인터를 다시 정리합니다. 같은 CLSID을 사용하여 STA 스레드 (동일한 스레드 또는 다른 스레드)가 CoCreateInstance을 다시 호출하면 프로세스가 반복되고 풀에서 같은 스레드가 스레드를 처리하게됩니다.

그런데 개체를 만든 스레드가 OutputOwningThreadId 호출을 처리하는 동일한 스레드 일 필요는 없습니다. 실제로 OutputOwningThreadId을 두 번 연속 호출하면 특히 여러 스레드의 동일한 객체에서 동시에 호출하는 경우 서로 다른 스레드 ID를보고하는 경우가 많습니다. 그것은 잘못된 이름입니다 : MTA에서는 "소유하는 스레드"같은 것이 없습니다.

사례 B : 당신은 클래스 공장을 만들어 사용자의 명시 적 FactoryThread를 회전 한 다음 뭔가하고 바쁜 얻는다 (이 메시지 펌프가 MTA에 관련이 돌고 있다는 사실을, 그것은 수 단지뿐만 아니라 Sleep(INFINITE)). 이 스레드는 COM 런타임에 제한이 없습니다. 앞에서 말했듯이, COM은 마술처럼 어떤 일을하던간에 도중에 중단 할 수 없으며 일부 COM 호출을 실행하게합니다. 따라서 사례 A에서와 마찬가지로 COM 관리 스레드 풀의 일부 스레드에서는 CreateInstance과 (잘못 명명 된) OutputOwningThreadId 호출이 모두 실행되지만 FactoryThread에서는 절대로 실행되지 않습니다.

네 접근법에서는 기본적으로 하나의 스레드를 낭비하고 있습니다. 이것은 레지스트리를 피할 수있는 이점을 얻기 위해 엄청난 대가가되는 것 같지 않습니다.

+0

알 수 있습니다. COM이 STA 및 MTA 스레드와 다르게 통신한다는 사실을 깨닫지 못했습니다. 당신의 도움을 주셔서 감사합니다. – bdwain