1) 만들어 놓은 클래스 라이브러리를 멤버변수로 선언하여 사용하게 되면 강한 결합을 하게 된다.
2) 그래서, 인터페이스를 사용하여 약한 결합을 만드는데 이게 바로 COM이 하는 역할이다.
3) 3개의 함수는 QueryInterface, AddRef, Release 를 반드시 구현해 줘야 한다.
4) 컴포넌트가 객체생성을 책임져야 하므로 반드시 IUnknown 를 최상위 부모로 하여 상속되어야 한다.
5) CreateInstance, DllGetClassObject 를 사용하여 객체를 생성해줘야 한다.
6) 이모든 과정을 단순화 한 것이 ATL, COM+ 이다...그래도 어렵다..ㅜ_ㅜ..
- regsvr32 DLL명- Com.dll 을 레지스트리(HKEY_CLASSES_ROOT\CLSID) 에 등록해준다.
1. IFace.h ( 인터페이스ID, 클래스ID 를 등록한다 )
more..
#ifndef _IFACE_H
#define _IFACE_H#undef _UNICODE
#undef UNICODE
#include <windows.h>
#ifndef interface
#define interface struct
#endif
// 모든 interface는 반드시 IUnknown 에서 상속되어야 한다.
// 모든 전화기의 interface
interface IPhone : public IUnknown
{
virtual void Calling( char* number ) = 0;
};
// 모든 계산기의 interface
interface ICalc : public IUnknown
{
virtual int Plus( int a, int b ) = 0;
virtual int Minus( int a, int b ) = 0;
};
// 모든 interface의 고유한 번호를 부여 해야 한다.
// {10A4AA2B-EEBE-4491-B91E-F610AA3DB6C3}
static const IID IID_IPhone = // Interface ID
{ 0x10a4aa2b, 0xeebe, 0x4491, { 0xb9, 0x1e, 0xf6, 0x10, 0xaa, 0x3d, 0xb6, 0xc3 } };
// {76AAF296-23B4-43de-8107-7381DEADD769}
static const IID IID_ICalc =
{ 0x76aaf296, 0x23b4, 0x43de, { 0x81, 0x7, 0x73, 0x81, 0xde, 0xad, 0xd7, 0x69 } };
// Com 자체에도 ID를 부여한다.
// {026E86AC-95FA-4166-B77E-764BFAE33D4C}
static const CLSID CLSID_AnyCall = // class ID
{ 0x26e86ac, 0x95fa, 0x4166, { 0xb7, 0x7e, 0x76, 0x4b, 0xfa, 0xe3, 0x3d, 0x4c } };
#endif // _IFACE_H
2. AnyCall.h ( 인터페이스를 제공한다. )
more..
#ifndef _ANYCALL_H
#define _ANYCALL_H #include "IFACE.h"
#include <iostream>
using namespace std;
class AnyCall : public IPhone, public ICalc
{
LONG m_ref;
public:
AnyCall() : m_ref(0) { cout << "Created AnyCall" << endl; }
~AnyCall() { cout << "Destroyed AnyCall" << endl; }
virtual void Calling( char* number );
virtual int Plus( int a, int b );
virtual int Minus( int a, int b );
// 결국 모든 인터페이스는 IUnknown의 자식이므로
// 모든 COM에는 아래 3개의 함수가 들어 있어야 한다.
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
virtual HRESULT __stdcall QueryInterface( const IID& iid, void** p );
};
// 객체 생성을 클라이언트가 직접 할 경우에는 반드시 AnyCall.h 가 전달되어야 한다.
// 즉, COM 이 변경되면 Client 도 다시 빌드해야 한다. - COM이 스스로의 객체를 만들어
// 줄 수 있어야 한다.
// 내부적으로 이미 선언되어 있다.
/*
#ifdef DLLSOURCE
#define DLLAPI _declspec(dllexport)
#else
#define DLLAPI _declspec(dllimport)
#endif
extern "C" DLLAPI IPhone* CreateInstance();
*/
#endif // _ANYCALL_H
3. AnyCall.cpp ( IUnknown 의 3개의 가상함수를 구현하고 기능을 구현해준다. )
more..
#define DLLSOURCE
#include "AnyCall.h"
#include "Registry.h"
void AnyCall::Calling( char* number )
{
cout << "Calling with Anycall - " << number << endl;
}
int AnyCall::Plus( int a, int b )
{
return a + b;
}
int AnyCall::Minus( int a, int b )
{
return a - b;
}
ULONG __stdcall AnyCall::AddRef()
{
return InterlockedIncrement( &m_ref );
}
ULONG __stdcall AnyCall::Release()
{
if( InterlockedDecrement( &m_ref ) == 0 )
{
delete this;
return 0;
}
return m_ref;
}
// 하나의 인터페이스를 사용 다른 인터페이스를 얻기 위해 호출하는 함수
HRESULT __stdcall AnyCall::QueryInterface( const IID& iid, void** ppv )
{
if( iid == IID_IUnknown ) // operator==(IID&, IID&) 가 미리 구현되어 있다.
{
// IPhone으로 해줘도 된다. 멤버 data가 없다.
*ppv = static_cast<IPhone*>(this);
}
else if( iid == IID_IPhone )
{
*ppv = static_cast<IPhone*>(this);
}
else if( iid == IID_ICalc )
{
*ppv = static_cast<ICalc*>(this);
}
else
{
// 지원하지 않은 인터페이스를 요청 했다.
*ppv = 0;
return E_NOINTERFACE;
}
static_cast<IPhone*>(*ppv)->AddRef();
return S_OK;
}
/* 무조건 IPhone 만 리턴하므로 원하는것을 리턴받아야 한다.
IPhone* CreateInstance()
{
IPhone* p = new AnyCall;
p->AddRef();
return p;
}
*/
static HMODULE g_hModule = NULL ;
const int CLSID_STRING_SIZE = 39;
const char szFriendlyName[] = "AnyCall Example";
const char szVerIndProgID[] = "AnyCall.Control";
const char szProgID[] = "AnyCall.Control.1";
BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
g_hModule = hModule;
break;
}
return TRUE;
}
// CreateInstance를 대체할 함수..
// #define STDAPI extern "C" HRESULT __stdcall
STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv)
{
//if( clsid == CLSID_AnyCall ) 라면 객체생성을 한다.
AnyCall* p = new AnyCall;
if ( iid == IID_IUnknown )
{
*ppv = static_cast<IPhone*>(p);
}
else if ( iid == IID_IPhone )
{
*ppv = static_cast<IPhone*>(p);
}
else if ( iid == IID_ICalc )
{
*ppv = static_cast<ICalc*>(p);
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
STDAPI DllRegisterServer()
{
// Get server location.
char szCLSID[CLSID_STRING_SIZE];
char szModule[512] ;
char szKey[64] ;
DWORD dwResult = ::GetModuleFileName(
g_hModule,
szModule,
sizeof(szModule)/sizeof(char)) ;
if(dwResult == 0)
return E_FAIL;
// CLSID\{504966F2-40AB-4bff-916A-15E3E91424B3}키를 조합하기 위한 루틴
ClsidToChar(CLSID_AnyCall, szCLSID, sizeof(szCLSID));
strcpy(szKey, "CLSID") ;
strcat(szKey, szCLSID) ;
SetRegistryInform(szKey, NULL, szFriendlyName);
SetRegistryInform(szKey, "InprocServer32", szModule);
SetRegistryInform(szKey, "ProgID", szProgID);
SetRegistryInform(szKey, "VersionIndependentProgID",szVerIndProgID) ;
// HKEY_CLASSES_ROOT\LittleSum Example
SetRegistryInform(szVerIndProgID, NULL, szFriendlyName) ;
SetRegistryInform(szVerIndProgID, "CLSID", szCLSID) ;
SetRegistryInform(szVerIndProgID, "CurVer", szProgID) ;
// HKEY_CLASSES_ROOT\LittleSum.Control.1
SetRegistryInform(szProgID, NULL, szFriendlyName) ;
SetRegistryInform(szProgID, "CLSID\\", szCLSID) ;
return S_OK ;
}
STDAPI DllUnregisterServer()
{
// Build the key CLSID\\{...}
char szCLSID[CLSID_STRING_SIZE];
char szKey[64] ;
ClsidToChar(CLSID_AnyCall, szCLSID, sizeof(szCLSID));
strcpy(szKey, "CLSID\\") ;
strcat(szKey, szCLSID) ;
// CLSID Key - CLSID\{...} 삭제.
LONG lResult = DeleteRegistryKey(HKEY_CLASSES_ROOT, szKey) ;
// version-independent ProgID Key 삭제.
lResult = DeleteRegistryKey(HKEY_CLASSES_ROOT, szVerIndProgID) ;
// ProgID key 삭제.
lResult = DeleteRegistryKey(HKEY_CLASSES_ROOT, szProgID) ;
return S_OK ;
}
4. Registry.h( 레지스트리 함수를 제공한다. )
more..
#ifndef __Registry_H__
#define __Registry_H__ void ClsidToChar(const CLSID& clsid, char* szCLSID, int length);
BOOL SetRegistryInform(const char* szKey, const char* szSubkey, const char* szValue);
DWORD DeleteRegistryKey(HKEY hStartKey , const char* pKeyName );
#endif
5. Registry.cpp( 레지스트리 함수를 제공한다. )
more..
#undef UNICODE
#undef _UNICODE
#include <windows.h>
#include "registry.h"
// CLSID 를 문자열로 변경해 준다.
void ClsidToChar(const CLSID& clsid, char* szCLSID, int length)
{
LPOLESTR wszCLSID = NULL ;
HRESULT hr = StringFromCLSID(clsid, &wszCLSID) ;
wcstombs(szCLSID, wszCLSID, length) ;
CoTaskMemFree(wszCLSID) ;
}
BOOL SetRegistryInform(const char* szKey, const char* szSubkey, const char* szValue)
{
HKEY hKey;
char szBuf[1024] ;
// 레지스터리의 키 와 서버 키의 값을 szBuf에 기록한다.
strcpy(szBuf, szKey) ;
if (szSubkey != NULL)
{
strcat(szBuf, "") ;
strcat(szBuf, szSubkey ) ;
}
// szBuf에 기록된 키 값의 정보를 가지고 키를 생성한다.
long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT,
szBuf,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL,
&hKey,
NULL) ;
if (lResult != ERROR_SUCCESS)
{
return FALSE ;
}
RegSetValueEx(hKey,
NULL,
0,
REG_SZ,
(BYTE *)szValue,
strlen(szValue)+1) ;
RegCloseKey(hKey) ;
return TRUE ;
}
DWORD DeleteRegistryKey(HKEY hStartKey , const char* pKeyName )
{
DWORD dwRtn, dwSubKeyLength;
LPTSTR pSubKey = NULL;
TCHAR szSubKey[256];
HKEY hKey;
// NULL 값 혹은 pKeyName에 정보가 없다면 리턴한다.
if ( pKeyName && lstrlen(pKeyName))
{
if( (dwRtn=RegOpenKeyEx(hStartKey,pKeyName,
0, KEY_ENUMERATE_SUB_KEYS | DELETE, &hKey )) == ERROR_SUCCESS)
{
while (dwRtn == ERROR_SUCCESS )
{
dwSubKeyLength = 256;
dwRtn=RegEnumKeyEx(
hKey,
0, // always index zero
szSubKey,
&dwSubKeyLength,
NULL,
NULL,
NULL,
NULL
);
if(dwRtn == ERROR_NO_MORE_ITEMS)
{
dwRtn = RegDeleteKey(hStartKey, pKeyName);
break;
}
else if(dwRtn == ERROR_SUCCESS)
dwRtn=DeleteRegistryKey(hKey, szSubKey);
}
RegCloseKey(hKey);
}
}
else
dwRtn = ERROR_BADKEY;
return dwRtn;
}
6. Anycall.def ( dllexport 역할을 해준다. )
more..
LIBRARY Com.dll
EXPORTS
DllGetClassObject PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
1. UseCom ( Com 을 사용하는 방법 - dll로드가 아닌 레지스트리에 등록된 COM을 사용한다. )
more..
#undef _UNICODE
#undef UNICODE
#include <iostream>
#include <windows.h>
#include "../Com/IFace.h" // 인터페이스를 제공하는 헤더파일.
using namespace std;
typedef IPhone*(*CREATEINSTANCE)();
int main()
{
CoInitialize(0); // COM 라이브러리 초기화
IPhone* phone = 0;
// 레지스트리에 가서 CLSID를 찾은 후에 해당 DLL을 load하고
// DllGetClassObject 함수를 찾은 후에 호출해서 객체를 만든다.
HRESULT ret = CoGetClassObject( CLSID_AnyCall, // COM의 CLSID
CLSCTX_INPROC_SERVER, // DLL에 있다.
NULL, // 통합(NULL 사용안함)
IID_IPhone, // 원하는 인터페이스 ID
(void**)&phone); // 인터페이스를 담을 포인터.
if ( FAILED(ret ) )
{
cout << "Error" << endl;
return 0;
}
phone->Calling("010-111-2222");
ICalc* pCalc = 0;
ret = phone->QueryInterface( IID_ICalc, (void**)&pCalc);
cout << pCalc->Plus(1,2) << endl;
pCalc->Release();
phone->Release();
CoUninitialize(); // 초기화 해제.
}