ATL(Active Template Library) 을알아? #2 어제브라질과의친선경기다들잘보셨겠죠? 아쉽게졌지만, 아나운서의말이생각나는군요. 우리도유소년을잘키우면호나우두같은선수가많이나오겠죠? 여기에대해차범근이한참머뭇거리더니웃으면서하는말이 쉽게나오는선수는아니죠. 흐흐 ^^; 많이생각하게하는말이었습니다. 쓸데없는말이었습니다. 그냥월드컵때가갑자기생각나서요. ^^; 그럼 2 강으로들어가보겠습니다. 오늘은조금어려운부분이라서내용을좀작게하겠습니다. ( 오 ~ 독자들을이렇게배려하는 내가참대단하게느껴지는군요. ㅋㅋㅋ사실, 둘러댈말이없어서요.^^) 자, 1 강에서는간단하게 ATL 맛보기를하였다. 사실, 제대로맛을봤을지는의문이다. ATL 이 라는놈은쉽다고생각하면쉽고, 어렵다고생각하면 MFC 는저리가라할만큼어려운것 도사실이다. 매도먼저맞는게낫다고어려운부분부터대충훑어보고지나가보자. CComPtr<ICreateErrorInfo> pcreateerrinfo; CComQIPtr<IErrorInfo, &IDD_IErrorInfo> perrinfo; 위의코드는실제구현부분을작성할때에러부분을처리하기위해사용해야하는부분이다. 이코드를이해하는사람이있는가? 이밖에도 1 강에서만들어본 MyNewComObj.h 파일의코드에도자세히보면, class CMyNewComObj : public IDispatchImpl<IMyInterface1, &IID_IMyInterface1, &LIBID_MYNEWCOMLib>,
public IDispatchImpl<IMyInterface2, &IID_IMyInterface2, &LIBID_MYNEWCOMLib>, public ISupportErrorInfo, public CComObjectRoot, public CComCoClass<CMyNewComObj,&CLSID_MyNewComObj> 이렇게상속받는것을볼수있다. 얼핏코드가어렵게보인다. 템플릿에익숙하지않은사람이라면누구나이해하기힘든코드이다. <> 이런모양자체가어색할것이다. 하지만, 미리겁먹지는말자. 그리고이템플릿은 COM 클라이언트를만들때는쓰일일이거의없다. 단지, COM 서버개체를만들때특히나 ATL을사용해서만들때사용되어질뿐이다. 아무래도작고빠른코드를만들기위해어쩔수없었으리라생각한다. 그렇다고해서내가여기서템플릿에대해여러분이납득할만한수준으로설명을해주느냐하면그렇지가않다. 나역시이템플릿은마냥어렵게만느껴진다. 내경험상으로는 MFC를사용할때 CObList 클래스를사용했을때써봤던단순한기억밖에없다. VC를전문으로다루는사이트를돌아다니다보면 WTL(Windows Template Library) 이라는말도자주보게되는데, 이라이브러리역시템플릿을이용한다. 한번공부해봐도손해보진않을듯싶다. < 참조 : http://www.codeproject.com/wtl/wtlintro1.asp> 일단중요한내용은아니니본론으로다시돌아가자. CComPtr이랑 CComQIPtr이앞에나왔다. 이클래스는 COM 인터페이스의포인터를관리하는놈이다. 즉템플릿선언에있어서 template<class T> 의 T에해당하는놈이위의코드에서는 ICreateErrorInfo 가된다. 물론, 실제적인코드에서 ICreateErrorInfo* p 를선언하고 p >Release() 이런형식으로사용이가능하겠지만, 더욱유용한연산자를추가하고관리를쉽게하기위해 CComPtr<ICreateErrorInfo> pcreateerrinfo 이러한형태로선언하고 pcreateerrinfo > 메서드형식으로 ICreateErrorInfo 의모든메서드를쓰는것을가능하게한다. 템플릿의목적마다다르겠지만 CComPtr의경우인터페이스를템플릿화했다고생각하면쉬울것이다. 그럼여기서의문이생긴다. 왜템플릿을굳이쓰는건가? 그냥인터페이스바로쓰면되지. 안그런가? 여기서중요한답을우리는이끌어내야한다. 내가나의 COM 경험담에서짜고치는고스톱이란말을참많이했던걸로기억한다. 인터페이스를구현할때마다 AddRef, Release, QueryInterface 를중복해서오버라이드해야하고, 참조관리역시만만치않은작업이었음을알았을것이다. 인터페이스상속을하더라도그작업을해주어야했었다. 이모든귀찮은작업을이템플릿이라는넘이처리해준다는것이다. 그럼우리는이제부터할일은뻔하다. 원하는코드를작성하다가템플릿이나오면 아쒸 ~ 또어려븐거나왔네 할것이아니라. 오 ~ 또나의수고를덜어주기위해니가나왔구나. 고맙다. 라고해야한다. 결국, 짜고치는고스톱쪽은모두 ATL이알아서해준다는것이다. 이것만해도코딩의양은반이상줄것으로생각된다.
자, 템플릿은잠시머리를식히고다시들여다보기로하자. 그럼이제다시쉬운따라하기로넘어가보도록하자. Insert 메뉴에서 New ATL Object 라는넘을선택해보자. 이런창이뜰것이다. 여기서 Simple Object 를선택하고 Next 버튼을누른다. 여기서생성하고자하는 COM 개체의이름을입력한다. 그리고원하는인터페이스의이름 도고치고싶다면고쳐라. 파일이름도고치고싶다면고쳐라. 하지만이건솔직히권할것이못된다. 헷갈리니깐
그리고 Attributes 텝을눌러보면다음과같은창이바뀔것이다. 결국여기서 Support ISupportErrorInfo 를선택하고확인을누르는것은 #1강에서했던 New Class 와똑같은작업이이루어진다. 그리고, 커넥션포인터부분도보인다. 이부분은이벤트구현과관계된것임을이전 COM 강좌에서언급했던걸로안다. 이부분은나중에좀더자세히알아보도록하자. 나의경우는그래서 New Class 보다는 New ATL Object 를그냥사용하고있다. 괜히복잡하게이것저것쓸필요는없을것같다. 하나만알아도부족함이없을것이다. 그리고, 지금당장은다알필요가없겠지만, ATL Object Wizard 에나오는카테고리라던가 Object의종류별특징은알아둘필요가있다. 왜냐하면기본적인작업을다해주는데굳이자기가이걸만들거라고 Simple Object 부터시작한다면시간만축낼뿐이기때문이다. 예를들어인터넷익스플로러에서사용되는인터페이스를사용하려고한다면간단하게 Internet Explorer Object가있다는사실만알아도이개체유형을선택해서생성하면쉽게되는것을일일이구현해야한다는말이된다. 자그럼이제앞에서언급한소스를한번천천히뜯어볼까합니다. 사실이부분은그렇게 중요하지않습니다. 이것까지알필요는없다는말입니다. 그냥 COM 의기초부터보신분 들은이것의역할만이해하시면됩니다. 그럼잠시살펴보겠습니다. class CMyNewComObj : public IDispatchImpl<IMyInterface1, &IID_IMyInterface1, &LIBID_MYNEWCOMLib>, public IDispatchImpl<IMyInterface2, &IID_IMyInterface2, &LIBID_MYNEWCOMLib>,
public ISupportErrorInfo, public CComObjectRoot, public CComCoClass<CMyNewComObj,&CLSID_MyNewComObj> 제가 CMyNewComObj 라고하나를만드니깐 ATL 에서자동으로이런소스코드를만들어줬죠. 생소한건역시나템플릿입니다. 그중에서도 CComObjectRootEx 와 CComCoClass 일거라생각합니다. 하나의개체를생성했을때소스코드를보면 CComObjectRootEx 와 CComCoClass 에서기본적으로상속받는것을보실수가있습니다. 이각각의놈들은하는역할이무엇일까? 어떤일을하는놈인지는간단하게알수있는방법이있습니다. 소스를찾아서메서드만확인해보면알겠죠. 분명히 COM에서봐왔던메서드들이나올것이뻔합니다. 그런것들을구현하는수고를덜어줄려고만든놈일테니말입니다. 먼저 CComObjectRootEx 이놈을살펴봅시다. 이놈의상위클래스가또있군요. 찾아보니 CComObjectRootBase 라는놈이군요. 이놈의메서드로 OuterAddRef, OuterRelease, OuterQueryInterface 라는놈이있습니다. 그리고여기서상속받은위의코드에서볼수있는 CComObjectRootEx의메서드로 Outer 대신 Internal 이붙은똑같은메서드들이있습니다. 내부와외부의구분은단순히개체통합에쓸때외부 COM 개체에위임을하느냐의여부에달렸습니다. 간단하죠? 그중에서도 InternalQueryInterface 메서드가중요한편에속할것같은데이메서드는내부적으로 AtlInternalQueryInterface 를호출합니다. 이메서드는소스코드에서볼수있는 COM 맵에서인터페이스를찾는역할을합니다. BEGIN_COM_MAP(CMyNewComObj2) COM_INTERFACE_ENTRY(IMyNewComObj2) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(ISupportErrorInfo) END_COM_MAP() COM 맵에는이렇게인터페이스들이엔트리로들어있습니다. 이맵든단순히 COM 개체가제공하는인터페이스를관리하는역할을하고있습니다. 하지만, 이클래스의핵심은클래스멤버로스래딩모델이있다는겁니다. 즉, 이템플릿매개변수로다음과같이 CComObjectRootEx<CComSingleThreadModel> 스래딩모델을받습니다. 즉, 이템플릿은스래딩모델을정의하기위해만든놈임을직감할수가있을겁니다. 너무자세하게알려고는하지맙시다. 다포기하게됩니다. 하지만, 꼭알아야하겠다는분은이클래스소스를보시면될겁니다. 궁금하신분은개인적으로공부하시길.. 그리고다음으로 CComObject 를알아보겠습니다. CComCoClass<CMyNewComObj,&CLSID_MyNewComObj> 이렇게사용되는걸볼수있는
데요. 이놈은 COM 개체클래스의이름을템플릿매개변수로사용함을알수있습니다. 실제로이놈이바로 AddRef, Release, QueryInterface의오버라이드를하는놈입니다. 가장중요한놈이라할수있겠죠.( 우리의수고를가장많이덜어줄놈입니다.) 이밖에도클래스팩토리도 COM 개체구현에서기본이었다는것을기억하실텐데요. 이부분을담당하는클래스도있습니다. 이를구현한클래스가 CComClassFactory 입니다. 하지만이부분은 CComCoClass 에서내부적으로처리하니깐그냥이런놈이있더라정도만아시면됩니다. 또, CComCreate 도있는데요. 이놈은 CreateInstance 메서드를가지고있습니다. 결국 CComCoClass 에서역시내부적으로처리하겠죠? 다들 COM 강좌에서 COM 개체를생성하는과정을기억하실겁니다. 거기에보면다나와있습니다. 더이상은언급하지않겠습니다. 결론은, CComCoClass 이놈이우리의귀찮은작업대부분을해준다는것을기억하시면될것같군요. 자, 이제게임오버입니다. 우리는 COM 배경지식만가지고있는상태에서 ATL에어떻게그러한기본기술들이숨어있는지대충알고넘어가면됩니다. CComObjectRootEx, CComCoClass 정도만확실히알고넘어가면됩니다. 중요한것은 COM 배경지식입니다. 이러한것들은어차피 ATL에서는내부에숨겨져서구현되어있기때문에구현부분은알필요가없습니다. 알필요가없다기보다는이해만하고넘어가면된다는겁니다. 헉 ~~~~~~~~~~~~~~ 내가잠시졸면서글을썼나보다. 갑자기왠존댓말을이렇게써댔지? 고치기귀찮아서그냥나둔다. 음, 역시사람은잠을많이자야해. 지금도잠이쏟아지는군. 오늘은조금 COM 기본지식이요구되는부분이었다. 실제구현에서는중요하지않을지라도어떻게대충돌아간다는정도는알아야할것같아서언급해보았다. 이밖에도처음에언급된소스에나오는, IDispatchImpl<IMyInterface1, &IID_IMyInterface1, &LIBID_MYNEWCOMLib> 이것도있지만, 이것역시대충 IDispatch 가보이는걸봐선 COM을조금이라도본사람이라면자동화와관련되어있음을알수있을것이다. 자동화의경우는내용이좀많아서오늘하기에는조금무리일듯싶다. 자동화와이벤트구현은다음강좌에서자세하게알아보도록하자. 오늘강좌는여기서마치는것이좋을듯하다. 오늘도다들수고많았다.
잡담입니다. 프로그래밍을오래해본사람들은모두느끼실테지만, 실력의향상그래프가계단모양처럼생기지않았을까라는생각을하지않을까하는데요. 오래된정체된느낌에서어느순간, 내가또이만큼성숙했구나하는생각말이죠. 하지만, 정체되었을때가프로그래머에게는엄청난스트래스로다가오는슬럼프일겁니다. 이제한줄만더코딩하면죽을것같다. 이제이책한장만더넘기면정신병원에가야할것같은생각많이하시죠? 그런순간은누구에게나있을겁니다. 제가해결책을알려드리죠. 이건절대비급이니유출금지입니다. 먼저뚜벅뚜벅걸어가서컴퓨터앞에앉아. 그리고그런생각들때, 툴띄우고일단코딩해 ~. 막그냥코딩하는거야 ~. 죽나안죽나보는거야. 거봐 ~ 안죽잖아. 그리고, 원서든번역서든읽어봐 ~. 정신멀쩡해 ~. 병원안가도돼. 또그런생각들면또이짓해 ~ 바로이겁니다. 기억합시다.~ ^^; 썰렁하다구요? 제가원래좀그래요. 즐거운하루보내시구요. 담강좌에서봐요.^^ e mail : icoddy@hotmail.com msn id : icoddy@hotmail.com 박성규