ATL ActiveX 만들기 - Part2. 컨트롤 구현
요즘은 확실히 ActiveX 사용이 눈에 띄게 줄어들긴 했지만, 아직도 정신 못차리고(-_-) ActiveX를 요구하는 사람들도 가끔 있다. 아마 앞으로도 한동안은 ActiveX가 필요한 경우가 간혹 있을지도 모르니까, 방법을 다 잊어버리기 전에 요점만 정리해 두자.
초기 작업
ATL 컨트롤을 추가한 다음 프로젝트를 다시 로드해 보면 여러가지 클래스들이 자동으로 구현됨을 확인할 수 있다.
빌어먹을 COM 사양 구현을 일일이 손으로 안해도 되게 해준 Visual Studio 만세! 를 한번 외쳐주자.
이제, 필요하지만 자동으로는 추가되지 않는 항목들을 손으로 추가해 보자.
1. CString
예전에는, 순수 ATL프로젝트를 사용했을 때 가장 큰 문제중에 하나는 CString을 사용할 수 없다는 거였다.
단순히 CString만을 사용하기 위해서 MFC지원 컨트롤을 만드는 경우도 있었다. 그만큼 CString이 편리하기도 하고 CComBSTR과 BSTR만으로 문자열을 조작한다는 것이 지랄맞게 힘들기 때문이다.
그러나!! VS2005부터는 CString이 더이상 MFC만의 것이 아니다! 템플릿 기반으로 작성된 새로운 CString이 도입되었다. 이걸 사용하기 위해서는 stdafx.h에 <atlstr.h>해더파일을 추가해 주면 된다.
- #include <atlstr.h>
어플리케이션 전역에서 사용하기 위해 stdafx.h에다가 삽입한다.
2. Safe For Scripting
이상태에서 ActiveX를 그냥 동작시키면, "스크립트에 안전하지 않다"거나, "초기화 하겠냐" 등의 경고메시지가 출력된다. (Vista 이상에서 나오는 ActiveX를 실행 하겠냐는 경고가 아니다!)
이 문제를 해결하려면, 이 컨트롤은 스크립팅과 초기화에 안전하다는 사실을 컨테이너에게 알려줘야 한다. 여러가지 방법중에 가장 편한 건, 다음 매크로를 HelloCtrl.h에 추가해 주는 방법이다.
- BEGIN_CATEGORY_MAP(CHelloCtrl)
- IMPLEMENTED_CATEGORY (CATID_SafeForScripting)
- IMPLEMENTED_CATEGORY (CATID_SafeForInitializing)
- END_CATEGORY_MAP()
짐작할 수 있듯이, 스크립팅과 초기화에 안전하다는 표시다.
이 외에도 Category map에 추가로 넣을 수 있는 옵션이 몇가지 더 있지만 일반적으로는 사용되지 않는다.
디버거 설정 및 실행
이 상태에서 디버거(F5)를 실행해 보면, "결과물이 EXE가 아니라서 실행할 수 없다. 블라블라~" 하는 오류를 볼 수 있다.
일반적인 디버거로는 ActiveX를 테스트해 볼 수 없으므로, 웹 브라우저 디버거를 사용하자. (물론 IE만 가능하다)
솔루션 탐색기에서 프로젝트를 오른쪽 클릭하고 속성(Property)을 선택한다. Configuration Properties - Debugging에서 Debugger로 Web Browser Debugger를 선택해 준다.
디버거가 실행되면서 컨트롤을 불러올 수 있도록, 시작 URL은 $(ProjectDir)HelloCtrl.htm을 입력해 준다. $(ProjectDir)는 프로젝트 파일이 저장되어있는 로컬 경로를 의미하며, 이곳에는 만들려고 하는 컨트롤의 OBJECT태그가 포함된 컨트롤이름.htm파일이 자동으로 생성되어 있다.
이제 F5를 통해 실행해 보면, 웹 브라우저에서 HelloCtrl.htm이 불려지고, 컨트롤의 모습을 확인할 수 있게된다.
속성(Property) 추가
컨트롤에 속성을 추가해 보자. 이 예제가 Hello World인 만큼, 화면에 출력할 내용으로 Message라는 문자열형 속성을 추가해 보겠다.
클래스 뷰에서 IHelloCtrl을 오른쪽 클릭하고, Add - Add Property를 선택한다.
속성추가 마법사가 표시되면, 문자열형 속성 이므로 BSTR을 선택하고 속성 이름으로 Message를 입력하자.
특별한 경우가 아니라면, IDL속성 설정은 기본값을 그대로 사용하면 된다.
이 과정을 거치면, IDL에는 Message속성이 추가되고, 이 속성의 실제 구현이 CHelloCtrl에 자동으로 추가된다.
속성에 값을 설정하기 위한 put_Message(BSTR newVal)과 속성값을 읽기 위한 get_Message(BSTR* pVal)이 그것이다.
간단하게, 내부의 m_strMessage변수에 값을 읽고 쓰는 정도의 처리만 구현해 보자.
우선 HelloCtrl.h에 멤버변수를 추가한다.
- private:
- CString m_strMessage;
다음은 HelloCtrl.cpp에 속성 구현부를 추가한다.
단순히 m_strMessage를 이용해 값을 읽고 쓰기만 하는 정도로 구현해 보자.
- STDMETHODIMP CHelloCtrl::get_Message(BSTR* pVal)
- {
- m_strMessage.SetSysString(pVal);
- return S_OK;
- }
- STDMETHODIMP CHelloCtrl::put_Message(BSTR newVal)
- {
- m_strMessage = newVal;
- return S_OK;
- }
여기까지 했다면, 이제 속성값을 읽고 쓰여 지는지를 테스트 해 보자.
HelloCtrl.htm에 버튼을 하나 추가하고, 버튼이 클릭되면 Message에 값을 설정한 다음, 스크립트 경고창(alert)으로 그 내용을 보여주는 간단한 스크립트를 추가해 보자.
- <script type="text/javascript">
- function setAndShowMessage() {
- var ctrl = document.getElementById("HelloCtrl");
- ctrl.Message = "Hello GreenB!";
- alert(ctrl.Message);
- }
- </script>
- <object id="HelloCtrl" classid="CLSID:4C80B2EE-6D19-4F77-9946-DF7AD17EEE67"></object>
- <input type="button" onclick="setAndShowMessage()" value="Set Message" />
실행하면, 예상된 결과를 볼 수 있다.
Method 추가
마땅한 기능의 Method가 생각나지 않으니까, 단순하게 Message속성으로 지정된 값을 Windows Message box로 표시해 주는 ShowMessage()라는 쓸데없는-_- 함수를 만들어 보겠다. 이 함수는 사용자가 Message box에서 "예"를 클릭하면 TRUE를, "아니오"를 클릭하면 FALSE를 반환하도록 구성되어 있다.
클래스 뷰에서 IHelloCtrl을 오른쪽 클릭하고, Add - Add Method를 선택한다.
Method 추가 대화상자에서, Method이름과 매개변수를 추가해 준다.
주의할 것은, 모든 COM Method의 반환형은 HRESULT라야 한다. 이는 사용자 논리 단위에서 사용되는 것이 아니라, COM을 호스트와 클라이언트 간의 규약이다. 사용자 논리 레벨에서의 반환값을 사용하고 싶다면, retval로 지정된 포인터형 매개변수를 사용해야 한다.
여기서는, Message box의 제목을 위한 BSTR형의 bstrCaption 매개변수와, 반환값으로 사용될 VARIANT_BOOL*형의 lpvbResult를 추가했다.
CHelloCtrl에 자동으로 추가된 ShowMessage(BSTR bstrCaption, VARIANT_BOOL* lpvbResult)함수에 다음 코드를 추가하자. 실제 구현은 단순하지만, lpvbResult를 통해 어떻게 외부에 값을 반환하는지를 주의깊게 보시기 바란다.
- STDMETHODIMP CHelloCtrl::ShowMessage(BSTR bstrCaption, VARIANT_BOOL* lpvbResult)
- {
- int nMessageBoxResult =
- ::MessageBox
- (
- GetActiveWindow(),
- m_strMessage, bstrCaption,
- MB_YESNO | MB_ICONINFORMATION
- );
- if ( nMessageBoxResult == IDYES )
- *lpvbResult = TRUE;
- else
- *lpvbResult = FALSE;
- return S_OK;
- }
Method의 동작을 확인하기 위해 스크립트 코드를 HelloCtrl.htm에 추가하자.
- <script type="text/javascript">
- ...
- function callShowMessage() {
- var ctrl = document.getElementById("HelloCtrl");
- ctrl.Message = "Hello GreenB!";
- if (ctrl.ShowMessage("GreenmaruX") == true) {
- alert("Yes button clicked.");
- }
- else {
- alert("No button clicked.");
- }
- }
- </script>
- </head>
- <body>
- ...
- <input type="button" onclick="callShowMessage()" value="Call ShowMessage" />
- ...
실행시켜 보면, 컨트롤과 익스플로러에서 각각 한번씩 Message box를 표시될 것이다.