4.Active X프로그램 만들기

본장은 Automation 에서 발전된 형태인 ActiveX 프로그램을 만들고 이프로그램을 이용하는 방법에 대해서 설명합니다.

마이크로 소프트사에서 제안하고 또한 많은 사람들이 좋다고 인정하는 ActiveX의 가장 일반적인 사용은 바로 OCX파일을 사용하는 방법과 OCX파일을 이용하는 두가지입니다. 인터넷 상에서 홈페이지를 작성할 때 OCX파일을 이용하고 또한 자신의 프로그램중 부족한 부분을 OCX파일을 사용하여 보충을 하며 자신만의 독특한 프로그램을 OCX로 사용을 하여 만들어 배포하자는 의미입니다. OCX파일을 구동하는 방법과  OCX파일을 만드는 방법 즉 ActiveX 컨트롤 프로그램을 만드는 방법을 본장에서 설명을 합니다. 만일 본편을 공부하는데 COM과 Automation과 OLE 삽입 등을 공부하는데 약간 어렵다고 느꼈다 하여도 OCX파일을 이용하는 방법과 ActiveX 컨트롤 프로그램을 만드는데는 어렵지 않다는 느낌을 가질수 있을 겁니다.

대상자: 초급,중급,고급

Chapter 등급: 필수

공부방법: 본장은 OLE중에서 꼭 알아야 할 항목입니다. 본장에서 작성하는 예제 2개를 모두 작성해 보시기 바랍니다.

1.ActiveX 컨트롤 개념파악

지금까지 OLE를 이용하여 클라이언트와 서버프로그램을 COM와 Automation을 이용하여 제작하여 보았습니다. COM의 불편함과 단점을 보안한것이 Automation이고 이 Automation을 보안한것이 ActiveX입니다. ActiveX는 OCX파일에서 발전된 형태로 보시면 됩니다. ActiveX는 확장자가 ocx이며 이프로그램은 Automation서버와 같은 형태로 다른 프로그램에서 메소드와 프로퍼티를 이용하여 호출한는 형태입니다. ActiveX는 Automation에서 사용하였던 GetProperty와 SetProperty 그리고 InvokeHelper 3개의 함수를 이용하는것은 같습니다. Automation에서는 GetProperty와 SetProperty 등을 직접 작업을 하는 반면 ActiveX는 이것이 자동적으로 되게 됩니다. 하나의 예를 들어서 설명을 해보겠습니다. bmp파일을 출력하는 draw.ocx라는 파일이 있으며 이프로그램이  ActiveX이라고 가정을 하였을경우  이 프로그램을 이용하고자 할경우의 전체 내용은 그림 4-4-1과 같은 형태입니다.

(그림 파일)

(그림 4-4-1)ActiveX 이용하기

그림 4-4-1은 draw.ocx가 있을경우 이 ActiveX를 이용할때 함수의 변화 형태를 그림으로 보인것입니다. draw.ocx에는 그림 4-4-1의 ①항목처럼 로드할 그림 파일의 파일명을 설정하는 file변수와 화면에 출력할 x,y 좌표를 저장하는 변수가 있습니다. 그리고 이변수들을 이용하여  그림을 출력하는 DrawImage라는 함수가 있습니다. 그림을 출력할때 가장기본적인 3개의 변수와 1개의 함수가 있는 형태입니다. Automation을 이용한다면 3개의 변수와 1개의 함수를 그림 4-4-1의 ②항목과 같이 GetProperty와 SetProperty함수를 이용하여 접근을 합니다. ActiveX에는 이 함수를 이용하여 클라이언트가 이용하고자 하는 함수명까지도 포함을 하고 있습니다. 즉 ②항목과 함께 ②항목을 이용하여 작성하는 ③항목의  함수까지도 포함되고 있는 것입니다. 이렇게 포함하고 있기 때문에 AcitveX를 로드하고자 할때는 단순하게 ③항목의 함수만 이용함으로써 ActiveX 서버프로그램에 접근을 할수가 있습니다. 프로그램의 조립의 완벽한 개념으로 가자는 의미입니다.  여기에서 조금 다른 이야기로 자바이야기를 해보겠습니다. 자바가 인터넷에서 인기를 얻고 있는 가장큰 특징중에 하나는 프로그램이 어느 OS에서든지 작동이 가능하다는 것입니다. 그리고 또하나 주목할점이 자바 프로그램은 실행파일을 만들때 기존에 사용된 라이브러리 클래스는 재컴파일하지 않고 바로 연계하여 사용한다는 것입니다. 쉽게 말해서 인터넷상에서 필요한 클래스들이 조립되어 구동된다는 의미입니다. 현대는 단순하게 한개의 컴퓨터에서 프로그램이 구동되는 시대가 아니라 네트워킹을 통해서 여러 컴퓨터에서 필요한 내용들이 구동되고 서로의 결과를 공유하는 형태입니다. 이런 환경에서의 안성맞춤 격이 자바라는 언어라는 인식이 점차 많은 사람들이 가지게 되었고 이때문에 자바가 급격하게 발전하게 된것입니다. 이것과 ActiveX와 비교를 해보겠습니다. ActiveX 는 어느 컴퓨터에서 든지 구동되는 것이 아니라 마이크로 소프트사에서 제작한 OS, 즉 Windows98 또는 WindowsNT에서 는 구동됩니다. 이부분에서는 자바와 차이점이 있습니다. 그러나 조립되어 구동된다는 면에서는 자바와 비슷합니다. 마이크로 소프트사의 운영체제만 이용한다면 자바와 ActiveX와는 별차이가 없게 되는 것입니다. 두번째로 자바는 프로그램이 실행되고자 한다면 언제나 서버에서 자바 프로그램을 다운 받은다음 실행해야 합니다. 반면에 ActiveX는 한번 다운 받은 다음 자신의 컴퓨터에서 실행파일이 존재하면 바로 구동이 됩니다. 마이크로 소프트사의 운영체제에서만 본다면 ActiveX 가 편합니다. 또한 ActiveX는 시스템과 ODBC와 아주 편리하게 연결됩니다. 이렇기 때문에 인트라넷, 또는 그룹웨어에서는 HTTP 서버를 이용하고 해당 프로그램을 모두 ActiveX로 사용하고 운영체제를 NT와 Windows98 또는 95를 이용하는 경우가 매우 많습니다.  데이터베이스 연결이 원할하며 ,분산처리 시스템을 원할히 할수 있으며, 조립식 프로그램 기법이 될수 있기 때문에 그룹웨어가 이런 형태로 움직이여 가고 있습니다. ActiveX가 좋다는 의미가 아닙니다. 필자는 ActiveX와 자바 둘다 모두를 사용할줄 알고 두부분 다 이용하기 때문에 어느 한쪽을 편들지는 않습니다.(필자가 쓴 Internet Programming Bible에는 자바에 치중을 더했습니다.)

다만 이책을 보시는 분들중 그룹웨어 프로그램을 제작하거나 또는 그런 회사에서 종사할 가능성이 있을경우에는 ActiveX를 중히 다루어야 한다는 것을 알려드리고 싶을 뿐입니다.  그리고 ActiveX는 바로 COM에서 Automation으로 변화되고 그리고 최후 종점이 된것이라는 것을 설명하고자 하는 것입니다. 이제 ActiveX를 직접 제작해 보면서 그림 4-4-1의 내용을  확인해 보겠습니다.

2.ActiveX 컨트롤 예제 ExActiveX

File메뉴에서 New를 선택하고  그림 4-4-2과 같이 MFC ActiveX ControlWizard를 선택합니다.

(그림 4-4-2)ActiveX 프로젝트 만들기

지금까지 프로그램을 만들경우에는 MFC AppWizard 를 이용하였으나 ActiveX 를 이용할때는 그림 4-4-2와 같이 MFC ActiveX ControlWizard 를 선택하여야 합니다. 이렇게 선택한후에 프로젝트 명을 설정하고 OK버튼을 클릭하게 되면 그림 4-4-3과 같은 화면이 나타납니다.

(그림 4-4-2)ActiveX ControlWizard Step1

첫 번째 항목은 얼마나 많은 컨트롤을 본 프로젝트에서 포함을 할것인가를 물어보는 것입니다. 전에 말씀 드렸듯이 한 개의 프로젝트에 여러개의 컨트롤 클래스를 만들 수 있다고 하였습니다. 본예제는 ActiveX Control를 쉽게 만들기 위해서 1개를 설정하였습니다. 두 번째 항목은 컨트롤에 라이센스 번호를 설정할것인가를 물어보는 것입니다. 상업용으로 만들 경우 이 번호를 만들어 놓으면 이 OCX파일을 이용하기 위해서는 번호를 알아야 하겠지요. 그러나 본 예제는 상업용이 아닌 예제이므로 NO항목을 선택합니다. 그다음 항목은 소스에 주석을 설정할것인가 입니다. 물론 설정하는 것이 좋겠지요. 다음 항목은 도움말 파일을 작성할것인가 입니다. 도움말을 만들고자 한다면 Yes를 선택하시면 됩니다. 도움말 만들기는 전 PART를 참조하세요.

Next버튼을 클릭하시면 그림 4-4-3과 같은 화면이 나타납니다.

(그림 4-4-3)ActiveX ControlWizard Step2

첫 번째 항목은 컨트롤 이름을 설정하는 것이고 두 번째는 본 ActiveX의 옵션입니다.

Activates when visible 란 본 프로그램이 설정되면 활성화 된다는 것이고

Invisible at runtime란 프로그램을 설정하면 화면에 나타나지 않는다는 것이고

Available in "Insert Object" dialog 란 객체삽입 창에서 사용가능하게 하는 것입니다.

Has an "About" box란 본프로그램에 대한 대화 상자를 가지고 있을것인가를 선택하는 것입니다.

Acts as a simple frame control은 말그대로 simple frame control로써 행동하는 것입니다. 각 옵션을 선택하면 좌측 화면이 바뀝니다. 실제 한 번 클릭해보면서 화면의 변화를 확인해 보세요. 다음 항목은 본 OCX가 어떤 윈도 클래스의 서브 클래스로 움직인다면 그 클래스를 설정하라는 것입니다. 그림 4-4-3과 같이 설정한후에 Finish를 클릭하면 프로젝트가 만들어 집니다.

<1>메소드와 프로퍼티

ActiveX와 OLE를 설명하는데 가장 기본이 되는 또한 가장 중요한 것이 바로 메소드와 프로퍼티입니다. 본편에서 계속적으로 이 내용을 설명하는데 또다시 반복을 하니 읽는 독자 여러분들 짜증이 나겠지만 사실 이것 빼면 ActiveX는 앙꼬 없는 찐빵이며 귀신 없는 만득이입니다. 그만큼 중요 하기 때문에 설명을 합니다.

그림 4-4-2과 같이 하여 프로젝트를 만들면 CExActiveXApp ,CExActiveXCtrl,CExActiveXPropPage 3개의 클래스가 만들어 집니다. CExActiveXApp란 바로 어플리케이션 클래스라고 보면 됩니다. 다음 CExActiveXCtrl이 바로 본 프로젝트의 컨트롤 클래스입니다. Ctrl를 컨트롤 클래스라고 하는데 이런 컨트롤 클래스들이 한 OCX파일안에 여러개가 등록될수 있습니다. 본장에서 설명하는 내용에서는 단지 한 개의 컨트롤을 사용하지만 여러개의 컨트롤을 사용할수 있다는 것이지요. CExActiveXPropPage란 이런 여러개의 컨트롤에서 사용되는 프로퍼티들을 상호 교한하는 데 사용하는 클래스입니다. 한 개의 컨트롤만 사용한다면 그 컨트롤에 해당하는 데이터만 있으면 되기 때문에 CExActiveXPropPage는 거히 무용지물이 됩니다. 쉽게 보면 일반 프로그램에서 도큐먼트 클래스와 같은 기능을 가지고 있다는 것이지요. 간단한 OCX들은 보통 한 개의 컨트롤 클래스를 가지고 있으나 복잡한 것들은 여러개를 가지고 있습니다. 본장에서는 여러분이 쉽게 ActiveX컨트롤 프로그램을 제작할수 있도 한 개의 컨트롤 을 사용하는 방법에 대해서 설명을 합니다. 결국 가장 중요한 클래스는 CExActiveXCtrl클래스인데 여기에서 우리가 필요한 변수의 설정과 함수의 설정이 다릅니다. 기존의 일반 어플리케이션 프로그램을 만들경우에는 헤더에 변수를 설정하고 바로 그 내용을 그 클래스 안에서 사용을 하였으나 ActiveX 컨트롤 클래스는 자신이 구동되는 프로그램이 아니라 다른 프로그램에서 호출하여 사용되는 프로그램이기에 변수를 설정할 때 내부적으로 사용하는 명과 외부적으로 사용하는 명 두 개를 가지고 있어야 합니다. 즉 프로퍼티를 명명하는데 있어서 두 개의 명을 가지고 있어야 한다는 것이지요. 제동생은 집에서 별명이 짱구 였습니다. 그래서 집에서는 짱구로 통하지요. 언제나 제동생을 부를 때 집에서는 짱구야! 이렇게 부릅니다. 그런데 밖에서는 본이름이 상권이라고 부릅니다. 전화가 오면 □저 이상권씨 계세요?□ 이렇게 하면 □아 짱구요 잠시만요 짱구야! 전화왔다!□ 이렇게 되는 것이지요. 즉 밖에서 OCX안에 있는 프로퍼티를 사용하기 위해서 호출하는 이름과 내부적으로 사용하는 이름이 다르다는 것입니다.  메소드도 마찬가지 입니다. 내부적으로 사용되는 메소드와 밖에서 호출되는 메소드가 다른것이지요. 그렇기 때문에 하나의 변수와 하나의 함수 다시 말해서 하나의 프로퍼티와 하나의 메소드가 내부적으로 사용하는 명과 외부적으로 사용하는명 두 개가 있는 것입니다. 결국 ActiveX와 일반 어플리케이션 프로그램의 가장 큰차이는 선언시에 두 개의 이름을 사용하는 것입니다. 프로그램을 작성할 때 한 개의 변수를 설정할때도 복잡하고 지겨워 죽겠는데 두 개를 설정하자니 얼마나 복잡하겠습니까? 만일 프로퍼티가 10개 메소드가 20개이면 결국 60개의 이름을 설정하는데 정말로 복잡하게 되는 것이지요. 이런 문제점 때문에 어쩌면 국내에서 ActiveX가 어렵게 느껴질수도 있을지 모르겠습니다. ActiveX Control Wizard를 사용하여 프로젝트를 만들면 ClassWizard를 이용하여 이렇게 변수 등록을 아주 편리하고 쉽게 하여 줍니다. MFC ClassWizard를 실행하고 카드철에서 Automation을 선택하고 컨트롤 클래스를 선택하면 그림 4-4-4와 같이 나타납니다.

(그림 4-4-4)ClassWizard에서 Automation선택

그림 4-4-4에서 Add  Property를 선택하면 그림 4-4-5과 같은 화면이 나타납니다.

(그림 4-4-5)Add Property 선택시 화면

첫 번째 입력란은 External name 로 설정되어 있을 것입니다. 즉 외부적으로 사용할 이름을 설정하라는 것이빈다. 외부 이름을 그림3 과 같이 LeftPos라고 설정을 하면 Variable name칸에는 앞에 □m_□ 이 삽입 되면서 써질것입니다. 여러분이 만일 내부이름을 자신이 원하는 형으로 바꾸고자 한다면 수정을 하여도 됩니다. 두 번째 입력란은 이 프로퍼티의 형이 무엇인가를 설정하여 주는 것입니다. 그림 3에서는 short로 설정하였습니다. 다음은 Implementation 이라고 항목이 있습니다. Member variable란 그냥 이 멤버만 설정하여 준다는 것입니다. 문제는 바로 그다음 항목

Get/Set methods입니다. 이것은 바로 프로퍼티를 설정하는 외부 함수를 자동으로 만들어 지면서 멤버를 설정한다는 내용입니다. ActiveX는 완벽한 객체지향을 하기를 바랍니다. 그렇기 때문에 프로퍼티는 결코 밖에서 바로 사용을 할 수가 없습니다. 즉 어떤 함수를 이용하여 프로퍼티를 사용하여야 합니다. 위와 같이 LeftPos라는 멤버가 있다고 한다면 에 프로퍼티에 데이터를 입력하고자 할 경우 또는 이 프로퍼티에서 데이터를 얻고자 할 경우 바로 프로퍼티를 건드리는 것이 아니라 어떤 함수를 이용하자는 것이지요. 그래서 Get/Set methods를 선택하면 한 개의 프로퍼티가 등록되면서 이에 해당하는 함수가 동시적으로 두 개가 만들어 집니다. 예를 들어서 LeftPos 란 프로퍼티를 Get/Set methods를 사용한다면

GetLeftPos 라는 함수와 SetLeftPos 라는 두 개의 함수가 만들어 진다는 것이지요.

short GetLeftPos();

void SetLeftPos(short);

GetLeftPos는 인자가 없고 앞에 형이 short인것과 SetLeftPos는 형이 void이고 인자가 short라는 것은 아마 이해가 될것입니다. 프로퍼티에 데이터를 설정하고자 하는 것이 바로 SetLeftPos이고 프로퍼티 값을 얻고자 하는 것이 바로 GetLeftPos이니까 설정은 설정값을 주고 얻고자 하는 것은 리턴값을 받아야 하니까요. 일반적으로 Get/Set methods방법을 사용하는 것이 좋습니다. 이유는 이런게 만들지 않아도 후에 ActiveX 컨트롤를 사용하는 곳에서 OCX를 이용하여 컨트롤 하는 클래스를 생성하면 자동적으로 이함수들을 만들기 때문입니다. 만일 이렇게 하지않는다면 어떤 문제가 발생하는지도 본장의 예제를 이용하여 설명을 하겠습니다.

프로퍼티를 위와 같이 등록을 하고 나면 컨트롤 클래스 즉 본편의 예제에서 CExActiveXCtrl의 헤더에서는 다음과 같이 설정됩니다.

short m_leftPos;

afx_msg void OnLeftPosChanged();

Get/Set methods를 사용하지 않았기 때문에 m_leftPos가 설정되고 그다음 voie OnleftPosChanged();라는 함수가 설정됩니다. 즉 프로퍼티의 변수값이 변할 때 발생되는 어떤 문제를 해결하기 위한 함수까지 만들어 진것이지요. 소스에서는 위와 대응되도록 다음과 같은 것을이 설정됩니다.

BEGIN_DISPATCH_MAP(CExActiveXCtrl, COleControl)

//{{AFX_DISPATCH_MAP(CExActiveXCtrl)

DISP_PROPERTY_NOTIFY(CExActiveXCtrl, "LeftPos", m_leftPos, OnLeftPosChanged,	VT_I2)

//}}AFX_DISPATCH_MAP

DISP_FUNCTION_ID(CExActiveXCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox,	VT_EMPTY, VTS_NONE)

END_DISPATCH_MAP()

BEGIN_DISPATCH_MAP란 바로 컨트롤 클래스에서 사용되는 내부적 이름과 외부적이름 그리고 이 프로퍼티가 변했을 때 발생하는 내용을 기록할 함수 그리고 이 프로퍼티의 형을 써줍니다. 이안에 DISP_PROPERTY_NOTIFY 라는 함수에 설정되어 있는 부분을 보시면 이해가 되실것입니다. 위와 같이 설정된 다음 소스에는 OnLeftPosChanged함수가 다음과 같이 설정됩니다.

void CExActiveXCtrl::OnLeftPosChanged()

{

// TODO: Add notification handler code

SetModifiedFlag();

}

메소드는 그림 4-4-4에서 Add Methods버튼을 클릭하였을 때 그림 4-4-6와 같은 화면이 나타나면서 설정합니다.

(그림 4-4-6)Add Methods 선택화면

처음 입력란의 External name란 외부에서 사용할 이름이고 Internal name란 내부에서 사용할 이름입니다. Return type란 이함수의 형을 이야기 하는 것이지요. Parameter List란 바로 이함수의 인자를 이야기 합니다. 즉 다음과 같은 함수를 만들때는 그림 4와 같이 설정을 하는 것입니다.

void SetDrawRect(short left,short top,short right,short bottom);

위와 같이 입력하면 헤더에는 다음과 같이 설정됩니다.

afx_msg void SetDrawRect(short left, short top, short right, short bottom);

소스의 BEGIN_DISPATCH_MAP 에는 다음과 같이 설정되고

DISP_FUNCTION(CExActiveXCtrl, "SetDrawRect", SetDrawRect, VT_EMPTY,	VTS_I2 VTS_I2 VTS_I2 VTS_I2)

함수가 다음과 같이 만들어 집니다.

void CExActiveXCtrl::SetDrawRect(short left, short top, short right, short bottom)

{

// TODO: Add your dispatch handler code here

}

이와 같이 ActiveX ControlWizard를 이용하여 프로젝트를 만들고 ClassWizard를 이용하여 프로퍼티와 메소드를 컨트롤 클래스에 설정할수 있습니다.

<2>이벤트 만들기

프로퍼티와 메소드만 가지고 모든 것을 할수 있다고 하여도 문제는 있습니다. 어떤 이벤트가 발생하였을때의 문제입니다. 한가지 예를 들어 보겠습니다. 만득이와 귀신 이야기를 들어 보지요. 귀신에게는 만득이를 쫒아 다니는 메소드와 무섭게 생긴 프로퍼티를 가지고 오직 하나뿐인 메소드 만득이 쫒아 다니기를 했다고 합시다. 오늘도 마찬가지로 만득이를 쫒아 다니는데 시간이 흘러서 날이 밝고 해가 뜨기 시작했습니다. 해가 뜨면 귀신은 얼른 귀신본부로 돌아가야 합니다. 그런데 날이 밝았다는 것은 메소드도 아니고 프로퍼티도 아닙니다. 메소드란 귀신 자신이 행동하는 것이고 프로퍼티란 귀신에게 가지고 있는 자원이라고 할수 있는데 날이 밝았다는 것은 이 두 개에 해당되지 않는 것이지요. 이것이 바로 이벤트입니다. 이벤트는 내부적으로 외부적으로 동시에 사용할수 있습니다. 그렇기 때문에 두 개의 명을 설정하는 것은 메소드와 프로퍼티와 갖지요. 이런 이벤트또한 ClassWizard로 만들 수 있습니다. 그림 4-4-4에서 ActiveX event를 선택하고 Add Event를 클릭하였을 때 그림 4-4-7와 같은 대화 상자가 나타납니다.

(그림 4-4-7)Add Event 선택시 화면

External name란 외부적으로 사용하는 명입니다. 이곳에 ChangeStringMsg라고 설정하였을 때 내부적으로는 FireChangeStringMsg로 설정됩니다. 즉 어떤 이벤트가 발생되었다는 의미이지요. 이와 같이 함수를 만들면 헤더에는 다음과 같이 설정이 됩니다.

void FireChangeStringMsg();

{FireEvent(eventidChangeStringMsg,EVENT_PARAM(VTS_NONE));}

위와 같이 이벤트가 설정되면서 이 이벤트는 바로 COleControl 클래스의 멤버 함수 FireEvent가 수행되도록 헤더에 함수가 바로 설정됩니다. 실제로 이벤트를 상위 클래스에서 자동으로 수행하는 경우는 거히 없습니다. 그렇기 때문에 보통 위의 함수에서 두 번째 라인을 삭제하고 소스에서 만들어 줍니다.

소스에는 위의 함수에 해당하는 것이 다음과 같이 설정됩니다.

BEGIN_EVENT_MAP(CExActiveXCtrl, COleControl)

//{{AFX_EVENT_MAP(CExActiveXCtrl)

EVENT_CUSTOM("ChangeStringMsg", FireChangeStringMsg, VTS_NONE)

//}}AFX_EVENT_MAP

END_EVENT_MAP()

즉 이벤트 맵핑에 이벤트 내용을 설정한 것입니다.

위와 같이 하여 특정 이벤트가 발생하였을 때 해당되는 함수를 만들수가 있습니다.

<3>함수 작성하기

ExActiveX프로그램은 화면에 "ActiveX 테스트" 란 글자가 출력되고 박스가 그려집니다. 그리기 스타일을 바꾸면 박스가 원이되고 5초후에는 글자가 "시간이 되었음" 으로 바뀌게 되는 내용입니다. 아주 간단한 ActiveX 컨트롤 입니다.

본프로그램을 만들기 위해서는 프로퍼티가 5개가 필요합니다. 이프로퍼티의 변수와 형과 내용은 표 1과 같습니다.

(표1) 프로퍼티 리스트

내부 명	외부 명	형	내용
m_leftPos	LeftPos	short	도형의 화면 좌측 위치
m_rightPos	RightPos	short	도형의 화면 우측 위치
m_topPos	TopPos	short	도형의 상단 위치
m_bottomPos	BottomPos	short	도형의 하단 위치
m_drawStyle	DrawStyle	short	도형의 그리기 스타일 0일 경우 박스 1일 경우 원을 그린다.

ClassWizard를 이용하여 전에 설명한 방법으로 프로퍼티를 만듭니다.

메소드는 표2와 같습니다.

(표2) 메소스 리스트

내부 명	외부 명	리턴 형	인자	내용
SetDrawRect	SetDrawRect	void	short,short, short,short	도형의 좌측,상단, 우측,하단 값 설정
SetDrawStyle	SetDrawStyle	void	short	그리기 스타일 설정
GetDrawStyle	GetDrawStyle	short	없음	그리기 스타일값 얻기

ClassWizard를 이용하여 전에 설명한 벙법으로 메소드를 만듭니다.

만약 프로퍼터 m_drawStyle를  만들 때 그림 3과 같은 화면에서 Get/Set Methods를 설정하고 프로퍼티를 만들었다면 SetDrawStyle와 GetDrawStyle는 자동적으로 만들어 집니다. 그렇지 않았을경우에는 호출시에 문제가 생깁니다. 일단 Get/Set Methods를 선택하지 않았다고 가정을 하고 표2와 같이 만듭니다.

이벤트는 전에 설명한 방법대로 FireChangeStringMsg 를 만듭니다. 만들고 난뒤에

헤더에서 다음과 같이 해줍니다.

void FireChangeStringMsg();

//{FireEvent(eventidChangeStringMsg,EVENT_PARAM(VTS_NONE));}

즉 함수의 몸통 즉 {FireEvent... 앞에 □//□ 주석 마크를 하여 단순히 함수만 설정하게 합니다. 그리고 이함수를 소스에 제작합니다.

제일먼저 본 클래스가 생성될 때 즉 생성자 함수를 다음과 같이 설정합니다.

CExActiveXCtrl::CExActiveXCtrl()

{

InitializeIIDs(&IID_DExActiveX, &IID_DExActiveXEvents);

//도형의 위치를 0,0,100,100으로 설정한다.

m_leftPos=0;

m_rightPos=100;

m_topPos=0;

m_bottomPos=100;

//그리기 스타일을 0으로 즉 처음에는 박스로

m_drawStyle=0;

//화면출력 글자 설정

m_strData="ActiveX 테스트";

// TODO: Initialize your control's instance data here.

}

즉 화면 그릴 도형의 좌표를 설정하고 그리기 스타일을 박스로 설정한후에 화면에 출력할 글자를 m_strData에 설정합니다. 여기서 m_strData는 프로퍼티로 만들지 않았습니다. 헤더에는 그냥 CString m_strData로 설정했습니다.

전에 변수를 모두 프로퍼티로 만들어야 한다고 해놓고 왜 m_strData를 프로퍼티로 설정하지 않았을까요? m_strData는 외부에서 사용할 필요가 없는 변수입니다. 내부적으로만 사용을 하지요. 이런 변수를 구지 프로퍼티로 만들필요는 없지요.

이제 화면을 출력하는 함수를 만들겠습니다. 컨트롤 클래스를 만들면 자동적으로

OnDraw함수가 생성됩니다. 이곳에 코딩을 하면 됩니다.

void CExActiveXCtrl::OnDraw(

CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)

{

// TODO: Replace the following code with your own drawing code.

//택스트를 출력한다.

pdc->TextOut(rcBounds.left,rcBounds.top,m_strData);

//설정된 좌표를 CRect로 변환

CRect rect=CRect(rcBounds.left+m_leftPos,

rcBounds.top+m_topPos+20,

rcBounds.left+m_rightPos,

rcBounds.top+m_bottomPos+20);

//그리기 스타일에 따라서

switch(m_drawStyle)

{

//박스 그리기

case 0:

pdc->Rectangle(rect);

break;

//원 그리기

case 1:

pdc->Ellipse(rect);

break;

}

}

OnDraw함수가 기존의 어플리케이션과는 좀다릅니다. 이유는 간단합니다. 현 프로그램은 다른 프로그램의 클라이언트 영역의 특정 위치에 설정됩니다. 그렇기 때문에 특정 위치영역과 무효화 된 영역의 CRect값을 인자로 받아야 합니다. rcBounds가 바로 현 ActiveX 컨트롤이 설정된 위치이며 rcInvalid가 무효화된 영역입니다. 화면 출력시에는 rcBounds의 값을 기본 위치에 설정을 해주어야 합니다. 예를 들어 rcBounds를 100,100,200,200으로 하였고 그리는 영역을 0,0,20,20으로 설정을 하였다면 100,100,120,120 으로 설정하여 화면에 도시해야 합니다.

다음 본 컨트롤이 만들어질 때 5초의 타이머를 걸어둡니다. 이렇게 걸어두면 5초후에는 타이머 이벤트가 발생합니다. 타이머 이벤트가 발생하였을 때 m_strData 값을 □시간이 되었음□ 으로 설정하고 화면으 다시 출력하게 합니다.

//프로그램이 만들어지고 나서

int CExActiveXCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (COleControl::OnCreate(lpCreateStruct) == -1)

return -1;

//타이머를 설정한다.

SetTimer(1,5000,NULL);

// TODO: Add your specialized creation code here

return 0;

}

//타이머 발생시에

void CExActiveXCtrl::OnTimer(UINT nIDEvent)

{

//타이머를 해제하고

KillTimer(1);

//이벤트 함수 실행

FireChangeStringMsg();

COleControl::OnTimer(nIDEvent);

}

위에서 FireChangeStringMsg()를 그냥 단순하게 형으로 설정하였습니다. 그렇다면 이제 본함수를 소스에 코딩을 해야 합니다.

//이벤트 발생시

void CExActiveXCtrl::FireChangeStringMsg()

{

//문자를 바꾸고

m_strData="시간이 되었음";

//화면을 갱신

InvalidateControl();

}

화면을 갱신할 때 일반 어플리케이션 프로그램에서는 Invalidate()함수를 사용하였으나 ActiveX컨트롤 에서는 InvalidateControl();를 사용하는 것을 유념하세요.

이제 좌표를 설정하는 함수를 보겠습니다.

//그릴 좌표를 설정하는 함수

void CExActiveXCtrl::SetDrawRect(short left, short top, short right, short bottom)

{

// TODO: Add your dispatch handler code here

//좌표 설정

m_leftPos=left;

m_rightPos=right;

m_topPos=top;

m_bottomPos=bottom;

//화면 갱신

InvalidateControl();

}

이제 그리기 스타일을 설정하고 바꾸는 함수를 보겠습니다.

//그리기 스타일 설정

void CExActiveXCtrl::SetDrawStyle(short style)

{

// TODO: Add your dispatch handler code here

m_drawStyle=style;

InvalidateControl();

}

//그리기 스타일 얻기

short CExActiveXCtrl::GetDrawStyle()

{

// TODO: Add your dispatch handler code here

return m_drawStyle;

}

위와 같이 설정한후에 프로그램을 컴파일하고 링크하면 ExActiveX.OCX가 만들어집니다. 이출력 결과를 보기위해서는 Tools 메뉴에서 ActiveX Control Test Container 항목을 선택하고 프로그램이 실행되면 Edit 메뉴의 Insert Ole Control 항목을 선택하여 ExActiveX 를 설정하면 그림4-4-8와 같이 프로그램이 실행됩니다.

(그림 4-4-8)ExActiveX 실행결과

<4>프로그램 소스

다음은 ExActiveX에서 CExActiveXCtrl 클래스의 소스 부분입니다. 본내용은 CExActiveXCtrl클래스만 분석하시면 이해하실수 있습니다.

(프로그램 소스시작)

#if !defined(AFX_EXACTIVEXCTL_H__2C76B9E5_448A_11D2_8B9C_006097AD9DDC__INCLUDED_)

#define AFX_EXACTIVEXCTL_H__2C76B9E5_448A_11D2_8B9C_006097AD9DDC__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

// ExActiveXCtl.h : Declaration of the CExActiveXCtrl ActiveX Control class.

/////////////////////////////////////////////////////////////////////////////

// CExActiveXCtrl : See ExActiveXCtl.cpp for implementation.

class CExActiveXCtrl : public COleControl

{

DECLARE_DYNCREATE(CExActiveXCtrl)

// Constructor

public:

CExActiveXCtrl();

CString m_strData;

// Overrides

// ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CExActiveXCtrl)

public:

virtual void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid);

virtual void DoPropExchange(CPropExchange* pPX);

virtual void OnResetState();

//}}AFX_VIRTUAL

// Implementation

protected:

~CExActiveXCtrl();

DECLARE_OLECREATE_EX(CExActiveXCtrl)    // Class factory and guid

DECLARE_OLETYPELIB(CExActiveXCtrl)      // GetTypeInfo

DECLARE_PROPPAGEIDS(CExActiveXCtrl)     // Property page IDs

DECLARE_OLECTLTYPE(CExActiveXCtrl)	// Type name and misc status

// Message maps

//{{AFX_MSG(CExActiveXCtrl)

afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

afx_msg void OnTimer(UINT nIDEvent);

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

// Dispatch maps

//{{AFX_DISPATCH(CExActiveXCtrl)

short m_leftPos;

afx_msg void OnLeftPosChanged();

short m_rightPos;

afx_msg void OnRightPosChanged();

short m_bottomPos;

afx_msg void OnBottomPosChanged();

short m_topPos;

afx_msg void OnTopPosChanged();

short m_drawStyle;

afx_msg void OnDrawStyleChanged();

afx_msg void SetDrawRect(short left, short top, short right, short bottom);

afx_msg void SetDrawStyle(short dstyle);

afx_msg short GetDrawStyle();

//}}AFX_DISPATCH

DECLARE_DISPATCH_MAP()

// Event maps

//{{AFX_EVENT(CExActiveXCtrl)

void FireChangeStringMsg();

//	{FireEvent(eventidChangeStringMsg,EVENT_PARAM(VTS_NONE));}

//}}AFX_EVENT

DECLARE_EVENT_MAP()

// Dispatch and event IDs

public:

enum {

//{{AFX_DISP_ID(CExActiveXCtrl)

dispidLeftPos = 1L,

dispidRightPos = 2L,

dispidBottomPos = 3L,

dispidTopPos = 4L,

dispidDrawStyle = 5L,

dispidSetDrawRect = 6L,

dispidSetDrawStyle = 7L,

dispidGetDrawStyle = 8L,

eventidChangeStringMsg = 1L,

//}}AFX_DISP_ID

};

};

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_EXACTIVEXCTL_H__2C76B9E5_448A_11D2_8B9C_006097AD9DDC__INCLUDED)

// ExActiveXCtl.cpp : Implementation of the CExActiveXCtrl ActiveX Control class.

#include "stdafx.h"

#include "ExActiveX.h"

#include "ExActiveXCtl.h"

#include "ExActiveXPpg.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

IMPLEMENT_DYNCREATE(CExActiveXCtrl, COleControl)

/////////////////////////////////////////////////////////////////////////////

// Message map

BEGIN_MESSAGE_MAP(CExActiveXCtrl, COleControl)

//{{AFX_MSG_MAP(CExActiveXCtrl)

ON_WM_CREATE()

ON_WM_TIMER()

//}}AFX_MSG_MAP

ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

// Dispatch map

BEGIN_DISPATCH_MAP(CExActiveXCtrl, COleControl)

//{{AFX_DISPATCH_MAP(CExActiveXCtrl)

DISP_PROPERTY_NOTIFY(CExActiveXCtrl, "LeftPos", m_leftPos, OnLeftPosChanged, VT_I2)

DISP_PROPERTY_NOTIFY(CExActiveXCtrl, "RightPos", m_rightPos, OnRightPosChanged, VT_I2)

DISP_PROPERTY_NOTIFY(CExActiveXCtrl, "BottomPos", m_bottomPos, OnBottomPosChanged, VT_I2)

DISP_PROPERTY_NOTIFY(CExActiveXCtrl, "TopPos", m_topPos, OnTopPosChanged, VT_I2)

DISP_PROPERTY_NOTIFY(CExActiveXCtrl, "DrawStyle", m_drawStyle, OnDrawStyleChanged, VT_I2)

DISP_FUNCTION(CExActiveXCtrl, "SetDrawRect", SetDrawRect, VT_EMPTY, VTS_I2 VTS_I2 VTS_I2 VTS_I2)

DISP_FUNCTION(CExActiveXCtrl, "SetDrawStyle", SetDrawStyle, VT_EMPTY, VTS_I2)

DISP_FUNCTION(CExActiveXCtrl, "GetDrawStyle", GetDrawStyle, VT_I2, VTS_NONE)

//}}AFX_DISPATCH_MAP

END_DISPATCH_MAP()

/////////////////////////////////////////////////////////////////////////////

// Event map

BEGIN_EVENT_MAP(CExActiveXCtrl, COleControl)

//{{AFX_EVENT_MAP(CExActiveXCtrl)

EVENT_CUSTOM("ChangeStringMsg", FireChangeStringMsg, VTS_NONE)

//}}AFX_EVENT_MAP

END_EVENT_MAP()

/////////////////////////////////////////////////////////////////////////////

// Property pages

// TODO: Add more property pages as needed.  Remember to increase the count!

BEGIN_PROPPAGEIDS(CExActiveXCtrl, 1)

PROPPAGEID(CExActiveXPropPage::guid)

END_PROPPAGEIDS(CExActiveXCtrl)

/////////////////////////////////////////////////////////////////////////////

// Initialize class factory and guid

IMPLEMENT_OLECREATE_EX(CExActiveXCtrl, "EXACTIVEX.ExActiveXCtrl.1",

0x2c76b9d8, 0x448a, 0x11d2, 0x8b, 0x9c, 0, 0x60, 0x97, 0xad, 0x9d, 0xdc)

/////////////////////////////////////////////////////////////////////////////

// Type library ID and version

IMPLEMENT_OLETYPELIB(CExActiveXCtrl, _tlid, _wVerMajor, _wVerMinor)

/////////////////////////////////////////////////////////////////////////////

// Interface IDs

const IID BASED_CODE IID_DExActiveX =

{ 0x2c76b9d6, 0x448a, 0x11d2, { 0x8b, 0x9c, 0, 0x60, 0x97, 0xad, 0x9d, 0xdc } };

const IID BASED_CODE IID_DExActiveXEvents =

{ 0x2c76b9d7, 0x448a, 0x11d2, { 0x8b, 0x9c, 0, 0x60, 0x97, 0xad, 0x9d, 0xdc } };

/////////////////////////////////////////////////////////////////////////////

// Control type information

static const DWORD BASED_CODE _dwExActiveXOleMisc =

OLEMISC_ACTIVATEWHENVISIBLE |

OLEMISC_SETCLIENTSITEFIRST |

OLEMISC_INSIDEOUT |

OLEMISC_CANTLINKINSIDE |

OLEMISC_RECOMPOSEONRESIZE;

IMPLEMENT_OLECTLTYPE(CExActiveXCtrl, IDS_EXACTIVEX, _dwExActiveXOleMisc)

/////////////////////////////////////////////////////////////////////////////

// CExActiveXCtrl::CExActiveXCtrlFactory::UpdateRegistry -

// Adds or removes system registry entries for CExActiveXCtrl

BOOL CExActiveXCtrl::CExActiveXCtrlFactory::UpdateRegistry(BOOL bRegister)

{

// TODO: Verify that your control follows apartment-model threading rules.

// Refer to MFC TechNote 64 for more information.

// If your control does not conform to the apartment-model rules, then

// you must modify the code below, changing the 6th parameter from

// afxRegApartmentThreading to 0.

if (bRegister)

return AfxOleRegisterControlClass(

AfxGetInstanceHandle(),

m_clsid,

m_lpszProgID,

IDS_EXACTIVEX,

IDB_EXACTIVEX,

afxRegApartmentThreading,

_dwExActiveXOleMisc,

_tlid,

_wVerMajor,

_wVerMinor);

else

return AfxOleUnregisterClass(m_clsid, m_lpszProgID);

}

/////////////////////////////////////////////////////////////////////////////

// CExActiveXCtrl::CExActiveXCtrl - Constructor

CExActiveXCtrl::CExActiveXCtrl()

{

InitializeIIDs(&IID_DExActiveX, &IID_DExActiveXEvents);

//도형의 위치를 0,0,100,100으로 설정한다.

m_leftPos=0;

m_rightPos=100;

m_topPos=0;

m_bottomPos=100;

//그리기 스타일을 0으로 즉 처음에는 박스로

m_drawStyle=0;

//화면출력 글자 설정

m_strData="ActiveX 테스트";

// TODO: Initialize your control's instance data here.

}

/////////////////////////////////////////////////////////////////////////////

// CExActiveXCtrl::~CExActiveXCtrl - Destructor

CExActiveXCtrl::~CExActiveXCtrl()

{

// TODO: Cleanup your control's instance data here.

}

/////////////////////////////////////////////////////////////////////////////

// CExActiveXCtrl::OnDraw - Drawing function

void CExActiveXCtrl::OnDraw(

CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)

{

//택스트를 출력한다.

pdc->TextOut(rcBounds.left,rcBounds.top,m_strData);

//설정된 좌표를 CRect로 변환

CRect rect=CRect(rcBounds.left+m_leftPos,

rcBounds.top+m_topPos+20,

rcBounds.left+m_rightPos,

rcBounds.top+m_bottomPos+20);

//그리기 스타일에 따라서

switch(m_drawStyle)

{

//박스 그리기

case 0:

pdc->Rectangle(rect);

break;

//원 그리기

case 1:

pdc->Ellipse(rect);

break;

}

}

/////////////////////////////////////////////////////////////////////////////

// CExActiveXCtrl::DoPropExchange - Persistence support

void CExActiveXCtrl::DoPropExchange(CPropExchange* pPX)

{

ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));

COleControl::DoPropExchange(pPX);

// TODO: Call PX_ functions for each persistent custom property.

}

/////////////////////////////////////////////////////////////////////////////

// CExActiveXCtrl::OnResetState - Reset control to default state

void CExActiveXCtrl::OnResetState()

{

COleControl::OnResetState();  // Resets defaults found in DoPropExchange

// TODO: Reset any other control state here.

}

/////////////////////////////////////////////////////////////////////////////

// CExActiveXCtrl message handlers

void CExActiveXCtrl::OnLeftPosChanged()

{

// TODO: Add notification handler code

SetModifiedFlag();

}

void CExActiveXCtrl::OnRightPosChanged()

{

// TODO: Add notification handler code

SetModifiedFlag();

}

void CExActiveXCtrl::OnBottomPosChanged()

{

// TODO: Add notification handler code

SetModifiedFlag();

}

void CExActiveXCtrl::OnTopPosChanged()

{

// TODO: Add notification handler code

SetModifiedFlag();

}

void CExActiveXCtrl::SetDrawRect(short left, short top, short right, short bottom)

{

m_leftPos=left;

m_topPos=top;

m_rightPos=right;

m_bottomPos=bottom;

}

void CExActiveXCtrl::OnDrawStyleChanged()

{

// TODO: Add notification handler code

SetModifiedFlag();

}

void CExActiveXCtrl::SetDrawStyle(short dstyle)

{

m_drawStyle=dstyle;

}

short CExActiveXCtrl::GetDrawStyle()

{

return m_drawStyle;

}

//이벤트 발생시

void CExActiveXCtrl::FireChangeStringMsg()

{

//문자를 바꾸고

m_strData="시간이 되었음";

//화면을 갱신

InvalidateControl();

}

int CExActiveXCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (COleControl::OnCreate(lpCreateStruct) == -1)

return -1;

SetTimer(1,5000,NULL);

return 0;

}

void CExActiveXCtrl::OnTimer(UINT nIDEvent)

{

//타이머를 해제하고

KillTimer(1);

//이벤트 함수 실행

FireChangeStringMsg();

COleControl::OnTimer(nIDEvent);

}

(프로그램 소스끝)

2.ActiveX 컨트롤 이용하기

본항목에서는 ActiveX 프로그램을 자신의 프로그램에 로드하여 사용하는 방법에 대해서 설명하겠습니다. ActiveX를 사용하는 방법은 매우 간단합니다. 그림 4-4-9는 ActiveX를 현재 프로젝트에 설정하는  방법을 설명한 그림입니다.

(그림 파일)

(그림 4-4-9)ActiveX 프로그램을 사용하기

프로젝트를 만든후에 특정 ActiveX프로그램을 삽입하고자 한다면 그림 4-4-9의 ⓐ항목 처럼 Project메뉴의 Add To Project 항목을 선택하고 Components and Controls.. 항목을 선택합니다. 이렇게 선택하게 되면 그림 4-4-9의 ⓑ와 같은 화면이 나타납니다. 이항목에서 Registered ActiveX Controls 항목을 선택하게 되면 현재 컴퓨터에 등록되어 있는 ActiveX 프로그램 리스트들이 나타납니다. 이리스트에 ActiveX프로그램이 등록되게 할려면 본 Part  2장에서 제작한 RegeAdd 프로그램을 이용하면 됩니다. Visual C++를 이용하여 컴파일하면 컴파일후에는 자동적으로 등록이 됩니다. 그러나 ActiveX프로그램을 자신의 컴퓨터에 등록하고자 할경우에는 RegeAdd프로그램을 이용하여 레지스트리에 프로그램을 등록하여야 합니다. 본장에서 제작한 ExActiveX프로그램은 컴파일하고 OCX파일을 만들게 되면 자동적으로 등록이 될것입니다. 이렇게 된 ActiveX프로그램을 선택하는 항목이 그림 4-4-9의 ⓒ입니다. ⓒ항목과 같이 ExActiveX프로그램을 선택하고 Insert버튼을 클릭하게 되면 해당 프로그램에 ActiveX가 설정됩니다. 설정될때 설정되는 클래스 정보를 알려주는 화면이 ⓓ항목입니다. ⓓ항목의 의미는 현재 ExActiveX프로그램을 현재 프로젝트에 설정하였을때 클래스는 CExActiveX이며 헤더 파일은 ExActiveX.h이고 소스파일은 ExActiveX.cpp라는 것입니다. 이렇게 등록된후에 대화상자 폼을 선택하였을때 나타나는 컨트롤 툴바에는 새로등록된 ActiveX에 해당하는 항목이 설정됩니다. 그림 4-4-9의 ⓔ항목이 새로 등록된 ActiveX의 항목이 설정된 화면입니다.

프로젝트에 ExActiveX컨트롤을 설정하게 되면 CExActiveX라는 클래스가 생성됩니다. 이클래스의 헤더는 다음과 같습니다.

#if !defined(AFX_EXACTIVEX_H__B8B46A01_4D66_11D2_81A9_00403320A530__INCLUDED_)

#define AFX_EXACTIVEX_H__B8B46A01_4D66_11D2_81A9_00403320A530__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++

// NOTE: Do not modify the contents of this file.  If this class is regenerated by

//  Microsoft Visual C++, your modifications will be overwritten.

/////////////////////////////////////////////////////////////////////////////

// CExActiveX wrapper class

class CExActiveX : public CWnd

{

protected:

DECLARE_DYNCREATE(CExActiveX)

public:

CLSID const& GetClsid()

{

static CLSID const clsid

= { 0x2c76b9d8, 0x448a, 0x11d2, { 0x8b, 0x9c, 0x0, 0x60, 0x97, 0xad, 0x9d, 0xdc } };

return clsid;

}

①

virtual BOOL Create(LPCTSTR lpszClassName,

LPCTSTR lpszWindowName, DWORD dwStyle,

const RECT& rect,

CWnd* pParentWnd, UINT nID,

CCreateContext* pContext = NULL)

{ return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID); }

BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle,

const RECT& rect, CWnd* pParentWnd, UINT nID,

CFile* pPersist = NULL, BOOL bStorage = FALSE,

BSTR bstrLicKey = NULL)

{ return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID,

pPersist, bStorage, bstrLicKey); }

// Attributes

public:

②//프로퍼티 에 값을 넣고 얻는 함수 리스트

short GetLeftPos();

void SetLeftPos(short);

short GetRightPos();

void SetRightPos(short);

short GetBottomPos();

void SetBottomPos(short);

short GetTopPos();

void SetTopPos(short);

short GetDrawStyle();

void SetDrawStyle(short);

// Operations

public:

③//메소드에 의한 함수

void SetDrawRect(short left, short top, short right, short bottom);

void SetDrawStyle(short dstyle);

short GetDrawStyle();

};

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_EXACTIVEX_H__B8B46A01_4D66_11D2_81A9_00403320A530__INCLUDED_)

①번 항목은 ActiveX 컨트롤을 자신의 프로그램 내부에서 출력하고자 할때 사용하는 공통적인 Create 함수입니다. 이함수는 ActiveX 프로그램을 설정하면 자동적으로 만들어 집니다.

②번항목은 ActiveX 컨트롤 내부에 있는 프로퍼티에 데이터를 넣거나 빼고자 할경우 사용하는 항목입니다. ExActiveX프로그램을 만들때 다음과 같은 프로퍼티를 설정한것을 기억하실것입니다.

short m_leftPos;

short m_rightPos;

short m_bottomPos;

short m_topPos;

short m_drawStyle;

위의 5개의 프로퍼티에 데이터를 넣고자 할때 사용하는 함수가 Get계열 함수이며 설정된 데이터를 얻고자 할때 사용하는 함수가 Set계열 함수입니다. ActiveX 를 자신의 프로젝트에 설정하게 되면 설정된 프로퍼티에 데이터를 설정,수정하는 함수이니 Get계열과 Set계열이 자동적으로 만들어지게 됩니다. ③항목은 메소드를 이용하여 만든 함수 입니다. 이함수는 ExActiveX에서 만든 메소드 함수가 그대로 설정된것입니다. 여기에서 주의할점이 있습니다. ExActiveX의 메소드 함수중에 GetDrawStyle와 SetDrawStyle 두개의 함수가 있습니다. 헌데 프로퍼티에 데이터를 설정하는 부분에도 GetDrawStyle와 SetDrawStyle가 있습니다. 즉 두개의 함수가 동시에 같은 형으로 존재하게 됩니다. ActiveX를 이용할경우 프로퍼티에 데이터를 넣거나 또는 데이터를 얻고자 하는 함수는 메소드로 만들 필요가 없다는 것을 증명하는 것입니다. ActiveX프로그램을 만들때 가끔 위와 같은 실수를 하고 컴파일이 안될때 어떻게 할까 걱정하는 분들이 많습니다. 이부분을 꼭 상기 하여 주시기 바랍니다. 위의 CExActiveX클래스를 이용하기 위해서는 프로퍼티 부분에 설정된 GetDrawStyle와 SetDrawStyle를 제거해주어야 합니다.

이제 소스 부분을 보겠습니다. 다음은 CExActiveX의 소스 부분입니다.

// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++

// NOTE: Do not modify the contents of this file.  If this class is regenerated by

//  Microsoft Visual C++, your modifications will be overwritten.

#include "stdafx.h"

#include "exactivex.h"

/////////////////////////////////////////////////////////////////////////////

// CExActiveX

IMPLEMENT_DYNCREATE(CExActiveX, CWnd)

/////////////////////////////////////////////////////////////////////////////

// CExActiveX properties

①프로퍼티 설정함수

short CExActiveX::GetLeftPos()

{

short result;

GetProperty(0x1, VT_I2, (void*)&result);

return result;

}

void CExActiveX::SetLeftPos(short propVal)

{

SetProperty(0x1, VT_I2, propVal);

}

short CExActiveX::GetRightPos()

{

short result;

GetProperty(0x2, VT_I2, (void*)&result);

return result;

}

void CExActiveX::SetRightPos(short propVal)

{

SetProperty(0x2, VT_I2, propVal);

}

short CExActiveX::GetBottomPos()

{

short result;

GetProperty(0x3, VT_I2, (void*)&result);

return result;

}

void CExActiveX::SetBottomPos(short propVal)

{

SetProperty(0x3, VT_I2, propVal);

}

short CExActiveX::GetTopPos()

{

short result;

GetProperty(0x4, VT_I2, (void*)&result);

return result;

}

void CExActiveX::SetTopPos(short propVal)

{

SetProperty(0x4, VT_I2, propVal);

}

/////////////////////////////////////////////////////////////////////////////

// CExActiveX operations

② 메소드 설정함수

void CExActiveX::SetDrawRect(short left, short top, short right, short bottom)

{

static BYTE parms[] =

VTS_I2 VTS_I2 VTS_I2 VTS_I2;

InvokeHelper(0x6, DISPATCH_METHOD, VT_EMPTY, NULL, parms,

left, top, right, bottom);

}

void CExActiveX::SetDrawStyle(short dstyle)

{

static BYTE parms[] =

VTS_I2;

InvokeHelper(0x7, DISPATCH_METHOD, VT_EMPTY, NULL, parms,

dstyle);

}

short CExActiveX::GetDrawStyle()

{

short result;

InvokeHelper(0x8, DISPATCH_METHOD, VT_I2, (void*)&result, NULL);

return result;

}

①번 항목은 AcitveX의 프로퍼티에 데이터를 넣고 또는 얻는 함수 소스 부분입니다. 소스내용은 간단하게 GetProperty와 SetProperty두개의 함수만 있습니다. 이것을 보시면 Automation의 확장이라는 것을 이해하실수 있을것입니다.

②번항목은 메소드를 이용하기 위해서 InvokeHelper 함수를 사용한것입니다. 결국

Automation형태인데 ActiveX사용자는 이것을 알필요가 없이 함수만 이용할수 있도록 설정한 것입니다. 또한 이정보가 Automation과 달리 OCX파일 안에 기록되어 있습니다. 그림 4-4-1의 내용을 다시 한번 생각해 보시기 바랍니다.

C++를 이용하여 ActiveX를 사용하고자 할때는 그림 4-4-9와 같은 방법으로 프로젝트에 삽입한다음 단순하게 해당 클래스를 이용하면 됩니다. 여기에서 중요한것은 해당 클래스의 내부를 수정할수는 없다는 것입니다. 간혹ActiveX 프로그램을 사용하면서 기능을 수정하고자 하는 분들이 있는데 그것은 가능하지  있습니다. ActiveX는 라이브러리 식으로 내부로 되어 있는 것이 아니라 외부에 프로그램이 있고 이프로그램을 GetProperty와 SetProperty 그리고 InvokeHelper를 이용하여 호출하여 사용하는 것이기 때문입니다. 다시 반복하여 말씀드리면 조립 형태의 프로그램이기 때문입니다.

<1>ActiveX 프로그램 이용 예제 ExAxClient

ExAcClient는 SDI형태로 프로젝트를 만들고 View 윈도우를 CFormView로 설정합니다.CFormView로 설정하게 되면 ID가 IDD_EXAXCLIENT_FORM 인 대화상자 폼이 설정됩니다. 이상태에서 그림 4-4-9와 같은 방법으로 ExActiveX컨트롤을 설정합니다. 이렇게 설정한후에 ExActvieX IDD_EXAXCLIENT_FORM에 도시합니다. 이렇게 도시한 형태가 그림 4-4-10과 같습니다.

(그림파일)

(그림 4-4-10)IDD_EXAXCLIENT_FORM

그림 4-4-10의 ⓐ항목은 IDD_EXAXCLIENT_FORM형태입니다. ExActiveX를 설정한 화면이 ①항목입니다. 이렇게 ExActiveX를 설정한후에 Ctrl키를 누른상태에서 해당항목을 마우스로 더블클릭하면 ⓑ와 같은 화면이 출력됩니다. 즉 DDX_Control를 이용하여 항목과 ActiveX프로그램과 연결하겠다는 의미입니다. ⓑ와 같은 화면이 출력된상태에서 CExActiveX클래스의 형인 m_exActivex 맴버와 연결을 시킵니다.

이와 같이 연결된 상태에서는 일반프로그램 작성하듯이 그냥 CExActiveX를 이용하면 됩니다. 그림 4-4-10의 ⓐ항목의  4개의 에디터박스와 연결되는 맴버들은 당음과 같습니다.

에디터박스와 맴버를 연결한다음  CComboBox m_listCombo 형으로 설정하여 그림 4-4-10의 ③항목과 연결시킵니다.

이렇게 연결하면 기본적인 모든 형태는 갖추게 됩니다. 그림 4-4-10의 ②항목 버튼을 클릭했을대는 콤보박스에서 선택한 항목과 에디터박스에 설정한 좌표의 값을 ActiveX에 전달해 주면 됩니다. 이내용은 다음과 같습니다.

void CExAxClientView::OnButton1()

{

UpdateData(TRUE);

//메소드 SetDrawRect호출

m_exActivex.SetDrawRect(m_nLeft,m_nTop,m_nRight,m_nBottom);

//콤보박스의 현재 위치를 얻는다.

short num=(short)m_listCombo.GetCurSel();

if(num<0)

num=0;

//현재 위치의 스타일을 설정한다.

m_exActivex.SetDrawStyle(num);

//화면을 재출력한다.

Invalidate();

}

위와 같이 하면 버튼을 좌표와 그리기스타일을 설정하고 "실행□ 버튼을 클릭하게 되면 현재의 값이 설정된 새로운 그림이 화면에 출력되게 됩니다.

그림 4-4-11은 ExAxClient 프로그램의 실행결과입니다.

(그림 4-4-11)ExAxClient 실행결과

<2>HTML에서 사용하기

HTML에서 우리가 만든 ExActiveX프로그램을 로드할수도 있습니다. 다음과 같이 HTML파일을 작성한후에 인터넷 익스플로어에서 실핼항수 있습니다.

인터넷에서 ActiveX 컨트롤을 사용하는 방법에 대한 좀더 자세한 방법이나 인테넷 프로그래밍에 대한 부분을 원하신다면 필자가 쓴 □인터넷 프로그래밍 바이블 2nd Edition□(영진출판사) 책을 참조하세요.

<HTML>

<HEAD>

<TITLE>ExActiveX</TITLE>

</HEAD>

<BODY>

<OBJECT ID="ExActiveX1" WIDTH=127 HEIGHT=137

CLASSID="CLSID:2C76B9D8-448A-11D2-8B9C-006097AD9DDC">

<PARAM NAME="_Version" VALUE="65536">

<PARAM NAME="_ExtentX" VALUE="3329">

<PARAM NAME="_ExtentY" VALUE="3620">

<PARAM NAME="_StockProps" VALUE="0">

</OBJECT>

</BODY>

</HTML>

</BODY>

</HTML>

그림 4-4-12는 인터넷 익스플로어에서 위에서 작성한 HTML파일을 로드한 형태입니다.

(그림 4-4-12) 인터넷 익스플로어에서 실행결과