나의 COM(Component Object Model) 경험담 #4 자이제네번째입니다. 조만간진짜 COM 컴포넌트를구현해보겠지만, 아직기초가부 족합니다. 조금만참읍시다. 조금만더하고실제코딩을하겠습니다. 이제제글을읽는분들은뒤에말을하지않더라도아실겁니다. 그럼시작하겠습니다. 잠시잡담좀하자. 잡담이제일재미있지않나? 머리아프게 COM 어쩌고저쩌고이젊은나이에뭐란말인가? 여러분들은이글을읽는목적이무엇인가? COM을알기위해서? 아니면 COM 컴포넌트를직접만들고싶어서? 그냥나의말투가재미있어서? 아니면정말심심해서인가? 내가처음 COM을공부할때는아무런목적의식이없었던것같다. 남들이잘안하고어렵고 MS에서아주강조하는중요한기술이라서하려고했었다. 결국, 돌아온것은 COM앞에서비참하게무너지는내자신이었다. 이제는목적의식을가져야한다. 그래야머리에도잘들어온다. 그런의미에서내가생각하는 COM을해야하는이유를말하겠다. 게임프로그래밍을하는사람은 DirectX를해야하고인터넷과관련된사람들은 ActiveX와 ASP를할것이다. 그리고엔터프라이즈급의프로그래밍을하는사람은 DCOM과 COM+ 도해야한다. 여기모든곳에 COM이숨어있다. DirectX를처음코딩해본사람은그코딩방식에당황하지않은사람은없었을것이다. 일반 Win32 API로코딩하는것과는너무나틀렸기때문이다. COM을모르는사람은그냥책에그렇게써있으니따라하면서구현한다. 대부분이렇지않나싶다. 프로그래밍의분야에서는오히려 COM과관련이없는분야가더적다고해도과언이아니다. MS가운영체제내부를거의 COM으로도배를해놓았으니어쩔수없다. 그리고일반어플리케이션프로그래머들도이제는적응을해야한다. 지금까지는 CPrettyButton 같은일반클래스들을복사해서사용해왔지만, 서서히 COM 컴포넌트를사용해야할것이다.
그리고 난절대 COM과관련된프로그래밍을하지않아 라고장담하는사람들조차도자신도모르게사용하고있다는것을알고있는지모르겠다. 쉘이바로그대표적인예이다. 단축아이콘을만들고, 아이콘트레이를사용하고하는것에서실제코딩에는 COM과관련된부분이없을지라도내부적으로 COM을사용한다는것을말이다. 바로이런이유에서 COM을알아야한다. 더가치있고더비싸게팔수있는프로그램을만들려면 COM이필수인것이다. 자이제여러분들의몸값을올릴수있는 COM을계속해보자.( 그렇다고내글을읽는다고해서몸값이올라가진않는다. 자신과의끝없는싸움을해야하는일이다.) #3에서짜가 COM 컴포넌트를구현해보면서 IUnknown 인터페이스에대해서알아봤다. 그럼오늘은뭘할것인가? 바로 IClassFactory 인터페이스이다. IUnknown이어떤일을하는지알았으니이제는 IClassFactory가어떤일을하는놈인지알아보자. 먼저이부분은조금어렵다. 그래서하나의전제를가지고시작하겠다. 어렴풋이나마이말을기억한상태에서읽어나가자. 클래스팩토리는메인컴포넌트를생성하기위한보조컴포넌트이다. 이보조컴포넌트는오 직하나의인터페이스 IClassFactory 만을가지고있다. 여기서왜알아봐야하는데? 라고의문을가지는사람이있을것이다. 그렇지않는사람은반성해야한다. 내가무슨말을하건딴지걸생각부터해야발전할수있다. 누가무슨말을해도마찬가지이다. 교수님이강의할때도딴지걸준비부터해봐라. 강의가재미있어진다.( 그렇다고실제로하진마라. 점수제대로못받는다.) COM 인터페이스는크게두가지로나뉘어진다. 표준 COM 인터페이스와커스텀 COM 인터페이스이다. COM에의해기본적으로제공되는인터페이스는표준 COM 인터페이스이고프로그래머가특정한목적에따라새로정의한인터페이스가커스텀 COM 인터페이스이다. IUnknown, IClassFactory는바로표준 COM 인터페이스에속하는것들이다. 따라서우리는표준 COM 인터페이스를이해하지않고서는 COM을이해하는것이불가능하므로무조건이해하고넘어가야한다. Factory 해석해보면공장이라는말이다. 공장 : 뭘만드는곳이잖아? 바로 COM 컴포넌트의인스턴스를생성하는놈이이놈이다. 즉이놈도 IUnknown 만큼이나중요한놈이었다. 그럼이놈을찬찬히뜯어보자.( 그렇다고이놈이실제로생성하는건쥐뿔도없다. 앞에서우리가 IUnknown의쿼리인터페이스를직접한것과같이우리가일일이 new 하면서 COM
개체를생성해줘야한다. 이부분은나중에보면알것이다.) #1 에서 CoCreateInstance 함수에서 COM 컴포넌트를만든다고했는데이건또무슨소리냐 하는분들이있을것이다. 이유를설명하겠다. 사실, 난소스가지고이러쿵저러쿵설명하는거제일싫어한다. 짜증난다. 지겹다. 책덮고싶어진다. 그런데내가그렇게하게생겼다. 하지말아버릴까보다. 일단오늘한번만해보고반응을봐야겠다. 소스를가지고설명이시작되면일반책과크게틀려지지않기때문이다. 여러분제할일끝났으니이제책보세요 ~~ 이거랑같은말인것이다. 아서서히잠수타고싶어진다. 어쨌든, 그건그거도우리는하던얘기를계속해야한다. 자다음코드를먼저살펴보자. STDAPI CoCreateInstance( REFCLSID rclsid, LPUNKNOWN punkouter, DWORD dwclscontext, REFIID riid, LPVOID * ppv ) *ppv = NULL; IClassFactory* plfactory = NULL; HRESULT hr = CoGetClassObject( rclsid, dwclscontext, NULL, IID_IClassFactory, (LPVOID*)&pIFactory ); If(SUCCEEDED(hr)) Hr = pifactory >CreateInstance(pUnkOuter, riid, ppv); pifactory >Release(); return (hr);
여기서직감적으로 pifactory >CreateInstance 에서컴포넌트가생성될것이라는것을알수있다. 결국 CreateInstance 메서드는 IClassFactory 인터페이스의메서드였다. 어쨌든, CoCreateInstance 에서만든다고해도틀린말은아니었다. 내부에서생성하니말이다.( 우길사람은우겨라. 친구랑 CoCreateInstance에서생성한다. 아니다. CreateInstance에서생성한다고싸우는것은어리석은짓일것이다.) 그럼왜 IClassFactory인터페이스가있어야하는지도답이나왔다. #2의내용을상기해보자 1. CoInitialize(NULL); 2. CoCreateInstance 에인자로넘길각종변수선언및초기화 ; 3. CoCreateInstance(clsid, ); 4. COM 컴포넌트사용 5. CoUninitialize(); 이것은 COM 컴포넌트를사용하는전체적인흐름이다라고했다. 여기서 CoCreateinstance 를호출할때내부에서필요로하는것이바로 IClassFactory 이기 때문이다. 위의코드는 COM 라이브러리내부에구현되어있는부분이다. COM 라이브러리는또뭐야하는분이있을거다. 그건 COM을사용가능하게해주는일종의 API라고생각하면되겠다. 좀더자세히말하자면, COM을사용하는모든어플리케이션에서유용하게사용될수있는컴포넌트관리서비스를제공해준다는말이다. 구체적인예를들면, CLSID를가지고레지스트리에서실제 COM 서버가어디에위치하는지를찾아낸다거나 COM 개체를생성하고, 메모리를관리해주는등등의일을해주는기본적인기능을하는것도이 COM 라이브러리가해주는역할이다. Co 어쩌고저쩌고시작하는함수는전부 COM 라이브러리라고함수라고생각하면되겠다. 앞에서모든인터페이스는 IUnknown 에서상속받는다고했으니 IClassFactory 역시예외는 아닐것이다. 그럼어떻게선언되어있는지한번구경해보자. IClassFactory 도 COM 컴포 넌트가반드시제공해야하는인터페이스이므로주의깊게보자. Interface IClassFactory : public IUnknown virtual HRESULT stdcall CreateInstance(
LPUNKNOWN punkouter, REFIID riid, LPVOID* ppv) = 0; virtual HRESULT stdcall LockServer(BOOL block) = 0; 그냥메서드두개를추가한것이다다. 우리가오버라이드해야할메서드가두개늘었다는것이다. 그이상도그이하도아니다. 앞으로얼마나더오버라이드를해야할까? 두고보자. ( 기뻐해라 ~~~~~~~~~~~~~~ 사실은이두개의인터페이스만하면실제 COM 컴포넌트의구현은어느정도가능해진다. 그럼다음에는진짜 COM을만들어서테스트의기쁨을누려보자.) 그럼, C정거장Factory를만들때이렇게만들면되겠군.. class C 정거장 Factory: public IClassFactory public: //interface IUnknown methods HRESULT stdcall QueryInterface(REFIID riid, void **ppobj); ULONG stdcall AddRef(); ULONG stdcall Release(); //interface IClassFactory methods HRESULT stdcall CreateInstance(IUnknown* punknownouter, const IID& iid, void** ppv) ; HRESULT stdcall LockServer(BOOL block) ; private: ; long m_nrefcount; 한김에바로두메서드를오버라이드해보자. HRESULT stdcall C 정거장 Factory::CreateInstance(IUnknown* punknownouter,
const IID& iid, void** ppv) // aggregate 을지원하지않는다. if (punknownouter!= NULL) return CLASS_E_NOAGGREGATION ; // 실제 COM 컴포넌트인스턴스를생성한다. C 정거장 * pobject = new C 정거장 ; if (pobject == NULL) return E_OUTOFMEMORY ; // 클라이언트에서요청한인터페이스를요청한다. return pobject >QueryInterface(iid, ppv) ; 이 CreateInstance 메서드는 CoGetClassobject 함수에전달된 CLSID 에대응되는 COM 컴포넌트의인스턴스만을생성한다. 이말은 CLSID 가인자로필요없다는뜻이다. 하나의 COM 컴포넌트와클래스팩토리는한쌍으로묶여있기때문이다. 참고로 CoGetClassobject 는 COM 라이브러리함수인것을알수있다. 앞에 Co 가 붙었으니말이다. 이함수가하는일은레지스트리에서 CLSID 와연결된 COM 컴포넌트 서버를메모리에로드한다. HRESULT stdcall C 정거장 Factory::LockServer(BOOL block) return E_NOTIMPL; 이 LockServer 는왜필요한가? 앞에서정거장을예로들면서우주선이하나도없으면정거 장의전기를모두내린다고했다. 그런데, 잘못하여우주선의수를잘못계산하게되어버 린것이다. 그러면우주선이있는데도전기를차단시켜버렸고정거장은그우주선사람들
로부터욕을바가지로얻어먹게될것이다. 이문제에대한보험이바로이메서드라고생 각하면되겠다. 이 COM 컴포넌트가여러클라이언트에서사용하는것이확실하다면 다음과같이수정하면된다. HRESULT stdcall C 정거장 Factory::LockServer(BOOL block) if(block) ++g_clocks; else g_clocks; return S_OK; 이렇게되면컴퍼넌트의언로드시기를검사할때두가지를검사하면된다. 앞에서구현한 우주선의수가 0 이고 g_clocks 도 0 일때그렇게하면된다는말이다. 우주선의수가 0 인지 만검사하는것보다는안정적일것이다. 자 IClassFactory 도끝났다. 하지만, IUnknown 에비해어렵다고생각할것이다. 지금은그 냥 IUnknown 과같이구현해야하는필수과정이라고만생각하자. 우리가 COM 컴포넌트프로그래밍을하다보면짜고치는고스톱이라는느낌을받게된다. 말그대로 IUnknown 이랑 IClassFactory는이런메서드들을구현해라하고우리는 COM 컴포넌트를만들면서거의내용이바뀌지않는코딩을어쩔수없이강요당하게된다. IUnknown 과 IClassFactory는해주는것이전혀없다. 그냥이런메서드구현해라라면서은근히강요만할뿐이다. 내가다한 (?) 거나마찬가지인것이다. 그리고이부분의코딩은 MS가하라는데로따라하기만하면된다. 내가맘대로건드릴부분도아니다. 오늘은 IClassFactory를이해하려고노력했다. 하지만, 아직감이오질않는다. 다시한번이것을이해하는방법은이렇게생각하는것이쉽다. 클래스팩토리는메인컴포넌트를생성하기위한보조컴포넌트이다. 이보조컴포넌트는오직하나의인터페이스 IClassFactory만을가지고있다.
내용이점점복잡해진다. 나도두서없이말하고또말하고그런느낌이다. 앞으로는좀머 리속으로정리좀하고올려야겠다. 그럼아무래도시간이걸리겠쥐? 오늘도고생들하셨다. 오늘은좀구체적으로나갔다. 그런데이게도움이될지모르겠다. 차라리책이더낳지않을까싶다. 물론내가설명하는방식과책은차이가있을것이다. 책보다더설명을잘할자신은없다. 왠지이제는갈림길에선느낌이다. 오늘은푹자야겠다. 맨날비몽사몽간에글을써서미안한감이없지않지만, 낮에한번읽어보니그렇게엉터리말은없는것처럼보인다. 사실, 내가미안해할이유도없다. 그렇지않나? 읽기싫으면안읽으면그만인데.. ㅡㅡ ;( 밤길가다칼맞을라..) e mail : icoddy@hotmail.com msn id : icoddy@hotmail.com 박성규