본문 바로가기

3.구현/VC++

안정성을 보장하는 ActiveX 컨트롤 제작

ActiveX 사용을 가급적 지양한다. 혹시나 필요할 수도 있어서 글을 유지한다.

일반적인 방식으로(ActiveX 컨트롤 제작에서 설명한 방식) ActiveX 컨트롤을 작성하면, 웹에서 ActiveX 사용의 마지막 부분에 ActiveX 컨트롤과 Html 객체와의 연동 시에 보안 문제가 있다는 것을 설명했다. 이제 그 해결법을 설명하고자 한다.

간단히 설명하면 ActiveX 컨트롤 루틴에 안정성을 보장하는 루틴(안정성을 보장하는 clsid를 레지스트리에 등록하는 루틴)을 추가해야 한다. 먼저 레지스트리에 clsid를 등록하는 함수를 작성하고 실제 ActiveX 컨트롤 제작에서 작성한 컨트롤에 추가를 해보기로 한다.

함수 작성

다음과 같은 내용의 cathelp.h라는 파일을 작성한다.

#if !defined(__CATHELP_H)
#define __CATHELP_H

#include "comcat.h"

// Helper function to create a component category and associated
// description
HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription);

// Helper function to register a CLSID as belonging to a component
// category
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid);

#endif

다음과 같은 내용의 cathelp.cpp라는 파일을 작성한다.

#include "stdafx.h"
#include "comcat.h"

// Helper function to create a component category and associated
// description
HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription)
{
       ICatRegister* pcr = NULL ;
       HRESULT hr = S_OK ;

       hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
                                          NULL,
                                          CLSCTX_INPROC_SERVER,
                                          IID_ICatRegister,
                                          (void**)&pcr);
       if (FAILED(hr))
               return hr;

       // Make sure the HKCR\Component Categories\{..catid...}
       // key is registered
       CATEGORYINFO catinfo;
       catinfo.catid = catid;
       catinfo.lcid = 0x0409 ; // english

       // Make sure the provided description is not too long.
       // Only copy the first 127 characters if it is
       int len = wcslen(catDescription);
       if (len>127)
               len = 127;
       wcsncpy(catinfo.szDescription, catDescription, len);
       // Make sure the description is null terminated
       catinfo.szDescription[len] = '\0';

       hr = pcr->RegisterCategories(1, &catinfo);
       pcr->Release();

       return hr;
}

// Helper function to register a CLSID as belonging to a component
// category
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
// Register your component categories information.
       ICatRegister* pcr = NULL ;
       HRESULT hr = S_OK ;
       hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
                                          NULL,
                                          CLSCTX_INPROC_SERVER,
                                          IID_ICatRegister,
                                          (void**)&pcr);
       if (SUCCEEDED(hr))
       {
               // Register this category as being "implemented" by
               // the class.
               CATID rgcatid[1] ;
               rgcatid[0] = catid;
               hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid);
       }

       if (pcr != NULL)
               pcr->Release();

       return hr;
}

위의 루틴의 내용을 이해하기 위해서 노력할 필요는 없다. 단지 "레지스트리에 clsid를 등록하는 함수구나"라고만 이해하고 넘어가기 바란다.
Test Control에 추가App 파일(여기서는 Test.cpp)를 열어 다음과 같이 위에서 작성한 cathelp.h를 include한다.

#include "cathelp.h" 

조금 아래에 다음과 같이 _tlid가 선언되어 있는 것을 볼 수 있을 것이다(_tlid에 할당된 값은 프로그램 마다 다르다).

const GUID CDECL BASED_CODE _tlid =
               { 0x9b548709, 0xc3df, 0x4956, { 0x9f, 0x65, 0x29, 0x28, 0xca, 0xbb, 0x6e, 0xc8 } };

바로 아래에 비슷한 3개를 더 등록해야 한다.
두 개는 다음과 같이 안정성을 보장하기 위해 예약된(고정된) clsid이고,

const CATID CATID_SafeForScripting     =
               {0x7dd95801,0x9882,0x11cf,{0x9f,0xa9,0x00,0xaa,0x00,0x6c,0x42,0xc4}};
const CATID CATID_SafeForInitializing  =
               {0x7dd95802,0x9882,0x11cf,{0x9f,0xa9,0x00,0xaa,0x00,0x6c,0x42,0xc4}};

나머지 하나는 ctrl 파일(여기서는 testctrl.cpp)에 있는 다음과 같은 내용을 복사해 와서,

IMPLEMENT_OLECREATE_EX(CTestCtrl, "TEST.TestCtrl.1",
       0xd886696, 0xc7ce, 0x11d3, 0xa1, 0x75, 0x8, 0, 0x2b, 0xf1, 0x75, 0x7)

다음과 같이 변경(_ctlid 선언)하면 된다.

const GUID CDECL BASED_CODE _ctlid =
       {0xd886696, 0xc7ce, 0x11d3, 0xa1, 0x75, 0x8, 0, 0x2b, 0xf1, 0x75, 0x7}

이제 App 파일에 있는 다음과 같은 함수에 레지스트리 등록 루틴을 추가하면된다.

STDAPI DllRegisterServer(void)
{
   AFX_MANAGE_STATE(_afxModuleAddrThis);

   if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
           return ResultFromScode(SELFREG_E_TYPELIB);

   if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
           return ResultFromScode(SELFREG_E_CLASS);

   return NOERROR;
}

추가한 루틴은 다음과 같다.

STDAPI DllRegisterServer(void)
{
       AFX_MANAGE_STATE(_afxModuleAddrThis);

       if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
               return ResultFromScode(SELFREG_E_TYPELIB);

       if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
               return ResultFromScode(SELFREG_E_CLASS);

      if (FAILED( CreateComponentCategory(CATID_SafeForScripting, L"Controls that are safely scriptable") ))
               return ResultFromScode(SELFREG_E_CLASS);

       if (FAILED( CreateComponentCategory(CATID_SafeForInitializing, L"Controls safely initializable from persistent data") ))
               return ResultFromScode(SELFREG_E_CLASS);

       if (FAILED( RegisterCLSIDInCategory(_ctlid, CATID_SafeForScripting) ))
               return ResultFromScode(SELFREG_E_CLASS);

       if (FAILED( RegisterCLSIDInCategory(_ctlid, CATID_SafeForInitializing) ))
               return ResultFromScode(SELFREG_E_CLASS);

       return NOERROR;
}

위 내용 또한 그대로 사용하면 되므로 이해하기 위해서 너무 많은 노력을 들이지는 말기 바란다. 컴파일을 하고 실행을 하면 보안 대화 상자가 생성되지 않고 잘 동작된다는 것을 알 수 있다.

샘플파일:

SafityActiveXDemo.zip
26.6 kB

참고

[1] http://blog.naver.com/loverock74?Redirect=Log&logNo=30009874182

반응형