이번엔 컨트롤에서 이벤트(Connection point)를 발생시키는 방법을 정리해 보겠다.
만약 이벤트를 지원할 필요가 없다면 이 과정은 건너뛰도록 하자.
만약 이벤트를 지원할 필요가 없다면 이 과정은 건너뛰도록 하자.
이벤트 구성은 "이론적으로는" 간단한데, 실제로는 Visual Studio의 버그나 예상치 못한 동작 등 인내심의 한계를 시험하게 되는 경우가 많다.
따라서, 꼭 필요한 경우가 아니라면 이벤트를 발생시키는 컨트롤의 작성은 피하시라고 권하고 싶다.
그러나! 세상만사 개발자 마음데로 되는건 별로 없으니까-_- 어떻게 하는지 정도는 알아두도록 하자.
따라서, 꼭 필요한 경우가 아니라면 이벤트를 발생시키는 컨트롤의 작성은 피하시라고 권하고 싶다.
그러나! 세상만사 개발자 마음데로 되는건 별로 없으니까-_- 어떻게 하는지 정도는 알아두도록 하자.
이벤트 구성
단순함을 위해 "사용자가 컨트롤을 클릭하면 이벤트가 발생하는" 정도의 이벤트만 구성 하겠다.
추가로, 이벤트 정보 전달 메커니즘의 이해를 위해 마우스 커서의 좌표를 전달하는데 까지 해보자.
추가로, 이벤트 정보 전달 메커니즘의 이해를 위해 마우스 커서의 좌표를 전달하는데 까지 해보자.
이 연제물에서 제시한 방향대로 잘 따라오고 있다면, GreenmaruX.HelloCtrl은 Connection point를 구현할 준비가 이미 끝난 상태다.
Connection point로 구현된 인터페이스인 _IHelloCtrlEvents에서 Add Method를 실행한다.
반환형은 void로 선택하고, 적절한 이벤트의 이름을 입력해 준다. 일반적으로 On + 동사형을 사용하는게 컨트롤 사용자 입장에서 자연스럽다.
반환형은 void로 선택하고, 적절한 이벤트의 이름을 입력해 준다. 일반적으로 On + 동사형을 사용하는게 컨트롤 사용자 입장에서 자연스럽다.
예제에서는 마우스가 클릭되었을때 이벤트를 발생 시킬 것이므로, OnMouseClick이라는 이름을 붙여줬다.
마우스의 좌표를 전달하기 위해 X, Y좌표값으로 사용할 정수형 매개변수 2개를 추가했다.
마우스의 좌표를 전달하기 위해 X, Y좌표값으로 사용할 정수형 매개변수 2개를 추가했다.
한줄요약: 이벤트의 구현은 void형 함수를 사용하고, 이벤트 처리기(Handler)에서 필요한 정보를 [in]매개변수로 넘긴다는 것만 기억하시면 되겠다.
이 과정을 거치면 _IHelloCtrlEvents에 이벤트가 하나 추가되는 것을 볼 수 있다.
Visual Studio 2010을 사용하는 경우, 생성되는 이벤트(GreenmaruX_i.h에 생성됨)를 조금 주의깊게 살펴보면 #if defined(__cplusplus) && !defined(CINTERFACE)조건에 의해 interface _IHelloCtrlEvents가 실제로 사용되지 않는다는걸 알 수 있다. 그렇지만 일단은 패스~ -_-)//
Connection Point 구현
그 다음, Connection Point를 구현해야 한다. Connection Point는 이름 그대로, 컨트롤 호스트가 이벤트와 접촉할 수 있는 지점 - 이벤트 처리기(handler) 호출 지점 - 을 의미한다.
Class view에서 CHelloCtrl을 오른쪽 클릭하고 Add - Add Connection Point를 선택한다.
Class view에서 CHelloCtrl을 오른쪽 클릭하고 Add - Add Connection Point를 선택한다.
Connection Point 마법사에서 HelloCtrl의 이벤트를 구현하도록 선택한다.
이렇게 해 두면 CProxy_IHelloCtrlEvents<T> 클래스에 Fire_OnMouseClicked(LONG x, LONG y) 함수가 자동으로 구현된다.
필요한 이벤트가 더 있다면, 이 과정을 반복한다.
이벤트 발생 시키기
우리가 작성할 이벤트는, 사용자의 클릭에 반응하는 것이다.
당연한 소리지만, 우선 컨트롤이 클릭 되었을 때, 컨트롤이 이 이벤트를 먼저 알아차려야 한다.
WM_LBUTTONDOWN이벤트를 받을 수 있도록 핸들러를 추가해 보자. CHelloCtrl에 다음 메시지 처리기를 추가해 준다.
당연한 소리지만, 우선 컨트롤이 클릭 되었을 때, 컨트롤이 이 이벤트를 먼저 알아차려야 한다.
WM_LBUTTONDOWN이벤트를 받을 수 있도록 핸들러를 추가해 보자. CHelloCtrl에 다음 메시지 처리기를 추가해 준다.
1
2
3
4
5
| BEGIN_MSG_MAP(CHelloCtrl) MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLeftButtonDown) CHAIN_MSG_MAP(CComControl <chelloctrl>) DEFAULT_REFLECTION_HANDLER() END_MSG_MAP() |
ActiveX에서 메시지의 처리는, C++을 통해 Windows Message를 사용하는 일반적인 방법과 크게 다르지 않다.
OnLeftButtonDown 함수를 메시지 처리기로 등록하고, 이 함수에서 이벤트를 발생시키기 위한 Fire_OnMouseClick를 호출해 주면 된다.
1
2
3
4
5
| LRESULT OnLeftButtonDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & bHandled) { Fire_OnMouseClick(LOWORD(lParam), HIWORD(lParam)); return 0; } |
아시다 시피, WM_LBUTTONDOWN메시지는 lParam을 통해 마우스 커서의 좌표를 통지해 준다.
예제의 단순함을 위해 GetCursorPos나 ScreenToClient등의 부가적인 처리는 건너 뛰었다.
예제의 단순함을 위해 GetCursorPos나 ScreenToClient등의 부가적인 처리는 건너 뛰었다.
예제에서 중요하게 봐야 할 부분은, Fire_OnMouseClick 함수를 호출하여 이벤트를 발생 시켰다는 것이다.
즉, 이벤트를 발생시킬 필요가 있다면 Proxy클래스에 자동으로 구현되는 Fire_이벤트 함수를 호출해 주기만 하면 된다. 이보다 간단할 수는 없다!
즉, 이벤트를 발생시킬 필요가 있다면 Proxy클래스에 자동으로 구현되는 Fire_이벤트 함수를 호출해 주기만 하면 된다. 이보다 간단할 수는 없다!
참고로, Fire_OnMouseClick함수의 내부 구현을 살펴보면, Connection point에 연결된 함수(처리기)를 호출해 주는 내용으로 구성되어 있다.
이벤트 처리기(Handler) 작성
이제, Javascript에서 이벤트를 받아서 처리하기 위한 Javascript 함수를 작성해 보자.
약간 특이한 형태의 Javascript선언이 필요하다. (표준 html은 아니지만, 어차피 ActiveX도 표준이 아니니까)
1
2
3
| <script type= "text/javascript" for = "HelloCtrl" event= "OnMouseClick(x,y)" > alert( "HelloCtrl MouseClick event fired! : (" + x + ":" + y + ")" ); </script> |
script tag에 추가로 다음 두가지 속성을 삽입해 줘야 한다.
- for: 컨트롤의 ID를 지정해 준다.
- event: 처리해야 할 이벤트를 지정해 준다. 매개변수도 기술해야 한다. Javascript에는 별도의 자료형이 없다는 걸 기억하자. 여기서 선언된 매개변수 명이 아래 script 블록에서 사용될 변수 명이다. (당연한 소리지만, 실제 컨트롤 안에서 정의한 변수명과 같을 필요는 없다.)
이제 컨트롤을 Build하고 실행시킨 다음 아무 곳이나 클릭해 보자.
만약 한번에 Javascript alert대화상자를 볼 수 있다면 당신은 무척 운이 좋거나, Visual Studio 2010을 사용하지 않았거나....뭔가 예제를 잘못 따라했을 가능성이 높다 -_-;
만약 한번에 Javascript alert대화상자를 볼 수 있다면 당신은 무척 운이 좋거나, Visual Studio 2010을 사용하지 않았거나....뭔가 예제를 잘못 따라했을 가능성이 높다 -_-;
아무리 클릭해도 우리의 컨트롤은 묵묵부답일 것이다. 뭐가 잘못 된걸까?-_-
이곳저곳 찔러보고, 컴파일도 다시 해보고, 프로젝트도 다시 로드해 보고.. 하다보면, _IHelloCtrlEvents에 정의되어 있던 OnMouseClick함수가 사라지는 경험도 하게 될 것이다. 나도 환장-_-할 뻔 했다.
이곳저곳 찔러보고, 컴파일도 다시 해보고, 프로젝트도 다시 로드해 보고.. 하다보면, _IHelloCtrlEvents에 정의되어 있던 OnMouseClick함수가 사라지는 경험도 하게 될 것이다. 나도 환장-_-할 뻔 했다.
정답은, 위에서 "#if defined(__cplusplus) && !defined(CINTERFACE)조건에 의해 interface _IHelloCtrlEvents가 실제로 사용되지 않는다" 라고 말 한데에서 찾을 수 있다.
자세한 설명은 생략하고, 문제 해결 방법을 설명 하겠다.
GreenmaruX.idl파일을 열어보면 _IHelloCtrlEvents의 정의를 찾을 수 있다. 여기에다가 수동으로 우리의 이벤트를 추가해 주자. (_IHelloCtrlEvents에 추가된 함수 정의를 잘라내기해서 붙여넣으면 간단하다.)
GreenmaruX.idl파일을 열어보면 _IHelloCtrlEvents의 정의를 찾을 수 있다. 여기에다가 수동으로 우리의 이벤트를 추가해 주자. (_IHelloCtrlEvents에 추가된 함수 정의를 잘라내기해서 붙여넣으면 간단하다.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| library GreenmaruXLib { importlib( "stdole2.tlb" ); [ uuid (128FE3CF-CCF5-4F99-B859-418331AB07A2) ] dispinterface _IHelloCtrlEvents { properties: methods: [id(1)] void OnMouseClick([in] LONG x, [in] LONG y); }; [ uuid (4C80B2EE-6D19-4F77-9946-DF7AD17EEE67), control ] coclass HelloCtrl { [ default ] interface IHelloCtrl; [ default , source] dispinterface _IHelloCtrlEvents; }; }; |
왜 이런 현상이 발생 했는지는 IDL, COM Interface, Connection Point에 대한 보다 자세한 이해가 필요하다.
각각에 대해 너무 궁금해서 잠도 못잘 것 같으신 분은, Goooooogle신을 영접하시거나 댓글 남겨 주시기 바란다.
각각에 대해 너무 궁금해서 잠도 못잘 것 같으신 분은, Goooooogle신을 영접하시거나 댓글 남겨 주시기 바란다.
이제 컨트롤을 실행해 보면, 예상된 결과 - 이벤트 발생과 처리 - 를 얻을 수 있을 것이다.
댓글 없음:
댓글 쓰기