[C++] ActiveX 만들기

개발 노트/C 언어  2012.09.14 19:31



 

안녕하세요 개발자 명월입니다.

 

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

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

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

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

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

 

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

 

그럼 시작하겠습니다.

 

 

 

 

 

 

새 프로젝트가 열리면

[프로젝트] -> 속성을 확인 하겠습니다.

 

 

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

대형 프로젝트에서는 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코드가 약간은 다를 수 있습니다. 참조 예제 파일 올렸으니 직접확인 하시는 것도 좋습니다.

 


댓글 5개가 달렸습니다.
댓글쓰기
  1. 안녕하세요
    2012.10.04 17:07 신고 |  수정/삭제  댓글쓰기

    예제 따라해서 ocx파일 생성 하고 cmd창에서 레지스터 등록됬다고 메시지 떳고 html txt파일로 작성해서 확장자 html로 변환한 뒤에 실행시켰는데 메시지박스가 안뜨네요...

    idl 파일에 보면 uuid가 4개나 존재하는데 하나씩 바꿔가면서도 해봤는데두 안되구요..

    html파일의 Test object에서 TestFunction호출을 하는게 안되는건지..

    호출은 되고 mfc에서 afxmessagebox출력을 못하는건지가 궁금합니다..

    • 明月 v명월v
      2012.10.04 17:35 신고 |  수정/삭제

      안녕하세요. 먼저 저희 블로그에 방문 주신거에 대해 감사드립니다.
      말씀 하신 부분이 어디가 정확히 에러인지 판단이 안되서 그러는데.
      그럼 일단 레지스트 등록, 즉 Regsvr32 등록 전에 HTML과 ocx 파일을 같은 폴더 안에 넣고 실행 시켜보세요.

      그리고 uuid 코드는 제 기억으론 가장 아래 쪽에 있는 레지등록 번호 일 것입니다.

      만약 같은 폴더에 넣었는데 안된다면 맨 처음 레지스트 등록번호 넣을때 문제가 생긴 것일 수도 있습니다.

      같은 폴더에 넣었는데 된다하면 레지스트리 등록하는 코드 부분에 문제가 생긴 것 입니다. 제 코드를 긁어서 사용했다고 하시면 간혹 백신에서 미확인 ActiveX를 차단하는 경우가 간혹 있으니 해당 백신을 끄고 재부팅후에 해보시면 될 듯 싶습니다.

  2. 안녕하세요
    2012.10.04 18:24 신고 |  수정/삭제  댓글쓰기

    빠른 답변 감사합니다..^^; 정확히 안된다 라고 말씀드릴수 있는 것은 html파일을 열었을 때 아무 동작도 하지 않는 것밖에 없긴 한데요..소스에서 에러가 있거나 uuid를 잘못 설정한 것 같지는 않아요..

    단 제가 컴파일할때 사용자 리다이렉션 어쩌구 뜨는 부분에 대한 설정값만 바꾸어주고
    (구성속성->링커->일반-> 출력등록 & 사용자 단위 리디렉션 속성:아니오로 변경)

    컴파일해서 ocx를 만들었는데요..그게 문제일지도 모르겠네요..;

    • 明月 v명월v
      2012.10.04 20:59 신고 |  수정/삭제

      그럼 스크립트 에러가 뜨지 않는 다는 것은 HTML이 지금 ocx를 인식하고 있다라는 뜻입니다. 저도 지금 딱히 어디가 문제일지도 모른다라는 것을 답변해드릴 수 없으니 제가 오늘 내일 중으로 확인하여 답변드리겠습니다. 감사합니다.

  3. hong
    2013.09.05 18:29 신고 |  수정/삭제  댓글쓰기

    안녕하세요...
    activeX로 레지스트리 등록하는 프로그램을 개발해야 하는데요...혹시 이 코드로 UAC권한 얻는게 가능한가요?
    현재 RegCreateKeyEx함수가 실행이 안돼서 고민하고 있네요...