IT_Programming/MFC · API

CoCreateInstance 함수

JJun ™ 2007. 12. 13. 23:32

STDAPI CoCreateInstance(
  REFCLSID rclsid,
  LPUNKNOWN pUnkOuter,
  DWORD dwClsContext,
  REFIID riid,
  LPVOID * ppv

);

 

[개요]

 

로컬 시스템 자원을 사용하는 목적으로 제공되는 인스턴스 생성 함수다. CoCreateInstanceEx를 이용해서 다른 컴퓨터의 시스템 자원을 접근할수 있다.

 

HKCR\APPID\{appid}\RemoteServerName와 같은 방식으로 원격 컴퓨터의 시스템 자원이 접근하는 경우에는 CoCreateInstance를 사용할수 있는 것으로 보인다.

 

내부적으로 레지스터리에서 로컬 경로를 알아내고 실제 CoGetClassObject를 사용하는 방식으로 CoCreateInstance 함수가 구현되어졌다.

 

어찌되었던 파일 경로 부분을 알아내기 위해서는 CLSID를 알아야하고, CLSID를 알게되면 물리적인 파일에서 DLL를 로드하게 될 것이다.

 

그다음으로 알아야 할 내용은 IID인데 Interface ID의 줄여서 적는다. IID는 물리적인 경로 파일에서 내부적으로 구현한 여러 인터페이스중에서 받아낼 Interface ID를 지정하는 것이다.

 

그 결과는 최종적으로 ppv에 담겨서 온다.

 

결국 위에서 빨간색으로 칠한 부분은 필수 인자임에 틀림이 없다. 간혹 이런 부분중에 일부를 숨겨서 조금더 간단하게 제공하는 함수들이 있긴하지만, 기본적인 요소는 3가지가 꼭 필요하게 된다.

 

 

LPUNKNOWN pUnkOuter는 COM 기술이 많은 기술적인 요구가 생김에 따라서 COM 객체가 상속이나 포함이라는 개념을 도입하게 되었는데, 이런 부분때문에 생긴 경우라고 보면 되겠다.

 

결국 IID가 내부적으로 다른 여러 인터페이스로 구성이 되면서 부가적으로 요구되는 인터페이스 정보 정도로 알아두자.

 

DWORD dwClsContext는 다음의 값으로 구성된다. 이 부분은 생성된 물리적인 존재가 DLL이냐 아니면 EXE이냐에 따라서 크게 메모리 영역상의 구분이 이루어진다. 결국 메모리 영역에 대한 설정부분이라고 이해하면 되겠다. 또한 로컬 시스템이냐 원격이냐에 따라서 역시 뭔가 다른 접근법이 필요할 것이다. 결국 그런 부분에 대한 제어부분이라고 생각하면 되겠다. 위치와 메모리 조합에 대한 정보라고 간단하게 정리할수 있을 것이다.

 

 typedef enum tagCLSCTX {
    CLSCTX_INPROC_SERVER        = 0x1,
    CLSCTX_INPROC_HANDLER       = 0x2,
    CLSCTX_LOCAL_SERVER         = 0x4, 

// 일반적으로 사용하는 3경우 CLSCTX_ALL로 3경우를 OR한 #define 인자를 사용함.

 


    CLSCTX_INPROC_SERVER16      = 0x8,
    CLSCTX_REMOTE_SERVER        = 0x10,
    CLSCTX_INPROC_HANDLER16     = 0x20,

    CLSCTX_RESERVED1            = 0x40,
    CLSCTX_RESERVED2            = 0x80,
    CLSCTX_RESERVED3            = 0x100,
    CLSCTX_RESERVED4            = 0x200,
    CLSCTX_NO_CODE_DOWNLOAD     = 0x400,
    CLSCTX_RESERVED5            = 0x800,
    CLSCTX_NO_CUSTOM_MARSHAL    = 0x1000,
    CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000,
    CLSCTX_NO_FAILURE_LOG       = 0x4000,
    CLSCTX_DISABLE_AAA          = 0x8000,
    CLSCTX_ENABLE_AAA           = 0x10000,
    CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000,
    CLSCTX_ACTIVATE_32_BIT_SERVER = 0x40000,
    CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000
} CLSCTX;

 

[내부 구현 코딩]

CoGetClassObject(rclsid, dwClsContext, NULL, IID_IClassFactory, &pCF);
hresult = pCF->CreateInstance(pUnkOuter, riid, ppvObj)
pCF->Release();

 

IClassFactory를 경유함으로 CoCreateInstance를 사용하는 대상은 IClassFactory 부분을 꼭 구현해야 한다.

 

 

[Return Values]

상속또는 포함에 관련 에러 또는 CLSID 또는 IID 생성 에러로 구분이 된다.

S_OK

An instance of the specified object class was successfully created.

REGDB_E_CLASSNOTREG --CLSID에러

A specified class is not registered in the registration database. Also can indicate that the type of server you requested in the CLSCTX enumeration is not registered or the values for the server types in the registry are corrupt.

CLASS_E_NOAGGREGATION --상속 또는 포함에러

This class cannot be created as part of an aggregate.

E_NOINTERFACE --IID에러

The specified class does not implement the requested interface, or the controlling IUnknown does not expose the requested interface.

==========================================

 

COM Server를 Registry에 등록하지 않고 그리고 CoCreateInstance() 라는 함수를 사용하지 않고 COM 객체를 생성하는 방법을 발견하여 여기에 올립니다.

그저... 꽁수 정도로만 알아두시구요...그리좋은 팁은 아닐꺼라 생각됩니다.

실은 이곳에 제 글을 올려보고 싶었습니다. ^^

 

1. DLL 을 Load한다.

     HINSTANCE hDLL = LoadLibrary("library.dll");

 

2. DLL에서 DllGetClassObject함수 포인터를 가져온다.

     typedef HRESULT STDAPICALLTYPE DLLGETCLASSOBJECT(

          REFCLSID rClsID,

          REFIID riid,

          void **pv);

 

     DLLGETCLASSOBJECT * MyDllGetClassObject = NULL;

     MyDllGetClassObject = (DLLGETCLASSOBJECT*)GetProcAddress( hDLL, "DllGetClassObject" );

 

2. IClassFactory를 가져온다

    IClassFactory * lpclf = NULL;

    hr = MyDllGetClassObject( CLSID_MyTestCOM, IID_IClassFactory, (LPVOID*)&lpclf );

 

3. COM 객체를 생성한다.

    lpclf->CreateInstance( NULL, IID_IMyTestCOM, (LPVOID*)& m_pTestCOM );

    if( lpclf ) lpclf->Release();

 

 

전체적인 소스는 다음과 같습니다.

typedef HRESULT STDAPICALLTYPE DLLGETCLASSOBJECT(

    REFCLSID rClsID,

    REFIID riid,

    void **pv);

 

 

    IClassFactory * lpclf = NULL;

    DLLGETCLASSOBJECT * MyDllGetClassObject = NULL;

    HRESULT hr;

    HINSTANCE hDLL = LoadLibrary("MyComTest.dll");    // DLL은 전역으로 두거나 COM객체를 Release 할때 FreeLibrary하면 되지만, 여기서는 굳이 FreeLibrary하지 않습니다.

    if( hDLL == NULL )

    {

        MessageBox("MyComTest.dll 파일 찾을 수 없음.");

    }

 

    if( m_pTestCOM )

    {

        MessageBox("객체가 이미 생성 되었음.");

        return;

    }

 

    MyDllGetClassObject = (DLLGETCLASSOBJECT*)GetProcAddress( hDLL, "DllGetClassObject" );

    if( MyDllGetClassObject )

    {

        // ClassFactory 구하기

        hr = MyDllGetClassObject( CLSID_MyTestCOM, IID_IClassFactory, (LPVOID*)&lpclf );

        if( SUCCEEDED(hr) )

        {

            // COM 객체 구하기

            lpclf->CreateInstance( NULL, IID_IMyTestCOM, (LPVOID*)& m_pTestCOM );

            if( lpclf ) lpclf->Release();

        }

        else

        {

            CString msg;

            msg.Format("MyDllGetClassObject 수행 실패!! Error:%d(0x%.8x)", hr, hr);

            MessageBox( msg );

            return;

        }

    }

 

 

===========================================================================

STDAPI CoCreateInstance (

   FEFCLSID         rclsid,

   LPUNKNOWN     pUnkOuter,

   DWORD             dwClsContext,

   REFIID              riid,

   LPVOID *          ppv )

{

   *ppv = NULL;

   IClassFactory* pIFactory = NULL;

   HRESULT hr = CoGetClassObject(rclsid,

                                dwClsContext,

                                NULL,

                                IID_IClassFactory,

                                (LPVOID*)&pIFactory);

  

   if(SUCCEEDED(hr)) {

             hr = pIFactory -> CreateInstance(pUnkOuter, riid, ppv);

             pIFactory -> Release();

   }

   return (hr);

}

===========================================================================

 

1. CoCreateInstance() 함수

client application에서 CoCreateInstance 함수를 사용하여 COM 개체의 instance를 생성하려고 할때, CoCreateInstance 함수 내부에서는 CoGetClassObject 함수를 호출하게 된다.

 

2. < CoGetClassObject() 함수 ( CoCreateInstance()  < CoGetClassObject() )

시스템 레지스트리의 HKEY_CLASSES_ROOT 루트키 밑에 있는 CLSID 서브 키에서 rclsid

매개변수에 지정된 CLSID와 같은 서브키를 찾아, 해당 서브키 밑에 있는 InprocServer32

(인-프로세스 서버인 경우) 또는, LocalServer32(로컬 서버인 경우) 서브 키 값에 지정된

COM 개체를 포함하고 있는 COM 컴포넌트 서버(DLL, EXE)의 위치값을 얻는다.

 

3. < CoLoadLibrary() 함수 ( CoGetClassObject() < CoLoadLibrary() )

CoLoadLibrary() 함수에서 얻어온 위치값의 COM 컴포넌트 서버를 메모리에 로드한다.

 

1) DLL로 구현된 인-프로세스 서버

4. < DllGetClassObject() 함수 ( CoGetClassObject() < DllGetClassObject() )

CoGetClassObject 함수는 DLL의 DllGetClassObject 익스포트 함수를 호출하게 된다.

DllGetClassObject 함수는 IClassFactory interface를 구현한 클래스 팩토리 COM 개체의

인스턴스를 생성하고, CoGetClassObject 함수에서 요청한 IClassFactory 인터페이스

포인터를 리턴하게 된다.

 

5. < IClassFactory::CreateInstance() 함수

( CoCreateInstance() < IClassFactory::CreateInstance() )

만약 COM 컴포넌트가 성공적으로 클래스 팩토리 COM 개체의 인스턴스를 생성하고

IClassFactory 인터페이스 포인터를 리턴하였다면 CoCreateInstance 함수에서 리턴되는

IClassFactory 인터페이스 포인터를 통하여 IClassFactory::CreateInstacne 함수를 호출.

출처 : Tong - lufian님의 Mutimedia Programming통