¿Qué clases ATL facilitan la contención de controles ActiveX?
El código de hospedaje de controles de ATL no requiere que se usen clases ATL; simplemente puede crear una ventana "AtlAxWin80" y usar la API de hospedaje de controles si es necesario (para obtener más información, consulte ¿Qué es la API de hospedaje de controles ATL?. Pero las siguientes clases facilitan el uso de las características de independencia.
Clase | Descripción |
---|---|
CAxWindow | Ajusta una ventana "AtlAxWin80", proporcionando métodos para crear la ventana, crear un control o adjuntar un control a la ventana y recuperar punteros de interfaz en el objeto host. |
CAxWindow2T | Ajusta una ventana "AtlAxWinLic80", proporcionando métodos para crear la ventana, crear un control o adjuntar un control con licencia a la ventana y recuperar punteros de interfaz en el objeto host. |
CComCompositeControl | Actúa como una clase base para las clases de control ActiveX basadas en un recurso de diálogo. Estos controles pueden contener otros controles ActiveX. |
CAxDialogImpl | Actúa como una clase base para las clases de diálogo basadas en un recurso de diálogo. Estos diálogos pueden contener controles ActiveX. |
CWindow | Proporciona un método, GetDlgControl, que devolverá un puntero de interfaz en un control, dado el id. de su ventana host. Además, los contenedores de la API de Windows expuestos por CWindow normalmente facilitan la administración de ventanas. |
¿Qué es la API de hospedaje de controles ATL?
La API de hospedaje de controles de ATL es el conjunto de funciones que permite que cualquier ventana actúe como un contenedor de control ActiveX. Estas funciones se pueden vincular al proyecto de forma estática o dinámica, ya que están disponibles como código fuente y expuestas por ATL90.dll. Las funciones de hospedaje de controles se enumeran en la tabla siguiente.
Función | Descripción |
---|---|
AtlAxAttachControl | Crea un objeto host, lo conecta a la ventana proporcionada y, después, adjunta un control existente. |
AtlAxCreateControl | Crea un objeto host, lo conecta a la ventana proporcionada y, después, carga un control. |
AtlAxCreateControlLic | Crea un control ActiveX con licencia, lo inicializa y lo hospeda en la ventana especificada, igual que AtlAxCreateControl. |
AtlAxCreateControlEx | Crea un objeto host, lo conecta a la ventana proporcionada y, después, carga un control (también permite configurar receptores de eventos). |
AtlAxCreateControlLicEx | Crea un control ActiveX con licencia, lo inicializa y lo hospeda en la ventana especificada, igual que AtlAxCreateControlLic. |
AtlAxCreateDialog | Crea un cuadro de diálogo sin modelo a partir de un recurso de diálogo y devuelve el manipulador de ventana. |
AtlAxDialogBox | Crea un cuadro de diálogo modal a partir de un recurso de diálogo. |
AtlAxGetControl | Devuelve el puntero de interfaz IUnknown del control hospedado en una ventana. |
AtlAxGetHost | Devuelve el puntero de interfaz IUnknown del objeto host conectado a una ventana. |
AtlAxWinInit | Inicializa el código de hospedaje de controles. |
AtlAxWinTerm | Anula la inicialización del código de hospedaje de controles. |
Los parámetros HWND
de las tres primeras funciones deben ser una ventana existente de (casi) cualquier tipo. Si llama explícitamente a cualquiera de estas tres funciones (normalmente, no tendrá que hacerlo), no pase un manipulador a una ventana que ya actúe como host (si lo hace, el objeto host existente no se liberará).
Las siete primeras funciones llaman a AtlAxWinInit de forma implícita.
Nota:
La API de hospedaje de controles constituye la base de la compatibilidad de ATL con la independencia de control ActiveX. Pero no suele ser necesario llamar a estas funciones directamente si se aprovechan o se usan las clases contenedoras de ATL por completo. Para obtener más información, consulte ¿Qué clases ATL facilitan la contención de control ActiveX?
¿Qué es AtlAxWin100?
AtlAxWin100
es el nombre de una clase de ventana que ayuda a proporcionar la funcionalidad de hospedaje de controles de ATL. Al crear una instancia de esta clase, el procedimiento de ventana usará de forma automática la API de hospedaje de controles para crear un objeto host asociado a la ventana y cargarlo con el control que especifique como título de la ventana.
¿Cuándo es necesario llamar a AtlAxWinInit?
AtlAxWinInit registra la clase de ventana "AtlAxWin80" (más un par de mensajes de ventana personalizados), por lo que se debe llamar a esta función antes de intentar crear una ventana host. Pero no siempre es necesario llamar explícitamente a esta función, ya que las API de hospedaje (y las clases que las usan) suelen llamar a esta función de forma automática. No pasa nada si llama a esta función más de una vez.
¿Qué es un objeto host?
Un objeto host es un objeto COM que representa el contenedor de control ActiveX proporcionado por ATL para una ventana determinada. El objeto host baja la clase de la ventana contenedora para que pueda reflejar los mensajes en el control, proporciona las interfaces de contenedor necesarias que usará el control y expone las interfaces IAxWinHostWindow e IAxWinAmbientDispatch para permitirle configurar el entorno del control.
Puede usar el objeto host para establecer las propiedades ambientales del contenedor.
¿Puedo hospedar más de un control en una única ventana?
No es posible hospedar más de un control en una sola ventana host ATL. Cada ventana host está diseñada para contener exactamente un control a la vez (esto permite un mecanismo sencillo para controlar la reflexión de mensajes y las propiedades ambientales por control). Pero si necesita que el usuario vea varios controles en una sola ventana, crear varias ventanas host como elementos secundarios de esa ventana es muy sencillo.
¿Puedo volver a usar una ventana host?
No se recomienda reusar las ventanas host. Para garantizar la solidez del código, debe vincular la duración de la ventana del host a la duración de un solo control.
¿Cuándo es necesario llamar a AtlAxWinTerm?
AtlAxWinTerm anula el registro de la clase de ventana "AtlAxWin80". Debe llamar a esta función (si ya no necesita crear ventanas host) después de que se hayan destruido todas las ventanas host existentes. Si no llama a esta función, se anulará automáticamente el registro de la clase de ventana cuando finalice el proceso.
Hospedaje de controles ActiveX mediante ATL AXHost
En el ejemplo de esta sección se muestra cómo crear AXHost y cómo hospedar un control ActiveX mediante varias funciones ATL. También se muestra cómo acceder a los eventos de control y receptor (mediante IDispEventImpl) desde el control hospedado. El ejemplo hospeda el control Calendar en una ventana principal o en una ventana secundaria.
Observe la definición del símbolo USE_METHOD
. Puede cambiar el valor de este símbolo para que varíe entre 1 y 8. El valor del símbolo determina cómo se creará el control:
Para los valores de número par de
USE_METHOD
, la llamada para crear el host baja de clase a una ventana y la convierte en un host de control. En el caso de los valores impares, el código crea una ventana secundaria que actúa como host.Para los valores de
USE_METHOD
entre 1 y 4, el acceso al control y al receptor de eventos se realiza en la llamada que también crea el host. Los valores entre 5 y 8 consultan al host para las interfaces y enlazan el receptor.
A continuación, se muestra un resumen:
USE_METHOD | Host | Control del acceso y del receptor de eventos | Función demostrada |
---|---|---|---|
1 | Ventana secundaria | Un paso | CreateControlLicEx |
2 | Ventana principal | Un paso | AtlAxCreateControlLicEx |
3 | Ventana secundaria | Un paso | CreateControlEx |
4 | Ventana principal | Un paso | AtlAxCreateControlEx |
5 | Ventana secundaria | Varios pasos | CreateControlLic |
6 | Ventana principal | Varios pasos | AtlAxCreateControlLic |
7 | Ventana secundaria | Varios pasos | CreateControl |
8 | Ventana principal | Varios pasos | 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;
}
}