G o o g l e은 웹문서를 수집(crawl)하면서 자동으로 문서의 HTML 버전을 생성합니다.
COM 개체 구현 실습
AddBack Local Server 예제 프로그램 작성
2
AddBack Local Server 구현
- Visual C++ 개발 환경의 [File/New] 메뉴 항목을 선택
- [New] 대화 상자의 [Properties] 탭에서 [Win32 Application] 항목 선택
- [Location] 텍스트 상자에 앞에서 생성한 AddBack 폴더를 지정
- [Project] 텍스트 상자에 ‘AddBack’이라고 입력
- [Location] 텍스트 상자 끝에 추가된 ‘AddBack’을 ‘OutProc’으로 변경
- [OK] 명령 단추를 클릭
3
4
- 앞에서 생성한 AddEnd.cpp와 Factory.cpp 파일을 OutProc 폴더에 복사한 후, 두 파일을 AddBack 프로젝트에 추가
- AddBack 프로젝트에 ‘AddBack.cpp’ 파일을 생성하여 추가한다
- AddBack.cpp 파일에 다음과 같은 6개의 파일을 포함시킨다
#include <windows.h>
#include <ole2.h>
#include "..\AddBack.h"
#include "..\Factory.h"
#include "..\Guid.h"
#include "..\Guid.cpp"
5
- WinMain 함수 구현
HINSTANCE g_hInstance;
DWORD g_dwRegister;
CFAddBack* g_pFactory;
BOOL OpenFactory(void);
BOOL CloseFactory(void);
void RegisterServer (void);
void UnregisterServer (void);
BOOL SetRegKeyValue(LPTSTR pszKey, LPTSTR pszSubkey, LPTSTR pszValue);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
HRESULT hr;
MSG msg;
hr = CoInitialize(NULL);
if(FAILED(hr)) {
MessageBox(NULL,
"COM 라이브러리를 초기화할 수 없습니다!",
NULL, MB_OK);
return 0;
}
6
// EXE 화일은 DllGetClassObject, DllCanUnloadNow, DllRegisterServer,
// DllUnregisterServer 함수를 노출시키지 못한다.
// 명령행에서 WinMain 함수에 넘어온 매개변수의 값이 RegServer 또는 UnregServer 옵션을
// 포함하고 있는 지를 검사하여 레지스트리에 등록 또는 해제하는 함수를 호출
if (lstrcmpiA(lpCmdLine, "-RegServer") == 0 ||
lstrcmpiA(lpCmdLine, "/RegServer") == 0) {
g_hInstance = hInstance;
RegisterServer();
return 0;
}
else
if (lstrcmpiA(lpCmdLine, "-UnregServer") == 0 ||
lstrcmpiA(lpCmdLine, "/UnregServer") == 0) {
UnregisterServer();
return 0;
}
if (lstrcmpiA(lpCmdLine, "-Embedding") == 0 ||
lstrcmpiA(lpCmdLine, "/Embedding") == 0)
OpenFactory();
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
CloseFactory();
::CoUninitialize();
return 0;
}
7
- ROT(Running Object Table)에 클래스 팩토리 COM 개체를 등록하는 코드 구현
BOOL OpenFactory(void) // CoGetClassObject에서 호출
{ // (ROT 참조후 없으면 호출)
BOOL bOK = FALSE;
HRESULT hr;
g_pFactory = new CFAddBack;
if(g_pFactory != NULL) {
g_pFactory->AddRef();
hr = ::CoRegisterClassObject(CLSID_AddBack, g_pFactory,
CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE,
&g_dwRegister);
bOK = SUCCEEDED(hr);
if(!bOK) {
g_pFactory->Release();
delete g_pFactory;
}
}
else
bOK = FALSE;
return bOK;
}
8
BOOL CloseFactory(void)
{
BOOL bOK = TRUE;
HRESULT hr;
if (g_dwRegister != 0) {
hr = CoRevokeClassObject(g_dwRegister);
if (FAILED(hr))
bOK = FALSE;
}
if(g_pFactory)
g_pFactory->Release();
return bOK;
}
9
- WinMain 함수 앞에 카운터로 사용할 전역 변수 정의
HINSTANCE g_hInstance;
DWORD g_dwRegister;
CFAddBack* g_pFactory;
LONG g_cObjects = 0; // COM 개체 카운터
LONG g_cLocks = 0; // 로크 카운터
BOOL OpenFactory(void);
BOOL CloseFactory(void);
void RegisterServer (void);
void UnregisterServer (void);
BOOL SetRegKeyValue(LPTSTR pszKey, LPTSTR pszSubkey, LPTSTR pszValue);
int APIENTRY WinMain(HINSTANCE hInstance,
- AddBack 프로젝트에 ‘Add.h’ 헤더 파일을 생성하고 앞에서 정의한 전역 변수 선언
extern LONG g_cObjects;
extern LONG g_cLocks;
void CloseExe (void);
10
- CFAddBack::CreateInstance 메소드에 COM 개체 카운터를 반영하는 코드 추가
HRESULT __stdcall CFAddBack::CreateInstance(LPUNKNOWN pUnkOuter,
REFIID riid, LPVOID* ppv)
{
HRESULT hr = E_FAIL;
CAddBack* pAddBack = NULL;
*ppv = NULL;
if(pUnkOuter != NULL)
hr = CLASS_E_NOAGGREGATION;
else {
pAddBack = new CAddBack;
if(pAddBack != NULL) {
++g_cObjects; // COM 개체 카운터를 증가시킨다.
// 클라이언트에서 요청한 인터페이스 포인터를
// CAddBack COM 개체에게 요청한다.
hr = pAddBack->QueryInterface(riid, ppv);
if(FAILED(hr)) {
--g_cObjects; // 실패한 경우 COM 개체 카운터를 감소시킨다.
delete pAddBack;
CloseExe(); // 가능하다면 종료한다.
}
}
else
hr = E_OUTOFMEMORY;
}
return hr;
}
11
- CFAddBack::LockServer 메소드에 Lock Counter를 반영하는 코드 추가
HRESULT __stdcall CFAddBack::LockServer(BOOL bLock)
{
if(bLock)
++g_cLocks;
else
--g_cLocks;
return S_OK;
}
- AddEnd.cpp 파일에 Add.h 헤더 파일을 포함시킨다
#include "..\AddEnd.h"
#include "Add.h"
#include "..\AddBack_i.c"
12
- CAddBack::Release 메소드에 COM 개체 카운터를 반영하는 코드를 추가
ULONG __stdcall CAddBack::Release(void)
{
if(--m_cRef == 0) {
// COM 개체 카운터를 감소시킨다.
--g_cObjects;
delete this;
// 가능하다면 종료한다.
CloseExe();
}
return m_cRef;
}
- CloseExe 함수를 구현
void CloseExe (void)
{
if (g_cObjects == 0 && g_cLocks == 0)
PostQuitMessage(0);
}
13
- AddBack COM 컴포넌트를 레지스트리에 등록 및 해제하는 RegisterServer와 UnregisterServer 함수 구현
void RegisterServer (void)
{
TCHAR szID[129];
TCHAR szCLSID[129];
TCHAR szModulePath[MAX_PATH];
wchar_t wszCLSID[129];
GetModuleFileName(g_hInstance, szModulePath,
sizeof(szModulePath)/sizeof(TCHAR));
StringFromGUID2(CLSID_AddBack, wszCLSID, 128);
wcstombs(szID, wszCLSID, 128) ;
lstrcpy(szCLSID, TEXT("CLSID\\"));
lstrcat(szCLSID, szID);
SetRegKeyValue(TEXT("AddBack.AddBack.1"), NULL,
TEXT("AddBack Component"));
SetRegKeyValue(TEXT("AddBack.AddBack.1"), TEXT("CLSID"), szID);
SetRegKeyValue(szCLSID, NULL, TEXT("AddBack Component"));
SetRegKeyValue(szCLSID, TEXT("ProgID"),
TEXT("AddBack.AddBack.1"));
// 인-프로세스 서버와 다른 부분
SetRegKeyValue(szCLSID, TEXT("LocalServer32"), szModulePath);
}
14
void UnregisterServer (void)
{
TCHAR szID[129];
TCHAR szCLSID[129];
TCHAR szTemp[129];
wchar_t wszCLSID[129];
StringFromGUID2(CLSID_AddBack, wszCLSID, 128);
wcstombs(szID, wszCLSID, 128) ;
lstrcpy(szCLSID, TEXT("CLSID\\"));
lstrcat(szCLSID, szID);
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("AddBack.AddBack.1\\CLSID"));
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("AddBack.AddBack.1"));
// 인-프로세스 서버와 다른 부분
wsprintf(szTemp, TEXT("%s\\%s"), szCLSID, TEXT("LocalServer32"));
RegDeleteKey(HKEY_CLASSES_ROOT, szTemp);
wsprintf(szTemp, TEXT("%s\\%s"), szCLSID, TEXT("ProgID"));
RegDeleteKey(HKEY_CLASSES_ROOT, szTemp);
RegDeleteKey(HKEY_CLASSES_ROOT, szCLSID);
}
15
BOOL SetRegKeyValue(LPTSTR pszKey, LPTSTR pszSubkey, LPTSTR pszValue)
{
BOOL bOk = FALSE;
LONG ec;
HKEY hKey;
TCHAR szKey[256];
lstrcpy(szKey, pszKey);
if(NULL != pszSubkey) {
lstrcat(szKey, TEXT("\\"));
lstrcat(szKey, pszSubkey);
}
ec = RegCreateKeyEx(HKEY_CLASSES_ROOT, szKey, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
NULL, &hKey, NULL);
if(ERROR_SUCCESS == ec) {
if(NULL != pszValue) {
ec = RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)pszValue,
(lstrlen(pszValue)+1)*sizeof(TCHAR));
}
if(ERROR_SUCCESS == ec)
bOk = TRUE;
RegCloseKey(hKey);
}
return bOk;
}
16
- AddBack 프로젝트를 build하여 AddBack.exe 파일 생성
- AddBack COM 컴포넌트를 레지스트리에 등록
- AddBack /regserver
- 이것을 수행하기 위해서는 예전에 등록하였던 것을 해제하고 새로 등록해야 한다.
- AddFront 애플리케이션을 실행하여 AddBack COM 컴포넌트 실행 확인
- AddBack 컴포넌트를 생성할 수 없다는 에러 발생
- AddBack Local Server에 인터페이스를 marshalling하는 Proxy/Stub DLL이 없기 때문
17
Proxy/Stub DLL 생성
MIDL 컴파일러가 생성한 코드 사용
- Visual C++ 개발 환경의 [File/New] 메뉴 항목 선택
- [New] 대화 상자의 [Project] 탭에서 [Win32 Dynamic Link Library] 항목 선택
- [Location] 텍스트 상자에 앞에서 생성한 AddBack 폴더 지정
- [Project] 텍스트 상자에 ‘Marshal’이라고 입력
- [Add to current workspace] 옵션 선택
- [OK] 버튼 클릭
18
19
- MIDL 컴파일러가 생성한 AddBack_i.c, AddBack_p.c, 그리고 DllData.c 파일을 Marshal 프로젝트에 포함시킨다
20
- 프로젝트에 RPC runtime import library를 추가하기 위해 [Project/Settings] 메뉴 항목을 선택하여 [Project Settings] 대화 상자의 [Link] 탭을 선택한 후 [Object/libraries modules] 텍스트 상자에 rpcrt4.lib 파일을 추가한다
21
- [Project Settings] 대화 상자 왼쪽의 트리뷰에서 DllData.c 항목을 선택하고, 오른쪽 [C/C++] 탭에서 [Preprocessor definitions] 텍스트 상자에 ‘REGISTER_PROXY_DLL’을 추가한다. 이것은 생성된 Proxy/Stub 코드가 자기 등록을 할 수 있게 하기 위한 것이다.
22
- Marshal 프로젝트에 ‘Marshal.def’ 파일을 생성하여 추가
LIBRARY Marshal.dll
DESCRIPTION 'Proxy/Stub DLL'
EXPORTS
DllGetClassObject @1 PRIVATE
DllCanUnloadNow @2 PRIVATE
GetProxyDllInfo @3 PRIVATE
DllRegisterServer @4 PRIVATE
DllUnregisterServer @5 PRIVATE
- Marshal 프로젝트를 build 하여 Marshal.dll 파일을 생성
- regsvr32.exe를 사용하여 Proxy/Stub DLL COM 컴포넌트를 레지스트리에 등록
- regsvr32 Marshal.dll
- AddFront 애플리케이션을 실행하여 AddBack COM 컴포넌트가 제대로 실행되는지 확인
'COM, ATL' 카테고리의 다른 글
IDispatch::Invoke (1) | 2008.08.16 |
---|---|
인보크 구현부 참조 (2) | 2008.08.16 |
자동화에서 타입라이브러리 등록과 해제 코드 (0) | 2008.08.14 |
com을 위한 기초 (3) | 2008.08.14 |
표준 프락시 스텁 dll 작성법 및 기본개념들 (0) | 2008.08.14 |
COM 이란 ... 참고자료....좀 잘못된 부분도 있는듯 하다 (0) | 2008.08.14 |
자동화 타입(BSTR, VARIANT, SAFEARRAY, UDT) (0) | 2008.08.14 |