인-프로세스서버 COM컴포넌트
- 같은 클라이언트 프로그램 안에서 COM컴포넌트가 생성되고 실행되는 것.
4.1 Win32 DLL의 이해
- 클라이언트 안에서 인-프로세스 서버 COM 컴포넌트가 실행되기 위해서는 Win32DLL이어야함
4.1.1 프로세스와 DLL
- 윈도우 운영체제의 프로세스는 4GB의 고유의 주소영역을 가짐. OS의 메모리관리자는 페이징을 이용
해 프로세스가 소유한 물리적인 메모리에 대해 4GB크기의 연속적인 리니어 주소 어딘가에 매핑되게 하
여 다른 프로세스의 메모리를 알 수 없게 한다.
> 하나의 프로세스에 문제가 생겨도 다른 프로세스는 영향받지 않음.
> DLL의 물리적 메모리 영역은 DLL을 로드한 어플리케이션의 프로세스 주소에 매핑된다.
4.1.2 DLLMain함수
- DLL이 메모리에 처음 로드되면 DllMain()이라는 진입점 함수가 호출된다. 모든 DLL이 제공해야 함
|
BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved){ switch(dwReason){ case DLL_PROCESS_ATTACH: break; //DLL이 프로세스 주소 영역에 매핑됨 case DLL_PROCESS_DETACH: break; //DLL이 프로세스 주소 영역에서 매핑 해제됨 case DLL_THREAD_ATTACH: break; //스레드 생성됨 case DLL_THREAD_DETACH: break; //스레드 종료됨 } } |
> 스레드 생성/종료, DLL로드/언로드에서 호출된다.
(참조 : http://msdn.microsoft.com/ko-kr/library/vstudio/ms682583(v=vs.110).aspx )
4.1.3 DLL익스포트 함수
- DLL을 사용하는 목적은 다른 어플리케이션이나 DLL에서 해당 DLL의 함수를 호출할 수 있게 제공하는
것이다. 이를 위해 DLL은 자신의 함수를 export해야 한다.
- DLL에서 함수 export하는 두 가지 방법
1) __declspec(dllexport)키워드의 사용
|
__declspec: 주어진 형식의 인스턴스가 MS전용 저장소 클래스 특성과 함꼐 저장하도록 지정하는 키워드 (참조: http://msdn.microsoft.com/ko-kr/library/dabb5z75.aspx) |
|
dllimport / dllexport키워드 : 함수, 데이터 및 개체를 DLL에 내보내거나 DLL에서 가져올 수 있음 (참조 : http://msdn.microsoft.com/ko-kr/library/3y1sfaz2.aspx ) |
2) 모듈정의파일( .def파일)에 export함수에 대한 정보를 기입
> 인-프로세스 서버 COM컴포넌트를 구현할 때에는 모듈정의 파일을 사용하는것이 좋음
예)
DLL프로젝트를 생성 후, 새항목 추가를 통해 def파일을 추가한다.
그리고 다음과 같이 내용을 채운다.
|
LIBRARY "inproccom.dll" EXPORTS DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE DllRegisterServer PRIVATE DllUnregisterServer PRIVATE |
위에서 파란색으로 표기한 부분이 추가한 내용이다.
4.2 인-프로세스 서버 COM 컴포넌트 익스포트 함수
- 다시한번 짚어보는 COM컴포넌트의 생성과정
|
1) CoCreateInstance()호출 2) CoCreateInstance()내부에서 CoGetClassObject()를 호출하여 COM객체 정보 검색 3) CoGetClassObject()에서 CoLoadLibrary()로 메모리에 로드 4) CoGetClassObject()는 DLL의 DllGetClassObject()를 호출한다. 5) DllGetClassObject()에서는 인수로 넘어온 CLSID에 대응하는 클래스팩토리 COM객 체를 생성한 후 IClassFactory인터페이스를 구해 CoGetClassObjet()로 반환 6) CoGetClassObject()는 5)에서 받아온 IClassFactory를 CoCreateInstance()로 반 환함 7) CoCreateInstance()에서는 받아온 IClassFactory의 CreateInstance()를 호출하여 COM객체 생성 |
4.2.1 DllGetClassObject함수
- COM라이브러리에서 DLL을 메모리에 로드한 후, DLL에 DllGetClassObject함수를 호출한다. 원형은
|
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv); |
> rclsid : CLSID레퍼런스타입. CoCreateInstance()의 첫 번째 인수에 지정된 CLSID가 전달됨.
> riid : rclsid로 지정된 CLSID로 식별되는 COM객체를 생성할 IClassFactory의 IID
> ppv : IClassFactory포인터를 저장할 포인터변수의 주소값
- DllGetClassObject()익스포트 함수에서는 클라이언트가 생성하고자 하는 COM객체, rclsid인수에 지정
된 COM객체에 대응되는 클래스팩토리 COM객체의 인스턴스를 생성하고, 해당 객체로부터 riid에 지정
된 IClassFactory 인터페이스 포인터를 구해 ppv인수에 넘겨주면 된다. 코드는 다음과 같다.
4.2.2 DllCanUnloadNow함수
- CoFreeUnusedLibraries() : COM라이브러리가 호출하는 함수. 사용되지 않는 COM컴포넌트 DLL을
메모리에서 언로드하여 해제하기 위함. DLL은 스스로 메모리에서 해제될 수 없다.
- DllCanUnloadNow() : COM라이브러리에서 DLL참조여부를 확인하기 위해 사용. S_OK리턴시 언로드
|
STDAPI DllCanUnloadNow(); |
- 이 export함수에서 자신의 COM컴포넌트가 클라이언트에 서비스중인지 알기위해 COM객체 카운터를
둔다. (전역으로)
|
LONG g_cObjects= 0; //COM객체 카운터 extern "C" STDAPI DllCanUnloadNow(void){ if(g_cObjects == 0){ return S_OK; } return FALSE; } |
> 이와 같이 COM객체 카운터를 둘 경우 이 카운터를 관리하는 코드를 추가해야 한다.
|
HRESULT STDMETHODCALLTYPE CHelloFactory::CreateInstance( LPUNKNOWN lpOuter, REFIID riid, LPVOID* ppv){ ... pHello = new pHello; if( pHello != NULL){ ++g_cObjects; hr = pHello->QueryInterface(riid, ppv); if( FAILED(hr)){ --g_cObjects; delete pHello; } } ... }
ULONG STDMETHODCALLTYPE CHello::Release(void){ if( --m_cRef == 0){ --g_cObject; delete this; } return m_ref; } |
4.2.3 DllRegisterServer 함수
- 클라이언트에서 COM컴포넌트 사용을 위해서 해당 컴포넌트는 레지스트리에 등록되어 있어야 함.
- DllRegisterServer()에서는 COM컴포넌트에 구현되어 있는 COM객체에 대한 정보를 레지스트리에 기
록하면 됨
> 기록할 정보는 다음과 같다 (위치는 HKCR\CLSID)
1) InProcServer32 : DLL의 경로에 관한 정보
2) ProgID : CLSID대신 프로그램이름을 나타낼 별칭
3) VersionIndependentProgID : 간단한 별칭이 들어감
(참조 : http://gomnezip.tistory.com/328)
- 이 과정 지원을 위해 3개의 Win32API함수를 사용해야 함.
1) RegCreateKeyEx() : 레지스트리 키를 생성 (존재할 경우 해당 키 open)
2) RegSetValueEx() : 레지스트리에 값 설정
3) RegCloseKey() : 레지스트리에 값 기록 후 키를 닫는 함수
위 DllRegisterServer를 구현한 예제
4.2.4 DllUnregisterServer함수
- regsvr32 /u 옵션으로 COM컴포넌트가 레지스트리에서 제거될 때 호출되는 함수. DllRegister에서 레지스트리에 키 값을 기록하는 과정이었다면, 이 함수에서 수행할 역할은 그 반대임. 키를 제거하면 됨.
* COM객체를 구현한다.
- CLSID는 GUIDGEN 으로 생성하여준다. idl파일도 만들어서 midl로 컴파일도 수행하여주고, 클래스 팩토리도 구현하여 둔다.
- 만들어진 DLL에 추가로 작업해 줄 함수는 다음 4개의 함수를 export하는것.
DllRegisterServer : COM객체의 정보를 DLL등록시 레지스트리에 기록하는 함수
DllUnregisterServer : COM객체가 등록 해제될 때, 기록했던 레지스트리 값을 지우는 기능 구현
DllGetClassObject : 클래스 팩토리를 반환하여 준다.
DllCanUnloadNow : 현재 COM컴포넌트가 참조중인지 여부를 판단하여 메모리 해제 여부를 반환
댓글