본문 바로가기
프로그램 (PHP,Python)

ActiveX 컨트롤 제작 및 배포

by 날으는물고기 2009. 6. 6.

ActiveX 컨트롤 제작 및 배포

  ActiveX는 마이크로 소프트사에서 COM, DCOM 기술에 인터넷 기술을 접목하여 개발한 개념으로 인터넷을 지원하는 프로그램이다. 요즈음 Application 프로그램의 개발은 ActiveX로 한번 프로그램하여 인터넷에서도 Local Application과 똑같이 동작하도록 프로그램하는 것이 추세이다. 그러나 Visual C++을 이용한 ActiveX 프로그램에 대한 자료를 구하기가 어려워, 지금까지 고생해가며 구한 자료를 토대로 내용을 정리하고자 한다. 이 여기에서 설명하는 내용만 이해하고 나면 ActiveX 프로그램을 어느 정도 제작할 수 있을 것이다.

  참고로 ActiveX를 작성하는 방식에는 MFC를 이용하는 방식과 ATL을 이용한 방식이 있는데 ATL을 이용하여 ActiveX Control을 제작하면 MFC를 이용하는 방식보다 다소 어렵기는 하나 작고 빠른 코드를 생산할 수 있는 장점이 있어 더 많이 사용되고 있다. 필자의 개인적 견해로는 컴퓨터와 네트워크의 발전으로 작고 빠른 코드를 생산할 수 있다는 장점은 크게 중요하다고 생각하지 않고, 그보다는 많은 사람들이 사용하는 방식이라는 점(소스 및 자료를 더 쉽게 구할 수 있다)이 더큰 장점이라고 생각된다. 그러나 MFC로 제작된 프로그램을 ActiveX로 수정 작성할 경우에는 ATL을 이용하는 방식 보다 특히 쉬우므로 여기서는 MFC를 이용한 방식을 설명한다.


ActiveX 컨트롤 제작

ActiveX 컨트롤의 확장자는 ocx이다. 이 파일은 한번 작성하여 일반 Application에서도 웹상에서도 사용이 가능하다. 여기에서는 ocx파일 만드는 방법을 설명한다.

먼저 Visual C++의 파일 메뉴의 부메뉴 New를 실행하면 다음과 같은 대화 상자가 생성된다.


MFC ActiveX ControlWizard를 선택하고 Project name에 프로젝트 이름을 입력한 후 OK 버튼을 선택하면 다음과 같이 대화 상자가 바뀐다.


Next>를 선택하면 대화 상자는 다음과 같이 바뀐다.


Finish를 선택하면 다음과 같은 대화 상자가 생성되고 OK를 선택하면 ActiveX 프로젝트가 생성된다.


생성되는 파일 및 그 기능(일반 Visual C++ 프로그램에서 생성되는 파일과 기능 비교)들을 설명하면 다음과 같다(프로젝트 명을 Tetris라고 입력했다고 가정).

Tetris.cpp : App 기능과 같은 역할을 한다.

TetrisPpg.cpp : Document 기능과 같은 역할을 하도록 구성된 것 같으나 거의 사용하지 않고 Ctrl에 메모리 내용도 구현한다.

TetrisCtrl.cpp = View 기능과 같은 역할을 한다. 즉 TetrisCtrl.cpp파일을 열어 OnDraw함수에 화면에 표시할 루틴을 입력한다.

 

ocx 파일은 바이너리 파일 형태로 배포가 된다. 따라서 ocx파일을 다른 Application에서 Access가능한 함수와 변수를 특수한 방법으로 정의해야만 한다. 외부에 노출되는 함수를 메소드라 하고 변수를 프로퍼티라 한다.

클래스 위자드를 실행하여 Automation 탭을 선택하면 다음과 같이 화면에 나타난다.


Add Method 및 Add Property버튼을 이용하여 함수 및 변수를 추가하면 외부 프로그램에서 Access가 가능하다.

나머지 프로그램은 일반 Visual C++ 프로그램하는 것과 같은 방법으로 하면 되고 화면을 새로 그려주는 함수인 Invalidate 대신 InvalidateControl을 이용한다.

 

데모 프로그램

  데모 프로그램은 아주 간단하게 제작하였다. Test라는 ActiveX 프로젝트를 만들고 일반 프로그램 제작하듯이 TestCtrl.h에 CString m_strText 및 short m_nDrawType을 선을 하고, TextCtrl.cpp에서 m_nDrawType이 0이면 사각형을 1이면 원을 그리고 m_strText의 문자열을 화면상에 출력하도록 구성한다.

외부 프로그램에서 이 ActiveX 프로그램을 제어하기 위하여 SetText 메서드를(m_strText 변경), DrawType 프로퍼티를 제공하였다. 다음은 그 방법을 설명한 것이다.

ClassWizard를 실행하여 Add Method 버튼을 선택하면 다음과 같은 대화 상자가 화면에 생성된다.


    External name : 외부 프로그램에서 호출할 함수 이름을 입력한다.

    Internal name : ActiveX 프로그램 내부적으로 사용할 함수 이름을 설정한다. 기본적으로 External name과 같은 이름으로 입력이 되는데 변경할 필요는 없다.

    Return type : ComboBox에서 return 할 type을 설정한다. 여기서는 return 할 데이터가 없으므로 void를 선택한다.

    Parameter list : 넘겨줄 인자를 설정한다. Name부분을 Click하여 변수 이름을 입력하고, Type 부분의 ComboBox에서 인자의 type을 설정한다.

 

Add Property를 선택하면 다음과 같은 대화 상자가 생성된다.


    External name : 외부에서 Access할 변수 이름을 설정한다.

    Type : 변수 type을 설정한다.

    Implementation : Get/Set methods를 선택한다.

    Get function : 변수를 받아들이는 함수이름을 입력한다. 자동 입력

    Set function : 변수를 설정하는 함수이름을 입력한다. 자동 입력

 

소스를 보면 TestCtrl.cpp에 SetText, GetDrawType, SetDrawType함수가 추가되어 있는 것을 볼 수 있을 것이다. 이 함수들을 다음과 같이 수정한다.

void CTestCtrl::SetText(LPCTSTR strText)
{

    m_strText = strText;
    InvalidateControl;

}

short CTestCtrl::GetDrawType()
{

    return m_nDrawType;

}

void CTestCtrl::SetDrawType(short nNewValue)
{

    m_nDrawType = nNewValue;
    InvalidateControl;
    SetModifiedFlag();

}

 

컴파일을 하면 test.ocx가 생성된다.

 

프로그램 소스 다운로드


일반 Application에서 ActiveX 사용

 Visual C++을 이용하여 작성한 일반 윈도우용 프로그램에서 ActiveX를 사용하는 방법을 설명한다.

우선 프로그램을 작성해 보자. File 메뉴의 부메뉴 New를 선택하여 생성된 Application Wizard에서 프로젝트 명칭을 UserActiveX라고 설정하고, Step 1에서 Dialog base를 선택하여 프로그램을 작성한다.


Project 메뉴의 부메뉴 Add To Project의 부메뉴 Components and Controls를 선택하면 다음과 같은 대화 상자가 생성된다.


Registered ActiveX Controls를 선택하면 등록된 ActiveX 컨트롤들의 리스트가 나타난다. 이들 중 Test Control("삽입하고자 하는 ActiveX 컨트롤 이름" control)을 선택한다.

위의 작업을 수행하면 Visual C++은 두가지 작업을 수행한다. 첫째는 ActiveX 컨트롤에서 외부에 공개한 메서드와 프로퍼티를 사용하도록 클레스를 제공하고(여기서는 Test.cpp 및 Test.h), 다음 그림과 같이 대화 상자에 콘트롤을 삽입하도록 하는 controls 바에 OCX라는 컨트롤이 추가된다.


OCX를 선택한 후 대화 상자에 삽입하면 ActiveX 컨트롤이 삽입된다.

이 이후는 일반 Application과 같으므로 말로만 대강 설명한다. 모르겠는 사람은 Source를 보면 금방 알 수 있을 것이다.


위 그림과 같이 실행 되도록 대화 상자를 적당히 배치하고 각 컨트롤을 다음과 같이 설정한다.

ActiveX Control ID : IDC_TESTCTRL   -> CTest m_TestCtrl(ClassWizard 실행하여 변수 설정)

Edit ID : IDC_TEXT_EDIT -> CString m_strText(ClassWizard 실행하여 변수 설정)

Button ID : IDC_TEXT_BUTTON -> 버튼 메시지 처리(ClassWizard 실행하여 BN_CLICKED 메시지 처리)

ComboBox ID : IDC_DRAWTYPE_COMBO ->int m_nDrawType 설정 및 콤보 박스 메시지 처리(ClassWizard 실행하여 CBN_SELCHANGED 메시지 처리)

버튼 메시지 처리 함수와  콤보 박스 메시지 처리 함수를 보면 다음과 같다.

void CUseActiveXDlg::OnTextButton()
{
        UpdateData();
        m_TestCtrl.SetText(m_strText);
}

void CUseActiveXDlg::OnSelchangeDrawTypeCombo()
{
        UpdateData();
        m_TestCtrl.SetDrawType(m_nDrawType);
}

 

프로그램 소스 다운로드



웹에서 ActiveX 사용

  웹에서 ActiveX 사용은 단순히 ActiveX 컨트롤을 html 파일상에 끼워 넣는 것은 간단하다. 그러나 제대로 사용하기 위해서는 html의 버튼, 글상자 등과 같은 객체와 연동하여 사용하기 위해서는 작업을 좀 더 해 주어야 하고 인터넷상에 배포하기 위해서는 두가지 보안 문제를 해결해야 한다. 여기에서는 ActiveX 컨트롤을 html 파일 상에 끼워 넣고 html 객체와 연동하여 동작하도록 하는 방법까지만 설명하고 인터넷상에 배포시 발생하는 두가지 보안 문제를 안정성을 보장하는 ActiveX 컨트롤 제작Cab파일 작성법에서 설명한다.

 

ActiveX 컨트롤을 html 파일상에 끼워 넣기

  ActiveX 컨트롤을 html 파일상에 끼워 넣는 방법으로는 여러 가지 방법이 있겠지만 여기서는 나모 웹에디터를 이용하는 방법을 설명하고, 간단히 그 html 파일에 대하여 설명한다.

나모 웹에디터를 실행하여 삽입 메뉴의 부메뉴 고급 메뉴의 부메뉴 ActiveX 콘트롤을 선택하면 다음과 같은 대화 상자가 생성된다.


클래스 ID의 콤보 박스에서 자신이 만든 ActiveX 컨트롤을 선택(자신이 만든 ActiveX 컨트롤의 이름 뒤에 Control 이 붙어 있는 항목 선택)한 후 확인을 선택하면 ActiveX 컨트롤이 삽입된다(조금 이상하게 보이더라도 신경쓰지 말것).

삽입된 컨트롤을 적당한 크기로 크기 조절을 한 후 저장을 한다. 저장된 파일을 Internet Explore에서 열면 작성한 ActiveX 컨트롤이 Internet Explore상에 나타날 것이다.

다음은 test.ocx를 만들어 html 파일에 끼워 넣은 후 저장한 파일의 내용이다.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<title>Untitled</title>
<meta name="generator" content="Namo WebEditor v3.0">
</head>

<body bgcolor="white" text="black" link="blue" vlink="purple" alink="red">

<p><object classid="clsid:0D886696-C7CE-11D3-A175-08002BF17507" width="445" height="449">
</object>
</p>
</body>

</html>

위 내용에서 나머지는 html 파일의 일반 내용이고 ActiveX 컨트롤을 끼워 넣어서 생긴 내용은 다음과 같다.

<p><object classid="clsid:0D886696-C7CE-11D3-A175-08002BF17507" width="445" height="449">
</object>
</p>

여기에서 clsid는 ActiveX 컨트롤을 만든 프로젝트 디렉토리의 .odl 파일을 열어 가장 마지막에 나와있는 clsid와 같은 것을 알 수 있다. 따라서 위와 같이 Text 편집기로 편집을 하여도 된다(나모 웹에디터가 없는 분은 Text 편집기로 편집해야만 될것이다).

 

HTML 객체와 ActiveX의 연동

  다시 나모웹 에디터로 가보자. 삽입 메뉴의 부메뉴 폼필드 메뉴의 부메뉴 한 줄 글상자를 선택하면 다음과 같은 대화 상자가 생성된다.


이름에 TextEdit(이름을 가지고 VbScript와 연동을 하므로 알아 보기 쉽게 입력)라고 입력하고 초기값을 ActiveX 테스트라고 입력한다.

삽입 메뉴의 부메뉴 폼필드 메뉴의 부메뉴 누름 단추를 선택하면 다음과 같은 대화 상자가 생성된다.


이름에 TextButton이라고 입력하고 값을 설정(화면에 표시될 문자열)이라고 입력하고, 단추 유형을 일반으로 선택한 후 이벤드를 선택하면 다음과 같은 대화 상자가 생성된다.


onClick(버턴이 눌러 졌을 때 호출될 함수 이름을 설정, 나중에 vbscript로 구현해야 한다)에 OnTextButtonClick라고 입력하고 확인을 선택한다.

삽입 메뉴의 부메뉴 폼필드 메뉴의 부메뉴 펼침 목록 메뉴를 선택하면 다음과 같은 대화 상자가 생성된다.


이름에 DrawTypeCombo라고 입력하고 추가를 선택하면 다음과 같은 대화 상자가 생성된다.


내용(ComboBox에 표시될 항목을 설정)에 사각형이라고 입력하고, 값(그 항목을 선택했을 때 값)에 0이라고 입력한 후 확인을 선택하면 항목이 하나 추가된다.

같은 방식으로 내용에 원을, 값에 1을 입력한 후 확인을 선택한다.

이벤트 버튼을 선택하면 다음과 같은 대화 상자가 생성된다.


onChange(콤보 박스 상태를 변경 시켰을 때 호출될 함수 이름을 설정, 나중에 vbscript로 구현해야 한다)에 OnDrawTypeChange라고 입력하고 확인을 선택한다.

화면 꾸미기는 이것으로 끝이다. 저장한 파일을 Internet Explore에서 열면 다음과 같이 화면에 나타난다.


이제는 Html 소스 파일을 열어 Text 편집을 하여야 한다. 지금까지 작성한 소스는 다음과 같다.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<title>Untitled</title>
<meta name="generator" content="Namo WebEditor v3.0">
</head>

<body bgcolor="white" text="black" link="blue" vlink="purple" alink="red">

<p><object classid="clsid:0D886696-C7CE-11D3-A175-08002BF17507" width="315" height="320">
</object>
 &nbsp;</p>
<form name="form" method="get">
<<p><input type="text" name="TextEdit" value="ActiveX 테스트"> <input type="button"
 name="TextButton" value="설정" onclick="OnTextButtonClick"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<select
 name="DrawTypeCombo" onchange="OnDrawTypeChange">
option value="0">사각형</option>
<option value="1">원</option></select></p>
</form>

<p>&nbsp;</p>
</body>

</html>

HTML 객체를 추가하기 전 소스와 비교해 보면 각 추가된 부분과 그 의미를 알 수 있을 것이다(Text 편집기에서 수동으로도 입력할 수 있을 것이다).

우선 완성된 소스를 보면 다음과 같다.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<title>Untitled</title>
<meta name="generator" content="Namo WebEditor v3.0">
</head>

<body bgcolor="white" text="black" link="blue" vlink="purple" alink="red">

<script language="vbscript">
sub OnTextButtonClick()
        Test.SetText form.TextEdit.value       
end sub

sub OnDrawTypeChange()
        Test.DrawType = form.DrawTypeCombo.value
end sub
</script>

<p><object classid="clsid:0D886696-C7CE-11D3-A175-08002BF17507" width="315" height="320"
id="Test"></object>
 &nbsp;</p>
<form name="form" method="get">
<p><input type="text" name="TextEdit" value="ActiveX 테스트"> <input type="button"
 name="TextButton" value="설정"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<select
 name="DrawTypeCombo">
<option value="0">사각형</option>
<option value="1">원</option></select></p>
</form>
<p>&nbsp;</p>
</body>

</html>

위에서 보면 이벤트 대화 상자에 입력한 함수들을 vbscript로 추가한 것을 알 수 있다. 또한 그 함수에서 ActiveX 컨트롤과 연동하고 있는데 우선 ActiveX 컨트롤 삽입 문장에서 그 컨트롤을 구분해 주기 위하여 id="Test"를 추가한다.

기억을 더듬어 보면 Test.ocx라는 ActiveX 컨트롤을 만들 때 SetText라는 메서드와 DrawType라는 프로퍼티를 만든 것을 기억할 것이다. 또한 Html 객체 들은 form.객체이름.value라고 하면 그 객체에 설정된 값을 읽을 수 있다는 것만 알면 vbscript에 대하여 잘 몰라도 위의 추가된 내용이 이해가 갈 것이다.

실행을 해보면 동작은 잘되나 하나의 문제점이 있다는 것을 알 수 있다. 그 문제 점은 처음 버튼이나 콤보 박스를 변경하면 다음과 같은 대화 상자가 생성되어 예를 선택해야만 제대로 동작한다는 것이다.


이 문제는 인터넷으로 공개했을 때 Internet Explore의 보안 설정을 낮음으로 설정하지 않으면 아예 동작을 하지 않게 하므로 꼭 해결해야 하는 문제다.
안정성을 보장하는 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;
}

위 내용 또한 그대로 사용하면 되므로 이해하기 위해서 너무 많은 노력을 들이지는 말기 바란다.

컴파일을 하고 실행을 하면 보안 대화 상자가 생성되지 않고 잘 동작된다는 것을 알 수 있다.

 

프로그램 소스 다운로드



배포하기(Cab 파일 작성법)

ActiveX 컨트롤(.OCX)을 웹페이지에 포함하여 다른 PC에서 그 페이지를 열면 제대로 동작하지 않는다. 제대로 동작하게 하기 위해서는 ActiveX 컨트롤을 각 PC에 복사하고 그 컨트롤을 레지스트리에 등록해주어야만 한다. 또한 필요한 DLL이 있다면 그 DLL 또한 복사해 주어야한다. 이 일련의 작업들을 자동으로 해주는 것이 Cab 파일이다.

Visual Basic에서는 배포 마법사라는 프로그램이 있어 이 Cab 파일을 쉽게 만들 수 있다. 그러나 Visual C++은 ....  필자가 근 1년을 헤매다가 겨우 찾았다.

ActiveX 컨트롤이 만들어진 상황에서 Cab 파일을 만드는 작업 순서를 보면 다음과 같다.

1. .inf 파일을 작성한다.

2. .ocx 파일과 .inf 파일을 .cab 파일로 압축한다.

3. .cab 파일을 서명한다.

4. .cab 파일을 웹페이지에 등록한다.

 

.inf 파일 작성법

.inf 파일은 Visual Basic에서는 배포마법사에서 VB project를 설정하여 .inf 파일이 자동으로 생성되어 .cab 파일에 포함된다. 그러나 Visual C++로 프로그램했다면 불행히도 수동으로 작성해야만 한다.

작성 예를 보면 다음과 같다.
 

; ========================= test.inf ========================

; This .inf file will control the installation of the MFC test
; control. This control has been compiled with Visual C++ version 4.2.
; The FileVersion tags in the dependent DLLs section on this file
; reflect this requirement.

[version]
; version signature (same for both NT and Win95) do not remove
signature="$CHICAGO$"
AdvancedINF=2.0

[Add.Code]
test.ocx=test.ocx
; These are the necessary supporting DLLs for MFC 4.2 ActiveX Controls
mfc42.dll=mfc42.dll
msvcrt.dll=msvcrt.dll
olepro32.dll=olepro32.dll

; dependent DLLs
[msvcrt.dll]
; This is an example of conditional hook. The hook only gets processed
; if msvcrt.dll of the specified version is absent on client machine.
FileVersion=6,0,8168,0
hook=mfc42installer

[mfc42.dll]
FileVersion=6,0,8168,0
hook=mfc42installer

[olepro32.dll]
FileVersion=5,0,4261,0
hook=mfc42installer

[mfc42installer]
file-win32-x86=<LINK TYPE="GENERIC" VALUE="http://activex.microsoft.com
/controls/vc/mfc42.cab">http://activex.microsoft.com/controls/vc
/mfc42.cab</LINK>
; If dependent DLLs are packaged directly into the above cabinet file
; along with an .inf file, specify that .inf file to run as follows:
;InfFile=mfc42.inf
; The mfc42.cab file actually contains a self extracting executable.
; In this case we specify a run= command.
run=%EXTRACT_DIR%\mfc42.exe

; thiscab is a keyword which, in this case, means that test.ocx
; can be found in the same .cab file as this .inf file
; file-win32-x86 is an x86 platform specific identifier
; See the ActiveX SDK - ActiveX Controls - Internet Component Download -
; Packaging component code for automatic download

[test.ocx]
file-win32-x86=thiscab
; *** add your controls CLSID here ***
clsid={0D886696-C7CE-11D3-A175-08002BF17507}
; Add your ocx's file version here.
FileVersion=1,0,0,1
RegisterServer=yes

위의 내용 중 test를 자신이 작성한 ActiveX 컨트롤의 이름으로 변경하고 중간에서 약간 위의 clsid={0D886696-C7CE-11D3-A175-08002BF17507}를 자신이 작성한 컨트롤의 clsid로 수정하면(clsid는 ActiveX 컨트롤을 만든 디렉토리의 .odl 파일을 열어 가장 마지막에 나와 있는 id를 입력하면 된다)보면 .inf 파일 작성은 완료된다.

 참고로 inf 파일의 내용을 대강 정리하면 다음과 같다.

세미콜론(;) : 주석문을 표시한다.

[Add.Code] : 설치할 파일들을 나열한다.

test.ocx=test.ocx
mfc42.dll=mfc42.dll
msvcrt.dll=msvcrt.dll
olepro32.dll=olepro32.dll

즉 test.ocx, mfc42.dll, msvcrt.dll, olepro32.dll을 설치한다는 의미이다. 그다음 부터는 [Add.Code]에서 설정한 파일들의 속성을 설정한다.

[test.ocx]
file-win32-x86=thiscab
clsid={0D886696-C7CE-11D3-A175-08002BF17507}
FileVersion=1,0,0,1
RegisterServer=yes

clsid는 작성한 OCX 파일의 CLSID를 입력한다.

FileVersion은 작성하] OCX 파일의 버전을 입력(Resource에서 설정한 값과 같이 입력)한다. 설명상 FileVersion은 ActiveX 파일을 작성하여 배포하던 중 버그를 발견하여 수정하여 재 배포를 할 때 이 파일 버전을 높여 주면 Client에서 알아서 다시 다운로드를 받는다 라고 되어 있었는데 필자가 테스트 해보니 잘 되지 않았다. 테스트 해보기 바란다.

나머지 파일들은 Visual C++로 ActiveX 프로그램을 작성했을 때 일반적으로 필요한 dll 파일들이다. 만약 같은 버전의 Visual C++이 설치되어 있으면 설치 되지 않고 그렇지 않으면 설치된다.

[msvcrt.dll]
FileVersion=6,0,8168,0
hook=mfc42installer

FileVersion은 설치될 dll의 버전이다. 현재 inf 설명에 적어 놓은 버전은 Visual C++ 6.0으로 프로그램 했을 경우 버전들이다.

hook은 설치할 방법을 설정하는 부분의 Title을 입력한다.

[mfc42installer]
file-win32-x86=<LINK TYPE="GENERIC" VALUE="http://activex.microsoft.com
/controls/vc/mfc42.cab">http://activex.microsoft.com/controls/vc
/mfc42.cab</LINK>
run=%EXTRACT_DIR%\mfc42.exe

http://activex.microsoft.com/controls/vc/mfc42.cab으로부터 다운로드하여 부가적 dll을 설치하도록 설정한 것이다.

 

.cab 파일 생성

.cab 파일을 생성하기 위해서는 cabarc.exe라는 프로그램이 필요하다. 이 프로그램은 아래 다운로드에서 다운로드가 가능하다.

.cab 파일 생성 예를 들면, test.ocx라는 ActiveX 컨트롤을 제작하였고 test.inf를 작성하였다면 test.cab 파일은 도스창에서 다음과 같이 입력하면 만들어 진다.

    cabarc.exe N test.cab test.ocx test.inf

제공하는 파일(아래 다운로드에서 .zip 파일을 받아 압축을 푼 파일)에서 step1.bat를 실행하면 위 과정이 실행된다. 물론 step1.bat를 열어 test를 제작한 ActiveX 컨트롤 이름으로 변경하고 그 .ocx파일과 .inf 파일을 제공하는 파일과 같은 위치에 복사한 후 step1.bat를 실행해야 한다.

 

.cab 파일 서명

명령 프롬프트에서 다음 명령줄을 입력하면 다음과 같은 대화 상자가 생성되어 암호를 입력하면 개인 키 파일 mycert.pvk 및 mycert.cer이 생성된다.


makecert -sv "mycert.pvk" -n "CN=JYS ActiveX" mycert.cer

명령 프롬프트에서 다음 명령줄을 입력하면 mycert.cer로부터 mycert.spc 파일을 생성한다.

cert2spc mycert.cer mycert.spc

여기 까지 과정은 한번만 수행하면 된다. 제공하는 파일에서 makespc.bat를 실행하면 위 과정이 실행된다.

 

명령 프롬프트에서 다음 명령줄을 입력하면 위의 개인 키 암호 입력 대화 상자가 생성되고 위 과정에서 입력한 암호와 같은 암호를 입력하면 .cab 파일에 서명한다.

signcode -v mycert.pvk -spc mycert.spc test.cab

위와 같이 입력하면 테스트용으로 .cab 파일을 인증한다. 만약 인증 기관에서 제대로된 인증을 받으려면 위 명령줄에 -t 인증기관 url을 추가해야 한다. 그러나 인증 기관에서 인증을 받으려면 돈을 내야하고 테스트용 인증은 단지 웹 페이지를 처음 열었을 때 대화 상자 하나 더 뜬다는 것만 다르므로 제대로된 인증을 받을지는 여러분이 판단하기 바란다.

명령 프롬프트에서 다음 명령줄을 입력하면 하면 테스트 인증서가 인식되도록 클라이언트 시스템에서 TRUE 값으로 Setreg.exe가 실행된다.

setreg -q 1 TRUE

Checktrust.exe를 실행하여 CAB 파일이 올바르게 서명되었는지 확인한다.

chktrust test.cab

제공하는 파일에서 step2.bat를 열어 test를 제작한 ActiveX 컨트롤 이름으로 변경한 후 step2.bat를 실행하면 위 과정이 실행된다.

 

웹 페이지 등록

.cab파일을 웹 페이지에 등록하기 위해서는 OBJECT 태그의 CODEBASE를 사용한다. 다음은 test.ocx를 삽입한 html 파일의 예이다.
 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<base target="detail">
<title>TaskForce</title>
<meta name="generator" content="Namo WebEditor v3.0">
</head>

<body bgcolor="white" text="black" link="blue" vlink="purple" alink="red">

<p><object classid="CLSID:0D886696-C7CE-11D3-A175-08002BF17507" codebase="test.CAB#version=1,0,0,1"
 width="300" height="330" id="test">
</object>
 </p>
</body>

</html>

위에서 CLSID:0D886696-C7CE-11D3-A175-08002BF17507, test.cab, version=1,0,0,1을 자신이 만든 ActiveX 컨트롤에 맞추어 변경하면 된다. version은 작성한 ActiveX 컨트롤의 resource의 version을 확인하여 입력한다.

 

보안 설정

예전에는 인터넷 익스플로어가 인증 기관으로부터 인증을 받지 않은 ActiveX(서명 안된 ActiveX로 표현)를 확인에 의해 사용하도록 기본 세팅되어 있었는데, 요즘은 그 세팅이 사용안함으로 되어 있다. 따라서 인터넷 익스플로어에서 다음과 같이 설정을 해주어야만 ActiveX를 사용할 수 있다.

도구 메뉴 -> 인터넷 옵션 ->두 번째 탭 보안 -> 사용자 지정 수준 -> 서명 안된 ActiveX 컨트롤 다운로드(리스트의 아래쪽에 있음)를 사용 안함에서 확인으로 변경

 

필요한 실행파일 및 예제 다운로드

     

테트리스 제작

테트리스 게임하기

위 프로그램은 ActiveX를 이용하여 작성한 테트리스 프로그램이다. 알고리즘도 간단하고 하여 예제로 좋은 프로그램인 것 같다.

 

구현 알고리즘

프로그램 작성시 메모리 구조부터 잡아야 한다. 우선 16*26메모리를 배열로 잡고 위를 제외한 테두리를 0 이 아닌 값(여기서는 15)로 나머지를 0으로 채운다.

BYTE m_TetrisMem[26][16];

테트리스 단위 모양(4*4=16)(앞으로 종류라고 명칭한다) 7개를 위쪽 화살표 키를 누를 때 마다 달라지는 4가지 모양(앞으로 모양이라고 명칭한다)을 모두 배열로 잡는다.

static BYTE TetrisUnit[7][4][16];

현재 종류(m_nKind), 다음 종류(m_nNextKind), 모양(m_nShape), 위치(m_pointUnit)등을 변수로 잡는다.

종류 및 모양으로부터 4*4 메모리를 구하는 함수를 구현한다(GetTetrisData). 참고로 4*4 메모리 내용중 0 이 아닌 값을 일정 색상의 사각형으로 그리면 테트리스 단위 모양이 된다.

타이머를 이용하여 좌표를 1칸씩 아래로 이동한다(m_pointUnit.y를 1씩 증가).

이동할 위치로 좌표를 수정한 후(위치 좌표는 그대로 두고 변수 하나를 더잡아 수정)이동 가능한지를 체크한다(GetTetrisData로부터 구한 4*4메모리를 좌표만큼 이동하여 m_TetrisMem과 비교하여 둘 다 0 이 아닌 값이 하나라도 있으면 이동불가).

이동 가능하면 이동하고 이동 불가면 타이머에 의한 이동일 경우에는 멈추고(GetTetrisData로부터 구한 메모리 중 0 이 아닌 값을 m_TetrisMem에 쓴다), 이동 버턴에 의한 경우에는 아무 동작도 하지 않는다.

멈춘 후 m_nKind에 m_nNextKind내용을 넣고 m_nNextKind에는 랜덤함수를 이용하여 구하고, 모양 및 위치는 초기화한다.

프로그램을 웹상에 표시하기 위해서는 Html 파일이 필요하다(버튼등과 VB Script를 이용하여 연동하는 부분 Html 파일에 있음). 제공 프로그램을 다운 받아 압축을 풀면 test.htm이 있고, 이 파일을 Internet Explore에서 열면 된다. test.htm에는 ocx파일을 직접 삽입하여 지원한 파일이다. 따라서 다른 PC에서 실행하도록 하기 위해서는 Cab파일 작성법에서 설명한 대로 tetris.cab파일을 만들고 ocx 삽입 부분만 cab 삽입 형태로 바꾸면 된다.

나머지는 분석해 보기 바란다.

 

소스 프로그램 다운로드




ActiveX에서 FormView 사용

 Visual Basic은 기본 적인 화면체계가 Form을 두고 그 위에 컨트롤들(버튼, 리스트 박스, 콤보박스, 에디트 박스 등)을 배치하여 쉬운 I/F로 지원이 가능하도록 구성되어 있다. 그러나 Visual C++은 화면들을 OnDraw 함수에서 그려주는 형태가 기본적인 형태이다. 거기에 FormView와 Dialog Base 방식에 의해 Visual Basic과 거의 같은 I/F를 지원한다.

  그러나 ActiveX에서는 불행히도 FormView의 형태를 Application Wizard에서 지원하지 않는다(있을지도 모르지만 찾지 못했다). 따라서 여기에서는 ActiveX에서 수동으로 FormView를 삽입하여 FormaView형태를 지원하는 방식을 설명한다.

 

FormView 만들기

  FormaView 형태로 프로그램을 해본 분이라면 알 수 있겠지만 Application Wizard에서 프로젝트를 생성할 때 자동으로 대화 상자(FormView 형태, 일반 대화 상자와 조금 다름)와 그 대화 상자에 연결된 FormView가 자동으로 생성된다는 것을 알 수 있을 것이다. 그러나 여기에서는 수동으로 제작해야만 한다.

  먼저 Workspace에서 리소스 탭을 선택한 후 대화 상자를 하나 추가한다. 추가된 대화 상자의 Property를 선택(추가한 대화 상자 전체가 선택되게 한 후 마우스 오른쪽 버튼을 선택하여 Property선택)하면 다음과 같이 화면에 나타난다.


    그림과 같이 Style를 Child로 Border를 None로 설정한다(FormView 형태의 대화 상자를 만든다). 물론 General 탭을 선택하여 대화 상자 ID를 적당히 바꿔줘야 할 것이다.

추가된 대화 상자 전체가 선택되게 한 후 Class Wizard를 실행하면 다음과 같은 대화 상자가 생성된다.


OK를 선택하면 다음과 같은 대화 상자가 화면상에 나타난다.


    Name에 FormView Class 이름을 입력하고(CTestFormView라 입력), Base Class에 FormView를 선택한 후 확인을 선택하면 입력한 Class 파일이 생성된다.

 

FormView 삽입하기

  Ctrl(프로젝트 이름을 TestForm이라고 했을 경우 CTestFormCtrl)에서 WM_CREATE 메시지로부터 실행되는 OnCreate함수를 하나 생성하여 다음과 같이 코드를 추가한다.
 

int CTestFormCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
        if (COleControl::OnCreate(lpCreateStruct) == -1)
                return -1;
        
 
       CRuntimeClass* pViewClass = RUNTIME_CLASS(CTestFormView);

        m_pTestFormView = (CTestFormView*) pViewClass->CreateObject();
        if (m_pTestFormView == NULL)
        {
                TRACE1("Warning: Dynamic create of view type %Fs failed\n", pViewClass->m_lpszClassName);
                return -1;
        }
        CCreateContext context;
        context.m_pNewViewClass = pViewClass;
        context.m_pCurrentDoc = NULL;
        if (!m_pTestFormView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0, 0, 600, 200), this, AFX_IDW_PANE_FIRST, &context))
        {
                TRACE0("Warning: couldn't create view for frame\n");
                delete m_pTestFormView;
                return -1;
        }
 

        return 0;
}

TestFormCtrl.h에 CTestFormView *m_pTestFormView를 추가한다.

컴파일을 하면 에러가 발생한다. 이 에러들은 View가 수동으로 삽입되도록 설계되지 않은 것을 강제로 삽입하는 것이기 때문에 발생하는 에러이다. FromView에 2 가지 작업을 해주어야 한다.

1. protected로 선언되어 있는 생성자, 소멸자 함수를 public으로 선언한다.

2. Class Wizard를 실행하여 Create 함수를 생성한다.

이제 FormView Application을 제작하는 일반적인 방법으로 프로그램을 제작하면 된다.

FormView를 이용할 경우 버튼등 컨트롤과 I/F가 모두 ActiveX에 포함되므로 Method(외부에서 Access 가능한 함수), Property(외부에서 Access 가능한 변수)를 제작할 필요는 없다.

 

프로그램 소스 다운로드



출처 : http://rnd.vitzro.com/
728x90

댓글