어떤 ATL 클래스가 ActiveX 컨트롤 포함에 도움이 되나요?
ATL의 제어 호스팅 코드는 ATL 클래스를 사용할 필요가 없습니다. 필요한 경우 "AtlAxWin80" 창을 만들고 컨트롤 호스팅 API를 사용할 수 있습니다(자세한 내용은 ATL 컨트롤 호스팅 API란?을 참조하세요.) 그러나 다음 클래스를 사용하면 포함 기능을 더 쉽게 사용할 수 있습니다.
클래스 | 설명 |
---|---|
CAxWindow | "AtlAxWin80" 창을 래핑하여 창을 만들고, 컨트롤을 만들거나, 컨트롤을 창에 연결하고, 호스트 개체에 대한 인터페이스 포인터를 검색하는 메서드를 제공합니다. |
CAxWindow2T | "AtlAxWinLic80" 창을 래핑하여 창을 만들고, 컨트롤을 만들거나, 라이선스가 부여된 컨트롤을 창에 연결하고, 호스트 개체에 대한 인터페이스 포인터를 검색하는 메서드를 제공합니다. |
CComCompositeControl | 대화 상자 리소스를 기반으로 ActiveX 컨트롤 클래스의 기본 클래스 역할을 합니다. 이러한 컨트롤에는 다른 ActiveX 컨트롤이 포함될 수 있습니다. |
CAxDialogImpl | 대화 리소스를 기반으로 하는 대화 상자 클래스의 기본 클래스 역할을 합니다. 이러한 대화 상자에는 ActiveX 컨트롤이 포함될 수 있습니다. |
CWindow | 호스트 창의 ID가 지정된 경우 컨트롤에 인터페이스 포인터를 반환하는 GetDlgControl 메서드를 제공합니다. 또한 일반적으로 노출되는 CWindow Windows API 래퍼는 창 관리를 더 쉽게 만듭니다. |
ATL 컨트롤 호스팅 API란 무엇인가요?
ATL의 제어 호스팅 API는 모든 창이 ActiveX 컨트롤 컨테이너 역할을 할 수 있도록 하는 함수 집합입니다. 이러한 함수는 소스 코드로 사용할 수 있고 ATL90.dll 의해 노출되므로 정적으로 또는 동적으로 프로젝트에 연결할 수 있습니다. 컨트롤 호스팅 함수는 아래 표에 나와 있습니다.
함수 | 설명 |
---|---|
AtlAxAttachControl | 호스트 개체를 만들고 제공된 창에 연결한 다음 기존 컨트롤을 연결합니다. |
AtlAxCreateControl | 호스트 개체를 만들고 제공된 창에 연결한 다음 컨트롤을 로드합니다. |
AtlAxCreateControlLic | 사용이 허가된 ActiveX 컨트롤을 만들고, 초기화하고, AtlAxCreateControl과 유사한 지정된 창에서 호스트합니다. |
AtlAxCreateControlEx | 호스트 개체를 만들고 제공된 창에 연결한 다음 컨트롤을 로드합니다(이벤트 싱크를 설정할 수도 있습니다). |
AtlAxCreateControlLicEx | 사용이 허가된 ActiveX 컨트롤을 만들고, 초기화하고, 지정된 창에서 호스트합니다(AtlAxCreateControlLic과 유사). |
AtlAxCreateDialog | 대화 상자 리소스에서 모덜리스 대화 상자를 만들고 창 핸들을 반환합니다. |
AtlAxDialogBox | 대화 리소스에서 모달 대화 상자를 만듭니다. |
AtlAxGetControl | 창에 호스트된 컨트롤의 IUnknown 인터페이스 포인터를 반환합니다. |
AtlAxGetHost | 창에 연결된 호스트 개체의 IUnknown 인터페이스 포인터를 반환합니다. |
AtlAxWinInit | 컨트롤 호스팅 코드를 초기화합니다. |
AtlAxWinTerm | 컨트롤 호스팅 코드를 초기화하지 않습니다. |
처음 세 함수의 매개 변수는 HWND
거의 모든 형식의 기존 창이어야 합니다. 이러한 세 함수를 명시적으로 호출하는 경우(일반적으로 필요하지 않음) 이미 호스트 역할을 하는 창에 핸들을 전달하지 마세요(이렇게 하면 기존 호스트 개체가 해제되지 않음).
처음 7개 함수는 암시적으로 AtlAxWinInit을 호출합니다.
참고 항목
컨트롤 호스팅 API는 ActiveX 컨트롤 포함에 대한 ATL 지원의 기초를 형성합니다. 그러나 ATL의 래퍼 클래스를 최대한 활용하거나 최대한 활용하는 경우 이러한 함수를 직접 호출할 필요가 거의 없습니다. 자세한 내용은 ActiveX 컨트롤 포함을 용이하게 하는 ATL 클래스를 참조 하세요.
AtlAxWin100이란 무엇인가요?
AtlAxWin100
는 ATL의 제어 호스팅 기능을 제공하는 데 도움이 되는 창 클래스의 이름입니다. 이 클래스의 인스턴스를 만들 때 창 프로시저는 자동으로 컨트롤 호스팅 API를 사용하여 창과 연결된 호스트 개체를 만들고 창의 제목으로 지정한 컨트롤로 로드합니다.
언제 AtlAxWinInit를 호출해야 하나요?
AtlAxWinInit는 호스트 창을 만들기 전에 이 함수를 호출해야 하므로 "AtlAxWin80" 창 클래스(사용자 지정 창 메시지 몇 개 포함)를 등록합니다. 그러나 호스팅 API(및 이를 사용하는 클래스)가 이 함수를 자주 호출하기 때문에 항상 이 함수를 명시적으로 호출할 필요는 없습니다. 이 함수를 두 번 이상 호출하는 데는 아무런 해가 없습니다.
호스트 개체란 무엇인가요?
호스트 개체는 특정 창에 대해 ATL에서 제공하는 ActiveX 컨트롤 컨테이너를 나타내는 COM 개체입니다. 호스트 개체는 컨트롤에 메시지를 반영할 수 있도록 컨테이너 창을 서브클래싱하고, 컨트롤에서 사용하는 데 필요한 컨테이너 인터페이스를 제공하고, 컨트롤 환경을 구성할 수 있도록 IAxWinHostWindow 및 IAxWinAmbientDispatch 인터페이스를 노출합니다.
호스트 개체를 사용하여 컨테이너의 앰비언트 속성을 설정할 수 있습니다.
하나의 창에서 여러 개의 컨트롤을 호스트할 수 있나요?
단일 ATL 호스트 창에서 둘 이상의 컨트롤을 호스트할 수 없습니다. 각 호스트 창은 한 번에 정확히 하나의 컨트롤을 보유하도록 설계되었습니다(이를 통해 메시지 리플렉션 및 컨트롤별 앰비언트 속성을 처리하는 간단한 메커니즘을 사용할 수 있습니다). 그러나 사용자가 단일 창에서 여러 컨트롤을 표시해야 하는 경우 해당 창의 자식으로 여러 호스트 창을 만드는 것은 간단합니다.
호스트 창을 다시 사용할 수 있나요?
호스트 창을 다시 사용하는 것은 권장되지 않습니다. 코드의 견고성을 보장하려면 호스트 창의 수명을 단일 컨트롤의 수명에 연결해야 합니다.
언제 AtlAxWinTerm을 호출해야 하나요?
AtlAxWinTerm은 "AtlAxWin80" 창 클래스의 등록을 취소합니다. 모든 기존 호스트 창이 제거된 후 이 함수를 호출해야 합니다(더 이상 호스트 창을 만들 필요가 없는 경우). 이 함수를 호출하지 않으면 프로세스가 종료될 때 창 클래스가 자동으로 등록 취소됩니다.
ATL AXHost를 사용하여 ActiveX 컨트롤 호스팅
이 섹션의 샘플에서는 AXHost를 만드는 방법과 다양한 ATL 함수를 사용하여 ActiveX 컨트롤을 호스트하는 방법을 보여 줍니다. 또한 호스트되는 컨트롤에서 컨트롤 및 싱크 이벤트(IDispEventImpl 사용)에 액세스하는 방법도 보여 줍니다. 샘플은 기본 창 또는 자식 창에서 일정 컨트롤을 호스트합니다.
기호의 정의를 확인합니다 USE_METHOD
. 이 기호의 값을 1에서 8 사이로 변경할 수 있습니다. 기호 값은 컨트롤을 만드는 방법을 결정합니다.
짝수 번호 값의
USE_METHOD
경우 호스트를 만들기 위한 호출은 창을 서브클래싱하고 컨트롤 호스트로 변환합니다. 홀수 값의 경우 코드는 호스트 역할을 하는 자식 창을 만듭니다.1에서 4 사이의 값
USE_METHOD
의 경우 호스트를 만드는 호출에서 이벤트의 컨트롤 및 싱크에 대한 액세스가 수행됩니다. 5에서 8 사이의 값은 호스트에서 인터페이스를 쿼리하고 싱크를 후크합니다.
다음은 요약입니다.
USE_METHOD | 호스트 | 액세스 및 이벤트 싱크 제어 | 설명된 함수 |
---|---|---|---|
1 | 자식 창 | 한 단계 | CreateControlLicEx |
2 | 기본 창 | 한 단계 | AtlAxCreateControlLicEx |
3 | 자식 창 | 한 단계 | CreateControlEx |
4 | 기본 창 | 한 단계 | AtlAxCreateControlEx |
5 | 자식 창 | 여러 단계 | CreateControlLic |
6 | 기본 창 | 여러 단계 | AtlAxCreateControlLic |
7 | 자식 창 | 여러 단계 | CreateControl |
8 | 기본 창 | 여러 단계 | AtlAxCreateControl |
// Your project must be apartment threaded or the (AtlAx)CreateControl(Lic)(Ex)
// calls will fail.
#define _ATL_APARTMENT_THREADED
#include <atlbase.h>
#include <atlwin.h>
#include <atlhost.h>
// Value of g_UseMethod determines the function used to create the control.
int g_UseMethod = 0; // 1 to 8 are valid values
bool ValidateUseMethod() { return (1 <= g_UseMethod) && (g_UseMethod <= 8); }
#import "PROGID:MSCAL.Calendar.7" no_namespace, raw_interfaces_only
// Child window class that will be subclassed for hosting Active X control
class CChildWindow : public CWindowImpl<CChildWindow>
{
public:
BEGIN_MSG_MAP(CChildWindow)
END_MSG_MAP()
};
class CMainWindow : public CWindowImpl<CMainWindow, CWindow, CFrameWinTraits>,
public IDispEventImpl<1, CMainWindow, &__uuidof(DCalendarEvents), &__uuidof(__MSACAL), 7, 0>
{
public :
CChildWindow m_wndChild;
CAxWindow2 m_axwnd;
CWindow m_wndEdit;
static ATL::CWndClassInfo& GetWndClassInfo()
{
static ATL::CWndClassInfo wc =
{
{
sizeof(WNDCLASSEX),
CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
StartWindowProc,
0, 0, NULL, NULL, NULL,
(HBRUSH)(COLOR_WINDOW + 1),
0,
_T("MainWindow"),
NULL
},
NULL, NULL, IDC_ARROW, TRUE, 0, _T("")
};
return wc;
}
BEGIN_MSG_MAP(CMainWindow)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
END_MSG_MAP()
BEGIN_SINK_MAP(CMainWindow)
SINK_ENTRY_EX(1, __uuidof(DCalendarEvents), DISPID_CLICK, OnClick)
END_SINK_MAP()
// Helper to display events
void DisplayNotification(TCHAR* pszMessage)
{
CWindow wnd;
wnd.Attach(GetDlgItem(2));
wnd.SendMessage(EM_SETSEL, (WPARAM)-1, -1);
wnd.SendMessage(EM_REPLACESEL, 0, (LPARAM)pszMessage);
}
// Event Handler for Click
STDMETHOD(OnClick)()
{
DisplayNotification(_T("OnClick\r\n"));
return S_OK;
}
LRESULT OnCreate(UINT, WPARAM, LPARAM, BOOL&)
{
HRESULT hr = E_INVALIDARG;
_pAtlModule->Lock();
RECT rect;
GetClientRect(&rect);
RECT rect2;
rect2 = rect;
rect2.bottom -=200;
// if g_UseMethod is odd then create AxHost directly as the child of the main window
if (g_UseMethod & 0x1)
{
m_axwnd.Create(m_hWnd, rect2, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, 0, 1);
}
// if g_UseMethod is even then the AtlAx version is invoked.
else
{
// Create a child window.
// AtlAx functions will subclass this window.
m_wndChild.Create(m_hWnd, rect2, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, 0, 1);
// Attach the child window to the CAxWindow so we can access the
// host that subclasses the child window.
m_axwnd.Attach(m_wndChild);
}
if (m_axwnd.m_hWnd != NULL)
{
CComPtr<IUnknown> spControl;
// The calls to (AtlAx)CreateControl(Lic)(Ex) do the following:
// Create Calendar control. (Passing in NULL for license key.
// Pass in valid license key to the Lic functions if the
// control requires one.)
// Get the IUnknown pointer for the control.
// Sink events from the control.
// The AtlAx versions subclass the hWnd that is passed in to them
// to implement the host functionality.
// The first 4 calls accomplish it in one call.
// The last 4 calls accomplish it using multiple steps.
switch (g_UseMethod)
{
case 1:
{
hr = m_axwnd.CreateControlLicEx(
OLESTR("MSCAL.Calendar.7"),
NULL,
NULL,
&spControl,
__uuidof(DCalendarEvents),
(IUnknown*)(IDispEventImpl<1, CMainWindow,
&__uuidof(DCalendarEvents), &__uuidof(__MSACAL), 7, 0>*)this
);
break;
}
case 2:
{
hr = AtlAxCreateControlLicEx(
OLESTR("MSCAL.Calendar.7"),
m_wndChild.m_hWnd,
NULL,
NULL,
&spControl,
__uuidof(DCalendarEvents),
(IUnknown*)(IDispEventImpl<1, CMainWindow,
&__uuidof(DCalendarEvents), &__uuidof(__MSACAL), 7, 0>*)this,
NULL
);
break;
}
case 3:
{
hr = m_axwnd.CreateControlEx(
OLESTR("MSCAL.Calendar.7"),
NULL,
NULL,
&spControl,
__uuidof(DCalendarEvents),
(IUnknown*)(IDispEventImpl<1, CMainWindow,
&__uuidof(DCalendarEvents), &__uuidof(__MSACAL), 7, 0>*)this
);
break;
}
case 4:
{
hr = AtlAxCreateControlEx(
OLESTR("MSCAL.Calendar.7"),
m_wndChild.m_hWnd,
NULL,
NULL,
&spControl,
__uuidof(DCalendarEvents),
(IUnknown*)(IDispEventImpl<1, CMainWindow,
&__uuidof(DCalendarEvents), &__uuidof(__MSACAL), 7, 0>*)this
);
break;
}
// The following calls create the control, obtain an interface to
// the control, and set up the sink in multiple steps.
case 5:
{
hr = m_axwnd.CreateControlLic(
OLESTR("MSCAL.Calendar.7")
);
break;
}
case 6:
{
hr = AtlAxCreateControlLic(
OLESTR("MSCAL.Calendar.7"),
m_wndChild.m_hWnd,
NULL,
NULL
);
break;
}
case 7:
{
hr = m_axwnd.CreateControl(
OLESTR("MSCAL.Calendar.7")
);
break;
}
case 8:
{
hr = AtlAxCreateControl(
OLESTR("MSCAL.Calendar.7"),
m_wndChild.m_hWnd ,
NULL,
NULL
);
break;
}
}
// have to obtain an interface to the control and set up the sink
if (g_UseMethod > 4)
{
if (SUCCEEDED(hr))
{
hr = m_axwnd.QueryControl(&spControl);
if (SUCCEEDED(hr))
{
// Sink events form the control
DispEventAdvise(spControl, &__uuidof(DCalendarEvents));
}
}
}
if (SUCCEEDED(hr))
{
// Use the returned IUnknown pointer.
CComPtr<ICalendar> spCalendar;
hr = spControl.QueryInterface(&spCalendar);
if (SUCCEEDED(hr))
{
spCalendar->put_ShowDateSelectors(VARIANT_FALSE);
}
}
}
rect2 = rect;
rect2.top = rect.bottom - 200 + 1;
m_wndEdit.Create(_T("Edit"), m_hWnd, rect2, NULL, WS_CHILD | WS_VISIBLE |
WS_BORDER | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE, 0, 2);
return 0;
}
LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL&)
{
_pAtlModule->Unlock();
return 0;
}
};
class CHostActiveXModule : public CAtlExeModuleT<CHostActiveXModule>
{
public :
CMainWindow m_wndMain;
// Create the Main window
HRESULT PreMessageLoop(int nCmdShow)
{
HRESULT hr = CAtlExeModuleT<CHostActiveXModule>::PreMessageLoop(nCmdShow);
if (SUCCEEDED(hr))
{
AtlAxWinInit();
hr = S_OK;
RECT rc;
rc.top = rc.left = 100;
rc.bottom = rc.right = 500;
m_wndMain.Create(NULL, rc, _T("Host Calendar") );
m_wndMain.ShowWindow(nCmdShow);
}
return hr;
}
// Clean up. App is exiting.
HRESULT PostMessageLoop()
{
AtlAxWinTerm();
return CAtlExeModuleT<CHostActiveXModule>::PostMessageLoop();
}
};
CHostActiveXModule _AtlModule;
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hInstance);
UNREFERENCED_PARAMETER(hPrevInstance);
g_UseMethod = _ttoi(lpCmdLine);
if (ValidateUseMethod())
{
return _AtlModule.WinMain(nCmdShow);
}
else
{
return E_INVALIDARG;
}
}