[C++] ActiveX 만들기


Development note/C , C++ , MFC  2012. 9. 14. 19:31

안녕하세요. 명월입니다.

 

이번 포스팅에서는 ActiveX 개발에 관한 포스팅입니다. 최근 웹 어플리케이션의 추세는 ActiveX가 점점 죽어가는 현실입니다.

실제로 ActiveX는 보안에도 취약하고 각종 악성코드 등을 양상해 내놓기 때문에 비추천 개발프로젝트 중 하나 입니다.

그러나 그렇게 안 좋은 코드, 프로그램이면서도 한국에서는 많이 사용하는 이유는 웹과 CS(Client Server)프로그램을 연결 시켜주기 때문입니다. 죽 웹폼과 어플리케이션, 어플리케이션과 웹폼과 통신이 된다고 생각하면 됩니다.

즉, 웹의 장점과 CS의 장점을 다 취할 수 있는 코드이기 때문에 아직도 많이 사용되는 것 입니다.

(이 말을 다시 말하면 복잡한 개발 코드가 간단하게 작성이 될 수 있다라는 뜻입니다.)

 

일단 그건 추세이고, 내가 ActiveX가 안좋다 또는 ActiveX를 비판하게 앞서 ActiveX 가 무엇인지 알아 둘 필요가 있습니다.

새 프로젝트가 생성되면 [프로젝트] -> 속성을 확인 하겠습니다.

속성에서 먼저 MFC사용을 정적으로 바꿉니다.

큰 프로젝트에서는 여러 라이브러리를 연결해서 사용하기 때문에 컴파일 용량을 줄일 수 있습니다.

그러나 이런 작은 프로그램 만들 때는 라이브러리를 연결하는 것이 어렵기 때문에 정적으로 선언을 하는게 용량은 크지만 실행상에 에러를 줄일 수 있습니다.

그외는 문자 집합은 유니코드로 설정합니다. 설정이 완료되면 확인을 누릅니다.

소스파일을 보면 파일이 총 6개 있습니다.

파일명 어미는 프로젝트 이름으로 자동 생성 되었고 그 외 cpp, 컨트럴.cpp, idl 등으로 나뉩니다. 여기서 먼저 수정을 해야 할 것은 cpp 부분입니다.

ActiveX_20120914.cpp를 확인하겠습니다.

 

윗 부분에 해더를 선언합니다.

#include "comcat.h"
#include "strsafe.h"
#include "objsafe.h"

그런후에 밑에 const CATID CLSID_SafeItem를 수정합니다.

수정하는 방법은 옆에 ActiveX_20120914Ctrl.cpp 파일을 확인하면 IMPLEMENT_OLECREATE_EX 이란 메소드가 있습니다.

거기 밑에 uuid 코드를 가져 옵니다.

4번째부터 끝자리수를 따로 다시 묶습니다.


IMPLEMENT_OLECREATE_EX(CActiveX_20120914Ctrl, "ACTIVEX_20120914.ActiveX_20120914Ctrl.1", 0xf8e13d79, 0x5aa8, 0x40b1, 0xbe, 0x32, 0xfd, 0xef, 0xd1, 0xfb, 0xfe, 0xcc)
// 아래와 같이 바꾼다.
const CATID CLSID_SafeItem ={ 0xf8e13d79, 0x5aa8, 0x40b1, {0xbe, 0x32, 0xfd, 0xef, 0xd1, 0xfb, 0xfe, 0xcc}};

카테고리를 추가합니다.

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;

CATEGORYINFO catinfo;
  catinfo.catid = catid;
  catinfo.lcid = 0x0409;
  size_t len;
  
hr = StringCchLength(catDescription, STRSAFE_MAX_CCH, &len);
  if (SUCCEEDED(hr))
  {
    if (len>127)
    {
      len = 127;
    }
  }  
  else
  {
  }
  hr = StringCchCopy(catinfo.szDescription, len + 1, catDescription);
  catinfo.szDescription[len + 1] = '\0';
  hr = pcr->RegisterCategories(1, &catinfo);
  pcr->Release();
  return hr;
}
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
  ICatRegister *pcr = NULL ;
  HRESULT hr = S_OK ;
  hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
  if (SUCCEEDED(hr))
  {
     CATID rgcatid[1] ;
     rgcatid[0] = catid;
     hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid);
  }
  if (pcr != NULL)
  {
    pcr->Release();
  }
  return hr;
}
HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
  ICatRegister *pcr = NULL ;
  HRESULT hr = S_OK ;
  hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
  if (SUCCEEDED(hr))
  {
     CATID rgcatid[1] ;
     rgcatid[0] = catid;
     hr = pcr->UnRegisterClassImplCategories(clsid, 1, rgcatid);
  }
  if (pcr != NULL)
  {
    pcr->Release();
  }
  return hr;
}

그리고 레지스트리 등록 메소드를 변경합니다.

STDAPI DllRegisterServer(void)
{
  HRESULT hr;
  AFX_MANAGE_STATE(_afxModuleAddrThis);
  if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
  {
    return ResultFromScode(SELFREG_E_TYPELIB);
  }
  if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
  {
    return ResultFromScode(SELFREG_E_CLASS);
  }
  hr = CreateComponentCategory(CATID_SafeForInitializing,L"Controls safely initializable from persistent data!");
  if (FAILED(hr))
  {
    return hr;
  }
  hr = RegisterCLSIDInCategory(CLSID_SafeItem,CATID_SafeForInitializing);
  if (FAILED(hr))
  {
    return hr;
  }
  hr = CreateComponentCategory(CATID_SafeForScripting,L"Controls safely  scriptable!");
  if (FAILED(hr))
  {
    return hr;
  }
  hr = RegisterCLSIDInCategory(CLSID_SafeItem,CATID_SafeForScripting);
  if (FAILED(hr))
  {
    return hr;
  }
  return NOERROR;
}
STDAPI DllUnregisterServer(void)
{
  HRESULT hr;
  AFX_MANAGE_STATE(_afxModuleAddrThis);
  if (!AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor))
  {
    return ResultFromScode(SELFREG_E_TYPELIB);
  }
  if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE))
  {
    return ResultFromScode(SELFREG_E_CLASS);
  }
  hr=UnRegisterCLSIDInCategory(CLSID_SafeItem,CATID_SafeForInitializing);
  if (FAILED(hr))
  {
    return hr;
  }
  hr=UnRegisterCLSIDInCategory(CLSID_SafeItem,CATID_SafeForScripting);
  if (FAILED(hr))
  {
    return hr;
  }
  return NOERROR;
}

기본 설정은 완료 되었습니다.

그럼 이제 메소드를 만들어 보겠습니다.

ActiveX20120914Ctrl.cpp 를 들어가서 가장 밑에다가 입력하겠습니다.

void CActiveX_20120914Ctrl::TestFunction(LPCSTR Parameter)
{
  AfxMessageBox((LPCTSTR)Parameter);
}

 

C++에서 메소드 등록을 하려면 헤더를 등록 해야 합니다.

ActiveX20120914Ctrl.h 로 가서 등록하겠습니다.

중간에 public 에 void TestFunction(LPCSTR Parameter); 라고 등록하였습니다.

다시 ActiveX20120914Ctrl.cpp 로 돌아가서 디스패치 맵을 수정합니다

윗부분에 있습니다.

DISP_FUNCTION(CLinkCtrl, "TestFunction", TestFunction, VT_EMPTY, VTS_BSTR)

이제 F6를 눌러서 컴파일을 합니다. (ActiveX는 웹에서 기동을 하기 때문에 디버깅또는 실행이 되지 않습니다.)

Debug 폴더 가보면 OCX 파일이 있습니다.

웹 문서를 작성하겠습니다.

Test 오브젝트를 불러서 그안의 TestFunction 함수를 불러드립니다.

참고로 clsid(클래스 아이디)는 프로젝트의 옆에 ActiveX_20120914.idl 가장 밑에 보면 uuid 가 있습니다. 그게 클래스 아이디 입니다. ( 밑에 사진은 중간에 제가 프로젝트를 지워버린 바람에 다시 만들어서 프로젝트 명이 바뀌었습니다.)

요렇게 맞추고 끝나냐 아닙니다.

그리고 등록하는 단계 시작버튼 -> 실행 -> CMD를 눌러서 해당 디렉토리에 갑니다.

Regsvr32 ActiveX_20120914.ocx 로 등록을 합니다.

그런 후에 웹페이지 만든 것을 같은 폴더에 둡니다. (사실 등록 완료되면 레지스트리 따라서 움직이니 같은 폴더 개념은 의미가 없습니다.)

그리고 실행해봅니다.

ActiveX가 실행 된 화면입니다.

예제 파일입니다.

 

ActiveX_Test.zip

※ 설명이랑 예제 이미지랑 안 맞을 수 있습니다. 무슨 영문인지 이미지가 싹 지워져 있었네요.ㅜㅜ

일단 다시 스샷을 찍어서 올리기는 했으나 소스는 안틀리나 프로젝트명이라던가 UUID코드가 약간은 다를 수 있습니다.