[형식 라이브러리 추출]


원하는 인터페이스의 idl을 midl 컴파일러를 통해 형식 라이브러리(tlb)를 얻어내어
이를 #import 하여 쉽게 COM을 억세스 하도록 합니다.


이해를 돕기 위하여 Microsoft의 MSN Messenger COM Library인 Messenger 인터페이스를
가져다 쓰는 예를 들며 설명합니다.


 


먼저, 원하는 인터페이스의 idl을 얻어내야 합니다. idl 파일이 직접 있다면 이를
이용하고, 없다면 VC++의 툴인 OLE/COM Object Viewer를 연 후, 원하는 COM 오브젝을
찾아내야 합니다.


 


Type Libraries 노드 밑에 Messenger API Type Library를 선택한 후 오른클릭
하여 View를 선택합니다.


( MSN 5.0 이하에서는 All Object 노드에서 Messenger를 찾을 수 있습니다. )



 


이렇게 하면 Messenger API 형식 라이브러리의 idl 을 볼 수 있습니다. 이제 이
idl을 메뉴에서 File/ Save As로 msnmsgr.idl로 저장 한 후 ( 이름은 마음대로 정하셔도
됩니다 ) 우리가 이 인터페이스를 사용하는데 도움을 주는 형식 라이브러리 (.tlb)로
만들어 봅시다.


 


아래는 idl에서 tlb 형식라이브러리를 만드는 예입니다.


D:\>midl
/mktyplib203 msnmsgr.idl


 


MSN 5.0 이하에서는 문제없이 컴파일이 되었는데 MSN 6.0에서는 아래와 같은 결과를
출력합니다.


 


D:\>midl
/mktyplib203 msnmsgr.idl


Microsoft
(R) MIDL Compiler Version 5.01.0164


Copyright
(c) Microsoft Corp 1991-1997. All rights reserved.


Processing
.\msnmsgr.idl


msnmsgr.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\oaidl.idl


oaidl.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\objidl.idl


objidl.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\unknwn.idl


unknwn.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\wtypes.idl


wtypes.idl


.\msnmsgr.idl(76)
: error MIDL2025 : syntax error : expecting a type specificati


on
near "MOPTIONPAGE"


.\msnmsgr.idl(76)
: error MIDL2026 : cannot recover from earlier syntax errors;


aborting
compilation


 


이 idl을 노트패드에서 열어보면 MOPTIONPAGE 의 정의가 아래 쪽에 있는 것을
확인 하 실 수 있습니다. 따라서 인자로 쓰이는 구조체의 정의 부를 해당 인터페이스
정의 부 위로 옮겨야 합니다. ( 예전 버전의 MSN 인터페이스는 그러지 않았는데 새로
인자 형식이 많이 생긴 듯합니다 )


 


그런 식으로 에러 나지 않게 정의부를 위로 옮기시면 아래와 같이 컴파일이 잘
됩니다. 그리고 그 결과로 msnmsgr.tlb 파일이 생기게 됩니다.


 


D:\bro\mine\gosunet\using_tlb>midl
/mktyplib203 msnmsgr.idl


Microsoft
(R) MIDL Compiler Version 5.01.0164


Copyright
(c) Microsoft Corp 1991-1997. All rights reserved.


Processing
.\msnmsgr.idl


msnmsgr.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\oaidl.idl


oaidl.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\objidl.idl


objidl.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\unknwn.idl


unknwn.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\wtypes.idl


wtypes.idl


 


이제 우리가 원하는 형식 라이브러리를 얻게 되었습니다. 이를 이용하여 COM 프로그래밍을
쉽게 할 수 있습니다.


 


간단하게 이 형식 라이브러리를 이용해서 MSN 자동 로그인을 하도록 하는 기능을
만들어 봅니다.


 


Visual C++을 실행 한 후 다이얼로그 기반의 프로젝트를 생성합니다.


그리고 방금 만든 msnmsgr.tlb를 해당 프로젝트 폴더로 복사합니다.


stdafx.h 파일에 다음을 추가합니다.


 


#import
"msnmsgr.tlb" named_guids, no_namespace


 


버튼 클릭 시 아래와 같은 코드가 실행되게 합니다.


 


    AfxOleInit();
// COM 초기화는 App::InitInstance() 에 두길 추천합니다.


 


    IMessengerPtr
msn;


    msn.CreateInstance(
__uuidof(Messenger), NULL, CLSCTX_LOCAL_SERVER );       


 


    msn->AutoSignin();


 


버튼 클릭으로 위의 코드가 실행되면 msn 메신저가 로그 오프일 때 자동 로그인이
실행되게 됩니다.


 


만일 형식라이브러리를 Import하여 스마트 포인터를 사용하지 않는다면


 


HRESULT
get_MyContacts(


    IDispatch
** ppMContacts


);


 


를 사용하려 한다면 raw한 COM 프로그래밍으로 하려면



  IMessengerGroups* Groups;

  IMessenger2*
msg;


     :


  IDispatch*
lpGroups;


  msg->get_MyGroups(
&lpGroups );


  Groups
= lpGroups;


  lpGroups->Release();


 


이렇게 해야 합니다만, 형식라이브러리 Import 후 스마트 포인터를 사용하면 아래와
같이 간결하고 안전한 코드를 짤 수 있습니다.


 


  IMessengerGroupsPtr
Groups;


  IMessenger2Ptr
msg;


  Groups
= msg->MyGroups;


 


이는 형식라이브러리를 import 할때 인터페이스의 프로퍼티를 자동으로 추가해주어서
가능한것으로 VB에서 프로퍼티 접근 하듯이 무척 간단하고 보기 좋은 프로그래밍을
할 수 있게 됩니다.


 


참고로, 만일 형식라이브러리를 사용하지 않고 직접 스마트 포인터를 사용하게
된다면


 


#include
"msgruaid.h"


#include
"msgrua.h"


#pragma
comment(lib,"msgrguid.lib")


 


_COM_SMARTPTR_TYPEDEF(IMessenger,
__uuidof(IMessenger));


_COM_SMARTPTR_TYPEDEF(IMessenger2,
__uuidof(IMessenger2)););


_COM_SMARTPTR_TYPEDEF(IMessenger3,
__uuidof(IMessenger3)););


 :


위처럼 사용하여 스마트 포인터를 쓸 수는 있지만 프로퍼티를 위에서 했듯이 불편한
방법으로 접근해야 할 것입니다.


 


 


[이벤트 받기]


 


 이벤트를 받는 코드를 raw하게 짜려 한다면 무척 불편하기도 합니다. 따라서
저는 ATL의 이벤트 받는 방식으로 접근합니다. 장점은 무엇보다 이벤트 메소드를
정의해서 쓰므로 가독성도 좋고 코드도 보기 좋다는 점입니다.


 


 만일 signin 말고 다른 이벤트를 받기 위해서 이벤트의 메소드 아이디를
알고 싶으시면 모든 인터페이스의 정의가 되어 있는 idl 파일에서 그 값을 보실 수
있습니다.


 


예를 들어 아래 0x400의 OnSign 이벤트는


BEGIN_SINK_MAP(CMessengerEvents)


   SINK_ENTRY_EX(0,
DIID_DMessengerEvents, 0x400, OnSigninx)


END_SINK_MAP()


 


idl에서 아래에서 찾아볼 수 있고 id는 0x400인것을 확인 하실 수 있습니다.


    [


      uuid(C9A6A6B6-9BC1-43A5-B06B-E58874EEBC96),


      helpstring("Messenger
Events"),


      hidden


    ]


    dispinterface
DMessengerEvents {


        properties:


        methods:


            [id(0x00000415),
helpstring("A new group has been created.")]


            void
OnGroupAdded(


                            [in]
long hr,


                            [in]
IDispatch* pMGroup);


                :


        [id(0x00000400),
helpstring("Signin attempt complete.")]


            void
OnSignin([in] long hr);


 


 또한 재미있는 팁은 msn에서 파일 보내기 메소드를 실행시 파일을 바로
보내지 못하게 msn 4.x 이후에는 보안상 막아두었습니다. ( msn 바이러스가 퍼지지
못하도록 하기 위해서 였겠죠 ) 제 프로젝트 성격상 파일 보내기가 자동으로 되어야
하기 때문에 작성된 저의 트릭을 보실 수 있을 것입니다. 98/2000/xp에서 잘 수행되었습니다.


  


 이벤트 받는 코드를 포함한 모든 저의 샘플 코드를 적어 놓도록 하겠습니다.


 


msnx.h


#if
!defined(AFX_MSNX_H__F7BD2C8E_8C37_4B4F_B106_CAB2E772167E__INCLUDED_)


#define
AFX_MSNX_H__F7BD2C8E_8C37_4B4F_B106_CAB2E772167E__INCLUDED_


 


#if
_MSC_VER > 1000


#pragma
once


#endif
// _MSC_VER > 1000


 


#include
<atlbase.h>


 


//You
may derive a class from CComModule and use it if you want to override


//something,
but do not change the name of _Module


class
CExeModule : public CComModule


{


public:


    CExeModule();


};


extern
CExeModule _Module;


 


#include
<atlcom.h>


#include
<atlwin.h>


 


 


#import
"msn6.tlb" named_guids, no_namespace


 


 


IMessengerPtr
GetMessenger();


 


class
CMessengerEvents :


    public
IDispEventImpl<0,CMessengerEvents,&DIID_DMessengerEvents,&LIBID_MessengerAPI,1,0>


{


public:


    IUnknownPtr     m_psinkObj;


    bool
Create( IUnknownPtr p )


    {


        m_psinkObj
= p;


        return
( DispEventAdvise(p,&DIID_DMessengerEvents) == S_OK);


    }


    void
Close()


    {


        if(
m_psinkObj )


        {


            DispEventUnadvise(m_psinkObj);


            m_psinkObj
= NULL;


        }


    }


 


    virtual
~CMessengerEvents() { Close(); }


 


    STDMETHOD
(OnSigninx) ( long hr )


    {


        return
S_OK;


    }


 


BEGIN_SINK_MAP(CMessengerEvents)


   SINK_ENTRY_EX(0,
DIID_DMessengerEvents, 0x400, OnSigninx)


END_SINK_MAP()


};


 


 


class
CMessengerAPI : public IMessengerPtr, public CMessengerEvents


{


public:


    


    virtual
~CMessengerAPI()


    {


        Close();


    }


    void
Close()


    {


        CMessengerEvents::Close();


    }


 


    bool
Create()


    {


        IMessengerPtr
msn = GetMessenger();


        if(
msn )


        {


            *(IMessengerPtr*)this
= msn;


            return
CMessengerEvents::Create( msn );


        }


        return
false;


    }


    


    CString
GetMyID();


    CString
GetMyNick();


 


    IMessengerWindowPtr
SendFile( LPCTSTR pszSigninName, LPCTSTR pszPath );


    IMessengerWindowPtr
InstantMessage( LPCTSTR pszSigninName );


 


    STDMETHOD
(OnSigninx)( long hr )


    {


        //
로그인 될때 이벤트가 떨어집니다.


        return
S_OK;


    }


};


 


#endif
// !defined(AFX_MSNX_H__F7BD2C8E_8C37_4B4F_B106_CAB2E772167E__INCLUDED_)


 


msnx.cpp


#include
"stdafx.h"


#include
"resource.h"


#include
"msnx.h"


 


#ifdef
_DEBUG


#undef
THIS_FILE


static
char THIS_FILE[]=__FILE__;


#define
new DEBUG_NEW


#endif


 


//////////////////////////////////////////////////////////////////////


//
Construction/Destruction


//////////////////////////////////////////////////////////////////////


CExeModule
_Module;


BEGIN_OBJECT_MAP(ObjectMap)


END_OBJECT_MAP()


 


CExeModule::CExeModule()


{


    Init(ObjectMap,
NULL, &LIBID_MessengerAPI);


}


 


IMessengerPtr
GetMessenger()


{


    IMessengerPtr
msn = NULL;


    try


    {


        msn.CreateInstance(
__uuidof(Messenger), NULL, CLSCTX_LOCAL_SERVER );       


    }


    //catch(_com_error
&e)


    catch(...)


    {


        //PrintComError(e);


        return
NULL;


    }


 


    return
msn;


}


 


IMessengerWindowPtr
CMessengerAPI::InstantMessage( LPCTSTR pszSigninName )


{


    IMessengerWindowPtr
window = NULL;


    try


    {   


        IMessengerPtr
msn = *this;


        IMessengerContactPtr
contact = (IMessengerContactPtr)msn->GetContact( (_bstr_t)pszSigninName,
(_bstr_t)msn->GetMyServiceId() );


        if(
contact )


            window
= msn->InstantMessage( _variant_t((IDispatch*)contact,true));


    }


    //catch(_com_error
&e)


    catch(...)


    {


        //PrintComError(e);


        return
NULL;


    }


    return
window;


}


 


CString
CMessengerAPI::GetMyID()


{


    CString
id;


    try


    {   


        IMessengerPtr
msn = *this;


        id
= (LPCTSTR)msn->MySigninName;


    }


    //catch(_com_error
&e)


    catch(...)


    {


        //PrintComError(e);


        return
_T("");


    }


    


    return
id;


}


 


CString
CMessengerAPI::GetMyNick()


{


    CString
nick;


    try


    {   


'KB > tutorial' 카테고리의 다른 글

How to i386 32bit OS Kernel Compile in VC6  (0) 2004.11.04
DDK in VC with SoftICE  (1) 2004.11.04
[winsock] 윈속 소켓 정리  (0) 2004.03.19
snmp 프로그래밍  (0) 2004.03.19
MSN 프로그래밍 하기  (0) 2004.03.19

+ Recent posts