이 블로그는 http://blog.greenmaru.com으로 이동 하였습니다.
2010년 12월 이후 업데이트는 새로운 블로그를 통해서만 이루어 집니다. 현재 보고 계신 페이지는 2011년 12월에 없어질 예정 이므로, 필요하신 분께서는 새로운 블로그로 링크를 변경해 주시기 바랍니다.
번거롭게 해 드려 죄송합니다.
<greenb>

ATL ActiveX 만들기 - Part3. 이벤트(Connection point) 구현

요즘은 확실히 ActiveX 사용이 눈에 띄게 줄어들긴 했지만, 아직도 정신 못차리고(-_-) ActiveX를 요구하는 사람들도 가끔 있다. 아마 앞으로도 한동안은 ActiveX가 필요한 경우가 간혹 있을지도 모르니까, 방법을 다 잊어버리기 전에 요점만 정리해 두자.

 

 

이 포스트 에서는 이벤트(Connection point)를 발생시키는 컨트롤을 작성해 보겠다.
만약 이벤트를 지원할 필요가 없다면 이 과정은 건너뛰도록 하자.

이벤트를 구성하는 것 자체는 "이론적으로는" 간단한데, 실제로는 Visual Studio의 버그나 예상치 못한 동작 등등 인내심의 한계를 시험하는 경우가 많다.
따라서, 꼭 필요한 경우가 아니라면 이벤트를 발생시키는 컨트롤의 작성은 피하시라고 권하고 싶다.
그러나~!! 세상만사 개발자 마음데로 되는건 별로 없으니까-_- 어떻게 하는지 정도는 알아두는게 좋겠다.

 

이벤트 구성

작성할 이벤트는, 단순함을 위해 "사용자가 컨트롤을 클릭하면 이벤트가 발생하는" 정도 까지만 구성 하겠다.
추가로, 마우스 커서의 좌표를 이벤트 정보로 전달하도록 하겠다.

이 연제물에서 제시한 방향대로 잘 따라오고 있다면, GreenmaruX.HelloCtrl은 Connection point를 구현할 준비가 이미 끝난 상태다.

Connection point로 구현된 인터페이스인 _IHelloCtrlEvents에서 Add Method를 실행한다.
반환형은 void로 선택하고, 적절한 이벤트의 이름을 입력해 준다. 일반적으로 On + 동사형을 사용하는게 컨트롤 사용자 입장에서 자연스럽다.

예제에서는 마우스가 클릭되었을때 이벤트를 발생 시킬 것이므로, OnMouseClick이라는 이름을 붙여줬다.
마우스의 좌표를 전달하기 위해 X, Y좌표값으로 사용할 정수형 매개변수 2개를 추가했다.

한줄요약: 이벤트의 구현은 void형 함수를 사용하고, 이벤트 처리기(Handler)에서 필요한 정보를 [in]매개변수로 넘긴다는 것만 기억하시면 되겠다.

이 과정을 거치면 _IHelloCtrlEvents에 이벤트가 하나 추가되는 것을 볼 수 있다.

Visual Studio2010을 사용하는 경우, 생성되는 이벤트(GreenmaruX_i.h에 생성됨)를 조금 주의깊게 살펴보면 #if defined(__cplusplus) && !defined(CINTERFACE)조건에 의해 interface _IHelloCtrlEvents가 실제로 사용되지 않는다는걸 알 수 있다. 그렇지만 일단은 Pass~

 

Connection Point 구현

그 다음, Connection Point를 구현해야 한다.
Class view에서 CHelloCtrl을 오른쪽 클릭하고 Add - Add Connection Point를 선택한다.

 Connection Point 마법사에서 HelloCtrl의 이벤트를 구현하도록 선택한다.

이렇게 해 두면 CProxy_IHelloCtrlEvents<T> 클래스에 Fire_OnMouseClicked(LONG x, LONG y) 함수가 자동으로 구현되게 된다.

필요한 이벤트를 추가로 더 구현해야 한다면, 이 과정을 반복해 주면 된다.

 

이벤트 발생 시키기

우선 사용자가 컨트롤을 클릭했을 때의 이벤트를 컨트롤이 먼저 알아차려줘야 한다.
WM_LBUTTONDOWN이벤트를 받을 수 있도록 핸들러를 추가해 보자. CHelloCtrl에 다음 메시지 처리기를 추가해 준다.

  1. BEGIN_MSG_MAP(CHelloCtrl)   
  2.     MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLeftButtonDown)   
  3.     CHAIN_MSG_MAP(CComControl<CHelloCtrl>)   
  4.     DEFAULT_REFLECTION_HANDLER()   
  5. END_MSG_MAP()  

ActiveX에서 메시지의 처리는,  C++을 통해 Windows Message를 사용하는 일반적인 방법과 크게 다르지 않다.

OnLeftButtonDown 함수를 메시지 처리기로 등록하고, 이 함수에서 이벤트를 발생시키기 위한 Fire_OnMouseClick를 호출해 주면 된다.

아시다 시피, WM_LBUTTONDOWN메시지는 lParam을 통해 마우스 커서의 좌표를 통지해 준다.
예제의 단순함을 위해 GetCursorPos나 ScreenToClient등의 부가적인 처리는 건너 뛰었다.

  1. LRESULT OnLeftButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)   
  2. {   
  3.     Fire_OnMouseClick(LOWORD(lParam), HIWORD(lParam));   
  4.   
  5.     return 0;   
  6. }  

예제에서 중요하게 봐야 할 부분은, Fire_OnMouseClick 함수를 호출하여 이벤트를 발생 시켰다는 것이다.
즉, 이벤트를 발생시킬 필요가 있다면 Proxy클래스에 자동으로 구현되는 Fire_이벤트 함수를 호출해 주기만 하면 된다. 이보다 간단할 수는 없다!

참고로, Fire_OnMouseClick함수의 내부 구현을 살펴보면, Connection point에 연결된 함수(처리기)를 호출해 주는 내용으로 구성되어 있다.

 

이벤트 처리기(Handler) 작성

이제, Javascript에서 이벤트를 받아서 처리하기 위한 Javascript 함수를 작성해 보자.

약간 특이한 형태의 Javascript선언이 필요하다. (표준 html은 아니지만, 어차피 ActiveX도 표준이 아니니까)

  1. <script type="text/javascript" for="HelloCtrl" event="OnMouseClick(x,y)">   
  2. alert("HelloCtrl MouseClick event fired! : (" + x + ":" + y + ")");   
  3. </script>  

script tag에 추가로 다음 두가지 속성을 삽입해 줘야 한다.

  • for: 컨트롤의 ID를 지정해 준다.
  • event: 처리해야 할 이벤트를 지정해 준다. 매개변수도 기술해야 한다. Javascript에는 별도의 자료형이 없다는 걸 기억하자. 여기에서 사용된 매개변수 명이 아래 script 블록에서 사용될 변수 명이다. (당연한 소리지만, 실제 컨트롤에서 정의한 변수명과 같을 필요는 없다.)

 

이제 컨트롤을 Build하고 실행시킨 다음 아무 곳이나 클릭해 보자.
만약 한번에 Javascript alert대화상자를 볼 수 있다면 당신은 무척 운이 좋거나, Visual Studio 2010을 사용하지 않았거나....뭔가 예제를 잘못 따라했을 가능성이 높다 -_-;

아무리 클릭해도 우리의 컨트롤은 묵묵부답일 것이다. 뭐가 잘못 된걸까?-_-
이곳저곳 찔러보고, 컴파일도 다시 해보고, 프로젝트도 다시 로드해 보고.. 하다보면, _IHelloCtrlEvents에 정의되어 있던 OnMouseClick함수가 사라지는 경험도 하게 될 것이다. 나도 환장-_-할 뻔 했다.

정답은, 위의 "#if defined(__cplusplus) && !defined(CINTERFACE)조건에 의해 interface _IHelloCtrlEvents가 실제로 사용되지 않는다" 라고 말 한데에서 찾을 수 있다.

자세한 설명은 생략하고, 문제 해결 방법을 설명 하겠다.
GreenmaruX.idl파일을 열어보면 _IHelloCtrlEvents의 정의를 찾을 수 있다. 여기에다가 수동으로 우리의 이벤트를 추가해 주자. (_IHelloCtrlEvents에 추가된 함수 정의를 잘라내기해서 붙여넣으면 간단하다.)

  1. library GreenmaruXLib   
  2. {   
  3.     importlib("stdole2.tlb");   
  4.     [   
  5.         uuid(128FE3CF-CCF5-4F99-B859-418331AB07A2)         
  6.     ]   
  7.     dispinterface _IHelloCtrlEvents   
  8.     {   
  9.         properties:   
  10.         methods:   
  11.             [id(1)] void OnMouseClick([in] LONG x, [in] LONG y);   
  12.     };   
  13.     [   
  14.         uuid(4C80B2EE-6D19-4F77-9946-DF7AD17EEE67),        
  15.         control   
  16.     ]   
  17.     coclass HelloCtrl   
  18.     {   
  19.         [default] interface IHelloCtrl;   
  20.         [default, source] dispinterface _IHelloCtrlEvents;   
  21.     };   
  22. };  

왜 이런 현상이 발생 했는지는 IDL, COM Interface, Connection Point에 대한 보다 자세한 이해가 필요하다.
각각에 대해 궁금하신 분은 Goooooogle신을 영접해 보시기 바란다.

이제 컨트롤을 실행해 보면, 예상된 결과 - 이벤트 발생과 처리 - 를 얻을 수 있을 것이다.

 

소스코드 다운로드

소스코드를 다운로드 하게 되면, Build한 다음 프로젝트를 닫았다가 다시 로드해서 보시기 바란다.

다음 글에서는, Windows Vista이상에서 사용되는 컨트롤을 위한 권한 상승(Elevation)을 구현해 보겠다.

저작자 표시 비영리 변경 금지

Trackback Address >> http://greenmaru.com/trackback/94 관련글 쓰기

Comment List

  1. BlogIcon 오자서 2010/07/06 14:43 address / modify or delete / reply

    음....^^;
    좋은글이긴 줗은글인것 같은데....끄응

  2. BlogIcon 별바람 2010/07/08 09:11 address / modify or delete / reply

    이런걸 올리시면 방문자들이 머리가 아파집니다..분위기 전환이 필요합니다..그런 의미에서 메이드복 입으신 마키짱님의 사진을 기대해보겠...쿨럭...왠지 매우매우 귀여우실듯 합니다..

  3. Ceik 2010/07/28 23:05 address / modify or delete / reply

    감사합니다.. 덕분에 정말 쉽게
    (혼자 책보면서 며칠동안 삽질했었습니다.. ㅠ_ㅠ)
    AX 컨트롤을 맛볼 수 있었습니다.

    한 번 하고 나니깐 이제 좀 감이 잡힙니다. ^0^)/

  4. BlogIcon 2012/01/11 08:18 address / modify or delete / reply

    떡 본 김에 제사 지낸다

  5. BlogIcon 원숭이 2012/01/12 18:08 address / modify or delete / reply

    가는말이 고와야 오는말이 곱다

|  1  | ...  8  |  9  |  10  |  11  |  12  |  13  |  14  |  15  |  16  | ...  104  |