오전 7:41 99-12-18
오후 6:33 00-02-04 Added. ( 이벤트 부분, 적용사례 )
ATL 조금 하기 조경민 ( bro@shinbiro.com )
==========================================================

ATL COM Object 만들기
----------------------------------
New/ ATL COM Project로 연다. TestATL이라는 프로젝트명으로한다.

그냥 InProcess DLL로 할 것이므로 dll을 선택한다.

( - Excutable EXE : Out of Process COM 구현시
   - Service EXE   : NT에서 쓰는 서비스 처럼 NT start up시
                     백그라운드로 돌 COM 구현시 )

   - MFC Support : CString 같은 MFC 쓰고 싶으면 체크한다.

Finish를 하여 프로젝트를 만든다.

내가 구상하는 Object에 대한 Interface 추가
- 워크스페이스에서 클래스뷰로 가서 프로젝트에 오른클릭을
하여 New ATL Object을 한다.
그리고 Simple ATL Object을 선택하고 Simple이라는 이름으로 하고
속성탭은 Both, Dual, Agrregate No , Connection Point Yes
를 한다.

Dual - 생성시 idl에 현 인터페이스가 상속될 것은 IDispatch로
        VTable이 생김으로 인터페이스의 늦은 바인딩의 이점이 있으나
        IDispatch::Invoke를 해야 함으로 많이 느리다.
Custum - IUnknown으로 직접 상속을 받게 되는 인터페이스로
          빠른 코드를 만들고 싶을때 쓰면 된다.

Agrregation : COM 통합을 할 것인가를 결정한다.

Connection Point : 이벤트 Fire 기능을 갖을 것인가를 결정한다.

=> 생성되는 인터페이스는 Simple 클래스로 idl에는
interface ISimple : IDispatch로 되어 있을것이다.

현재 클래스뷰 구조

[-] TestSimple classes
   [-] -O _ISimpleEvents      : ISimple의 이벤트 인터페이스
   [-]    CSimple             : ISimple의 구현 클래스 ( 프록시클래스)
       + -O ISimple           : ISimple 인터페이스
       +    CSimple()         : CSimple의 생성자 메소드
   [+] CTestSimpleApp         : App
   [-] ISimple                : ISimple 인터페이스(위와 같은 놈)
   [+] Globals

Add Method/Property to Interface
----------------------------------
클래스뷰의 ISimple에서 오른클릭후 Add Method나 Add Property
를 함으로써 인터페이스에 대한 메소드, 프로퍼티를 줄수 있다.

메소드 구현하기 만일 Method라는 것을 만들고 싶다면
ISimple에서 오른 클릭후 Add Method후
리턴값 : HRESULT ( 바꿀수 없다 )
함수명 : Method
파라미터 : [in] long val

한후 CSimple안의 ISimple에서 Method를 두번 클릭하면 실제 구현하는
메소드를 볼 수 있다.
즉 CSimple이 ISimple의 Method를 구현해 주고 있는 것이다.

STDMETHODIMP CSimple::Method(long val)
{
        // 아래 코드는 MFC 서포트로 처음에 세팅했다면 들어가는 코드이다
        AFX_MANAGE_STATE(AfxGetStaticModuleState())

        // TODO: Add your implementation code here

        return S_OK;
}


Event Fire 기능 주기
--------------------
이미 생성시 Connection Point를 체크했기 때문에 idl에는
다음과 같은 것이 더 추가된다.
dispinterface _ISimpleEvents
실제로 이를 구현하는 클래스를 만들려면
그냥 CSimple에서 오른클릭하면 Implements Connnectiion Point이라는
메뉴가 나오지 않는다. 일딴 컴파일을 해서 성공을 한 후
CSimple에서 오른클릭후 Implements Connnectiion Point를 한후
원하는 이벤트 인터페이스를 선택한후 OK를 누르면 ( _ISimpleEvents )
CProxy_ISimpleEvents라는 클래스가 생겨 구현하는 클래스가 생긴다.
만일 Fire_Clikin( long x )를 구현 하고 싶다면

먼저 ISimpleEvents에서 오른클릭후 Add Method를
void Clikin( long x )를 만들어준다.
리턴값 : void
함수명 : Clickin
파라미터 : [in] long x

그러나 CProxy_ISimpleEvents는 바뀌지 않았따.
( 이말은 이벤트 메세드를 _ISimpleEvents에 생성을 했을때 각 메소드
들이 CProxy_ISimpleEvents에서도 똑같이 보여야만 한다는 말이다 )
-_- 웃긴것은 다시 컴파일을 한 후 ( 계속 그렇게 해야 한다 )
ISimple 오른클릭후 Implements Connnection Point
를 해주어야지 반영된다는 것이다.
반영되면 그것으로 CProxy_ISimpleEvents::Fire_Clikin(long x)
가 생기게 된다. 이 때 메소드의 내용이 이미 구현이 되어진다.
( 이 부분이 바로 실제 인터페이스의 내용을 구현하는 것이다 )

실제 Fire를 하기

CSimple의 코드내에서

Fire_Clickin(1);
이렇게 쓰게 되면 그때 이벤트가 생기게 된다.

-----------------------------------------------------------------
이벤트를 캐취하자. ( in VB )

' 일딴 아래처럼 이벤트를 갖은 타입 라이브러리의 인터페이스를
' 설정한다.
Private WithEvents obj As TESTSIMPLELib.Simple
'       ~~~~~~~~~~
Private Sub Command1_Click()
    Set obj = New TESTCOMMLib.Simple ' 실 객체를 할당한다.
    obj.Method (1)   ' 메소드를 호출한다.    
End Sub

' 이벤트를 캐취하는 이벤트 메서드 정의
Private Sub obj_Event(ByVal val As Long)
    MsgBox "이벤트가 뜹니다."
    Set obj = Nothing    ' 이제는 더이상 이벤트를 받지 않는다.
                         ' 또한 obj에 할당된 객체도 해체된다.
End Sub

이렇게 캐취해 낼수 있다.

다음은 MSDN에서 발췌한 WithEvents에 대한 제한 사항이다.

WithEvents 변수의 제한 사항
다음 제한 사항에 유의하여 WithEvents 변수를 사용해야 합니다.

WithEvents 변수는 포괄적 개체 변수가 될 수 없습니다. 즉, As Object로
선언할 수 없고 변수를 선언할 때 클래스 이름을 지정해야 합니다.

WithEvents 변수를 As New로 선언할 수 없습니다. 이벤트 원본 개체는
명시적으로 작성되고 WithEvents 변수에 할당되어야 합니다.

표준 모듈에서 WithEvents 변수를 선언할 수 없습니다. 클래스 모듈,
폼 모듈, 클래스를 정의하는 기타 모듈 등에서만 선언할 수 있습니다.

WithEvents 변수의 배열을 작성할 수 없습니다.

----------------------------------------------------------------------
ATL COM 이벤트는 VC에서 어떻게 다루어야 하는지 아직 모른다.
VC에서 ATL COM 객체를 가져다가 쓰기

// You need to point this header file to the directory
// you placed the Simple_ATL project
#include "..\Simple_ATL\Simple_ATL.h"
#include <iostream.h>

// Copy the following from the Simple_ATL_i.c file
// from the Simple_ATL project directory

const IID IID_IFirst_ATL =
{0xC8F6E230,0x2672,0x11D3,{0xA8,0xA8,0x00,0x10,0x5A,0xA9,0x43,0xDF}};
const CLSID CLSID_First_ATL =
{0x970599E0,0x2673,0x11D3,{0xA8,0xA8,0x00,0x10,0x5A,0xA9,0x43,0xDF}};
void main(void)
{
        // Declare and HRESULT and a pointer to the Simple_ATL interface
        HRESULT                        hr;
        IFirst_ATL                *IFirstATL;
        // Now we will intilize COM
        hr = CoInitialize(0);
        // Use the SUCCEEDED macro and see if we can get a pointer
        // to the interface
        if(SUCCEEDED(hr))
        {
                hr = CoCreateInstance( CLSID_First_ATL, NULL,
                                      CLSCTX_INPROC_SERVER,
                                      IID_IFirst_ATL, (void**) &IFirstATL);                
                // If we succeeded then call the AddNumbers method, if it failed
                // then display an appropriate message to the user.
                if(SUCCEEDED(hr))
                {
                        long ReturnValue;
                        hr = IFirstATL->AddNumbers(5, 7, &ReturnValue);
                        cout << "The answer for 5 + 7 is: " << ReturnValue << endl;
                        hr = IFirstATL->Release();                  
                }
                else
                {
                        cout << "CoCreateInstance Failed." << endl;
                }
        }        
        // Uninitialize COM
        CoUninitialize();
}

쉽게 하기 ( IID나 CLSID 몰라도 하기 )
------------------------------------------------------------------------------
대신 실행하면 나오는 TestSimple.tlb파일( 형식 라이브러리)이 필요하다.
이 형식 라이브러리 파일을 자기 프로젝트 디렉토리에 놓기만 하고
StdAfx.h 파일에 다음처럼 기입한다.
#include "atlbase.h"
#import "TestComm.tlb" no_namespace named_guids

그러면 자동으로 ISimplePtr같은 포인터를 그냥 쓸수 있다. (IID도 자동으로 로드됨)
dlg.h에서 인터페이스 변수를 선언
ISimplePtr m_SimplePtr;

        HRESULT hr;
        hr = CoInitialize(0);
        if(SUCCEEDED(hr))
        {
        hr = ::CoCreateInstance(CLSID_Simple,
                NULL,
                CLSCTX_INPROC_SERVER,
                IID_ISimple,
                (LPVOID*)&m_SimplePtr);
        
        if(FAILED(hr))
        {
                TRACE("생성 불가\n");
                return FALSE;
        }
        }

를 한후

m_SimplePtr->Method(1);

로 사용 하면 된다.

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

Global Hooking in Win32  (0) 2004.03.19
익스체인지2K 서버 까는 방법  (0) 2004.03.19
VB With AutoCAD R14  (1) 2004.03.19
[com] ATL COM 쓰기  (0) 2004.03.19
[mfc] Socket 프로그래밍  (4) 2004.03.19

+ Recent posts