Cómo integrar ventanas C# en proyectos C++
Si desea ver el código fuente completo de nuestro complemento Notepad++, visite nuestra página de inicio del complemento.
Cree controles ActiveX usando C#
Esta parte de la solución se basa en el artículo "Exponer controles de Windows Forms como controles ActiveX" escrito por Morgan Skinner. Aunque la solución proporcionada por Skinner se desarrolló en Visual Studio 8 Beta, su ejemplo funcionará bien (con cambios menores) en la versión VS8. Aquí hay una lista de los cambios que hicimos a la solución de Skinner:
1. Establezca ClassInterface en ClassInterfaceType.None (para exponer la única interfaz especificada a COM). Más información en el próximo capítulo.
2. Ingresando a la pestaña "Aplicación" del elemento del menú "Propiedades del proyecto" y seleccionando "Información de ensamblaje" en el cuadro de diálogo "Información de ensamblaje" (Información de ensamblaje)" para configurar el proyecto para que sea visible. a COM. En el cuadro de diálogo Información del ensamblaje, está seleccionado "Hacer visible el ensamblaje COM".
1 También debe registrar el proyecto para la interoperabilidad COM (tenga en cuenta que en la versión VS8.0, "Construir" La ventana de propiedades es. diferente en la versión VS8.0 que en la versión beta). Cuando esta característica está marcada, Visual Studio registrará automáticamente el control .NET ActiveX cuando el proyecto se compila correctamente.
4. En Skinner. artículo, hay un pequeño error en la función ComUnregisterFunction().
La siguiente es la función correcta:
//
// Anular registro de la función DLL ActiveX
////
////
[ComUnregisterFunction()]
public static void UnregisterClass(string i_Key)
{
// Elimina HKEY_ CLASSES_ROOT/ de las claves pasadas porque no las necesito
StringBuilder sb = new StringBuilder(i_Key);
sb.Replace(@"HKEY_CLASSES_ROOT\", "");
sb.DeleteSubKey("Control", false);
// Luego abra InprocServer32< / p>
RegistryKey inprocServer32 =
RegisterKey.OpenSubKey("InprocServer32", true);
// Eliminar código.
// Elimina la clave CodeBase. Del mismo modo, no se arrojará si falta
inprocServer32.DeleteSubKey("CodeBase", false);
// Finalmente cerrar la clave principal
// Eliminar la clave CodeBase.
Exportar métodos explícitos para COM
Para hacer los diseños más precisos, exportamos métodos específicos para COM. Cada programa externo que utilice nuestro control sólo tendrá acceso a los métodos que necesite.
La mejor manera de exportar un método específico es crear una interfaz que contenga todos los métodos relevantes. Luego se deben agregar propiedades específicas a esta interfaz y la clase de formulario debe implementarse en esta interfaz.
//
// Interfaz COM: puede ejecutar código C# desde C++
//
[InterfaceType( ComInterfaceType.InterfaceIsDual)]
interfaz pública ICSSExplorerInterface
{
void setButtonCaption(String strNewCaption);
void setAdapterDllPtr (IntPtr i_AdapterDllPtr);
}
Usamos el sistema de mensajería de Microsoft para comunicarnos con la ventana del contenedor y otras ventanas del proyecto VC. No manejamos ningún evento ya que sería más complejo y no necesario para nuestra solución.
Añadimos el siguiente código en la clase MyDotNetActiveX para permitir la mensajería:
private static uint DOT_NET_BUTTON_PRESSED = 0x0400;
private void btnOK_Click( object sender, EventArgs e )
{
SendMessage(m_AdapterDllPtr.ToInt32(),
DOT_NET_BUTTON_PRESSED, IntPtr. );
}
#region MAPPING_OF_USER32_DLL_SECTION
[DllImport("user32.dll", EntryPoint = "SendMessage")]
público estático externo IntPtr SendMessage(
int hwnd , uint wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage" )]
public static extern int SendMessage(
int hwnd, uint wMsg, int wParam, string lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage");
[DllImport( "user32.dll", EntryPoint = "SendMessage") EntryPoint = "SendMessage")]
public static extern int SendMessage(
int hwnd, uint wMsg, int wParam, out int lParam);
[ DllImport("user32.dll", EntryPoint = "SendMessage")]
público estático extern int GetNbFiles(
int hwnd, uint wMsg, int wParam, int lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage")]
public static extern int GetFileNames(
int hwnd, uint wMsg, int
[MarshalAs(UnmanagedType.LPArray)]IntPtr[] wParam,
int lParam);
[DllImport( "user32.dll", EntryPoint = "SendMessage") )
public static extern int SendMessage(
int hwnd, uint wMsg, int wParam, StringBuilder lParam);
#endregion
En el código de inicialización, asumimos que la ventana del contenedor se comunicará a través de su identificador de ventana (parámetro hwnd).
Ahora estamos listos para compilar y probar el control. Después de una compilación exitosa, Visual Studio registrará automáticamente el control ActiveX. Puede ver la información de registro utilizando el software gratuito RegDllView.
Prueba del control en el contenedor de prueba de controles ActiveX
Antes de pasar al siguiente paso de este artículo, es un buen momento para probar nuestro control en un programa de terceros. Usamos el contenedor de prueba de control ActiveX (tstcon32.exe) para realizar pruebas. Este programa se puede encontrar en el directorio de instalación de Visual Studio.
1. Inserte el control a través del elemento de menú "Insertar nuevo control" en la barra de menú "Editar".
2. Ahora seleccione el elemento de menú "Control" "Métodos de invocación de control" en la barra de menú.
3. Seleccione la función setButtonCaption en el control del cuadro combinado del nombre del método.
4. Escriba "Hola" en el cuadro de texto del valor del parámetro y luego presione el botón de llamada.
5. Los siguientes son los resultados de la prueba.
Agregar. la ventana C++ Controles ActiveX C#
Uso de la inclusión de controles ATL
Cualquier control ActiveX se puede incluir utilizando la Biblioteca de plantillas activas (ATL).
En esta parte de la guía, haremos lo siguiente:
Crear un proyecto de aplicación C++ Win32
Insertar un control ActiveX en una ventana de C++ p >
Enviar comandos a controles ActiveX
Recibir mensajes de controles ActiveX
Crear un proyecto de aplicación C++ Win32
Crear un nuevo proyecto Win32 y asignarle un nombre "CPP_Container:":
2. Utilice la configuración predeterminada y presione "OK /p>
HWND _hAtl;
HWND _hSelf;
IUnknown* _pUnk;
DotNetActiveX::ICSSExplorerInterfacePtr _pDotNetCOMPtr;
HINSTANCE _hWebLib = ::LoadLibrary(TEXT("ATL.DLL"));
Copiar código
2. Después de que Visual Studio compila el proyecto C#, se creará el archivo DotNetActiveX.tlb. Este archivo contiene todos los métodos y estructuras del proyecto. Usaremos el siguiente comando para importarlos.
// importar funciones y estructuras de control de C#
#import "DotNetActiveX.tlb" named_guids raw_interfaces_only
3 Agregue las siguientes funciones en CPP_Container.cpp.
Esta función inserta el contenedor ATL en la ventana y carga nuestro control ActiveX de C#:
void loadActiveX(LPCTSTR strActiveXName)
{
// Inicializa la contención del control ATL código .
BOOL (WINAPI *m_AtlAxWinInit)();
m_AtlAxWinInit = (BOOL (WINAPI *)(void))::.GetProcAddress
(_hWebLib , "AtlAxWinInit");
m_AtlAxWinInit();
// Obtener el tamaño del cliente de la ventana principal
// m_AtlAxWinInit();
m_AtlAxWinInit = (BOOL (WINAPI *)(void)).area y enumerar las ventanas secundarias.
// Pasa dimensiones a ventanas secundarias durante la enumeración.
RECT rcClient
GetClientRect(_hSelf, &rcClient,
_hAtl = ::CreateWindowEx(
WS_EX_CLIENTEDGE,\
TEXTO("AtlAxWin"),\
strActiveXName,\
WS_CHILD WS_VISIBLE /*WS_CLIPCHILDREN */WS_EX_RTLREADING,\
0 , 0, rcClient.right, rcClient.bottom,/
_hSelf,\
NULL,\
NULL,\
NULL );
if (!_hAtl)
{
MessageBox( NULL, TEXT("¡No se puede cargar AtlAxWin!"),
szTitle, MB_OK | MB_ICONSTOP);
throw int(106901);
}
HRESULT (WINAPI *m_AtlAxGetControl) (HWND h, IUnknown** pp );
m_AtlAxGetControl = (HRESULT (WINAPI *)
(HWND, IUnknown**)).:GetProcAddress(_hWebLib, "AtlAxGetControl");
m_AtlAxGetControl(_hAtl, &_pUnk);
_pUnk->QueryInterface(__uuidof( DotNetActiveX::ICSSExplorerInterface),
(LPVOID *) &_pDotNetCOMPtr);
if ( _pDotNetCOMPtr ! = NULL)
{
_pDotNetCOMPtr->setAdapterDllPtr((long) _hSelf);
}
más
{
// Obtener el cliente de la ventana principal
// El tamaño del área y enumerar las ventanas secundarias.
// Pasa dimensiones a ventanas secundarias durante la enumeración.
RECT rcClient
GetClientRect(_hSelf, &rcClient);
::DestroyWindow(_hAtl);
_hAtl = ::CreateWindowEx (
WS_EX_ CLIENTEDGE,\
TEXT("AtlAxWin"),\
TEXT("MSHTML:""Por favor registre ActiveX
control antes de usar este complemento.""),\
WS_CHILD | WS_CLIPCHILDREN
WS_EX_RTLREADING,\
0, 0, rcClient.right, rcClient.bottom,\
_hSelf,\
NULL ,\
NULL,\
NULL);
}
}
}
Copiar código
4. Para un desarrollo más preciso, agregue Agregue el siguiente código en el Bloque de procesamiento de mensajes WM_DESTORY (Nota: para destruir la ventana de control ActiveX de C# y liberar la memoria cargada)
_pDotNetCOMPtr->Release();
::DestroyWindow( _hAtl);
_pUnk->Release();
::FreeLibrary(_hWebLib);
5. Finalmente, llame a la función loadActiveX en la función _tWinMain
loadActiveX(TEXT("DotNetActiveX.MyDotNetActiveX"));
Enviando comandos al control C# ActiveX
Después de insertar el archivo TLB, estamos en el proyecto C#. Se mostrarán todos los métodos exportados. Ahora, simplemente llamemos al método relevante:
char *strHelloWorld = "Hello World!";
_bstr_t bstrHelloWorld(strHelloWorld);
.p>
_pDotNetCOMPtr->. setButtonCaption(bstrHelloWorld);
Esto cambiará el título del botón a "¡Hola mundo!".
Los mensajes de los controles de C# llegan a la ventana de C++ a través del sistema de mensajería de Microsoft. Al llamar a la función loadActiveX, hemos enviado el identificador de la ventana al control C#. Entonces ahora solo necesitamos agregar algo de código (nota: código de manejo de mensajes) en la función WndProc. La función WndProc es responsable de procesar cada mensaje que llega a la ventana. Por lo tanto, agregaremos otra rama case a esta función:
case DOT_NET_BUTTON_PRESSED:
MessageBox(NULL, TEXT("Llegó el mensaje de C#: ¡¡Botón presionado!!")),
szTitle, MB_OK | MB_ICONINFORMATION);
descanso;