visual usar studio nacio escritorio donde documentacion diseƱo como aplicaciones aplicacion c# wpf winapi hwndhost

c# - usar - documentacion de wpf



Alojamiento de aplicaciones externas en la ventana de WPF (6)

Bueno ... si la pregunta se hubiera planteado hace 20 años, uno debería responder: "¡Claro, mira ''OLE''!", Aquí hay un enlace a lo que es "Enlace e incrustación de objetos":

http://en.wikipedia.org/wiki/Object_Linking_and_Embedding

Si lees este artículo, verás la cantidad de interfaces que esta especificación definió, no porque su autor haya pensado que era divertido, sino porque técnicamente es difícil de lograr en los casos generales.

De hecho, sigue siendo compatible con algunas aplicaciones (principalmente de Microsoft, ya que Microsoft era casi el único patrocinador de OLE ...)

Puede incrustar estas aplicaciones utilizando algo llamado DSOFramer (consulte los enlaces aquí en SO: MS KB311765 y DsoFramer faltan en el sitio MS ), un componente que le permite alojar servidores OLE (es decir, aplicaciones externas que se ejecutan como otro proceso) visualmente dentro de una aplicación . Es una especie de gran truco que Microsoft dejó escapar hace unos años, que ya no es compatible hasta el punto de que los binarios son bastante difíciles de encontrar.

Puede (todavía) funcionar para servidores OLE simples, pero creo que leí en alguna parte que ni siquiera funciona para nuevas aplicaciones de Microsoft como Word 2010. Entonces, puede usar DSOFramer para aplicaciones que lo soportan. Puedes probarlo.

Para otras aplicaciones, bueno, hoy, en el mundo moderno en el que vivimos, no aloja aplicaciones , ejecuta procesos externos, aloja componentes y, en general, se supone que se ejecutan en el proceso. Es por eso que tendrá grandes dificultades para hacer lo que quiere hacer en general . Un problema que enfrentará (y no menos con las versiones recientes de Windows) es la seguridad: ¿cómo puede su proceso en el que no confío manejar legítimamente mis ventanas y menús creados por mi proceso :-)?

Aún así, puede hacer bastante aplicación por aplicación, utilizando varios hack de Windows. SetParent es básicamente la madre de todos los hacks :-)

Aquí hay un fragmento de código que amplía la muestra que señala, agrega cambio de tamaño automático y la eliminación del cuadro de título. Demuestra cómo eliminar implícitamente la caja de control, el menú del sistema, como un ejemplo:

public partial class Window1 : Window { private System.Windows.Forms.Panel _panel; private Process _process; public Window1() { InitializeComponent(); _panel = new System.Windows.Forms.Panel(); windowsFormsHost1.Child = _panel; } [DllImport("user32.dll")] private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); [DllImport("user32.dll", SetLastError = true)] private static extern int GetWindowLong(IntPtr hWnd, int nIndex); [DllImport("user32")] private static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent); [DllImport("user32")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags); private const int SWP_NOZORDER = 0x0004; private const int SWP_NOACTIVATE = 0x0010; private const int GWL_STYLE = -16; private const int WS_CAPTION = 0x00C00000; private const int WS_THICKFRAME = 0x00040000; private void button1_Click(object sender, RoutedEventArgs e) { button1.Visibility = Visibility.Hidden; ProcessStartInfo psi = new ProcessStartInfo("notepad.exe"); _process = Process.Start(psi); _process.WaitForInputIdle(); SetParent(_process.MainWindowHandle, _panel.Handle); // remove control box int style = GetWindowLong(_process.MainWindowHandle, GWL_STYLE); style = style & ~WS_CAPTION & ~WS_THICKFRAME; SetWindowLong(_process.MainWindowHandle, GWL_STYLE, style); // resize embedded application & refresh ResizeEmbeddedApp(); } protected override void OnClosing(System.ComponentModel.CancelEventArgs e) { base.OnClosing(e); if (_process != null) { _process.Refresh(); _process.Close(); } } private void ResizeEmbeddedApp() { if (_process == null) return; SetWindowPos(_process.MainWindowHandle, IntPtr.Zero, 0, 0, (int)_panel.ClientSize.Width, (int)_panel.ClientSize.Height, SWP_NOZORDER | SWP_NOACTIVATE); } protected override Size MeasureOverride(Size availableSize) { Size size = base.MeasureOverride(availableSize); ResizeEmbeddedApp(); return size; } }

Esto es básicamente todos los hacks "tradicionales" de Windows. También puede eliminar los menús de elementos que no le gustan, como se explica aquí: http://support.microsoft.com/kb/110393/en-us (Cómo quitar elementos de menú del cuadro de menú de control de un formulario).

También puede reemplazar "notepad.exe" por "winword.exe" y parece que funciona. Pero hay limitaciones para esto (teclado, mouse, enfoque, etc.).

¡Buena suerte!

Estamos desarrollando un administrador de diseño en WPF que tiene ventanas gráficas que pueden ser movidas / redimensionadas / etc. por un usuario. Las ventanas gráficas normalmente están llenas de datos (imágenes / películas / etc.) a través de proveedores que están bajo nuestro control en el administrador de diseño. Mi trabajo es examinar si también es posible alojar cualquier aplicación externa de Windows (es decir, bloc de notas, calc, adobe reader, etc.) en una ventana gráfica. Me encuentro con una serie de problemas.

La mayoría de los recursos apuntan a usar la clase HwndHost. Estoy experimentando con este tutorial de la propia Microsoft: http://msdn.microsoft.com/en-us/library/ms752055.aspx

Lo he adaptado para que el cuadro de lista se reemplace con el identificador de Windows desde la aplicación externa. ¿Alguien puede ayudarme con estas preguntas?

  1. El tutorial agrega una subventana estática adicional en la que se coloca el ListBox . No creo que necesite eso para aplicaciones externas. Si lo hago, tengo que convertir la aplicación externa en una ventana secundaria (usando Get / SetWindowLong de user32.dll para establecer GWL_STYLE como WS_CHILD ). Pero si hago eso, la barra de menú de la aplicación desaparece (debido al estilo WS_CHILD ) y ya no recibe entrada.
  2. Si utilizo la ventana secundaria y hago que la aplicación externa sea un niño, las cosas funcionan de forma razonable, pero a veces la aplicación externa no se ve bien.
  3. Además, necesito la ventana secundaria para cambiar el tamaño de la ventana gráfica. es posible?
  4. Cuando la aplicación exernal genera una ventana secundaria (es decir, Bloc de notas-> Ayuda-> Acerca de), esta ventana no está alojada en HwndHost (y por lo tanto se puede mover fuera de la ventana gráfica). ¿Hay alguna manera de prevenir eso?
  5. Como no necesito más interacción entre la aplicación externa y el administrador de diseño, ¿tengo razón al asumir que no necesito capturar y reenviar mensajes? (el tutorial agrega un HwndSourceHook a la ventana secundaria para detectar cambios de selección en el cuadro de lista).
  6. Cuando ejecuta el ejemplo (no modificado) VS2010 y cierra la ventana, VS2010 no ve que el programa haya finalizado. Si te rompes todo, terminas ensamblado sin fuente. Algo mal está sucediendo, pero no puedo encontrarlo.
  7. El tutorial en sí parece ser muy descuidado, pero no he encontrado ninguna documentación mejor sobre este tema. ¿Algún otro ejemplo?
  8. Otro enfoque es no usar HwndHost sino WindowsFormHost como se explica here . Funciona (¡y es mucho más simple!) Pero no tengo control sobre el tamaño de la aplicación. Además, WinFormHost no es realmente para esto?

Gracias por cualquier información en la dirección correcta.


Después de leer las respuestas en este hilo y hacer un poco de prueba y error, terminé con algo que funciona bastante bien, pero por supuesto algunas cosas necesitarán su atención para casos especiales.

Usé el HwndHostEx como clase base para mi clase de host, puedes encontrarlo aquí: http://microsoftdwayneneed.codeplex.com/SourceControl/changeset/view/69631#1034035

Código de ejemplo:

public class NotepadHwndHost : HwndHostEx { private Process _process; protected override HWND BuildWindowOverride(HWND hwndParent) { ProcessStartInfo psi = new ProcessStartInfo("notepad.exe"); _process = Process.Start(psi); _process.WaitForInputIdle(); // The main window handle may be unavailable for a while, just wait for it while (_process.MainWindowHandle == IntPtr.Zero) { Thread.Yield(); } HWND hwnd = new HWND(_process.MainWindowHandle); int style = NativeMethods.GetWindowLong(hwnd, GWL.STYLE); style = style & ~((int)WS.CAPTION) & ~((int)WS.THICKFRAME); // Removes Caption bar and the sizing border style |= ((int)WS.CHILD); // Must be a child window to be hosted NativeMethods.SetWindowLong(hwnd, GWL.STYLE, style); return hwnd; } protected override void DestroyWindowOverride(HWND hwnd) { _process.CloseMainWindow(); _process.WaitForExit(5000); if (_process.HasExited == false) { _process.Kill(); } _process.Close(); _process.Dispose(); _process = null; hwnd.Dispose(); hwnd = null; } }

El HWND, NativeMethods y enumeraciones provienen también de la biblioteca DwayneNeed (Microsoft.DwayneNeed.User32).

Simplemente agregue NotepadHwndHost como un niño en una ventana de WPF y debería ver la ventana del bloc de notas alojada allí.


La respuesta de Simon Mourier está extremadamente bien escrita. Sin embargo, cuando lo probé con una aplicación winform hecha por mí, falló.

_process.WaitForInputIdle();

puede ser reemplazado por

while (_process.MainWindowHandle==IntPtr.Zero) { Thread.Sleep(1); }

y todo va bien.

Gracias por la gran pregunta y todos ustedes por sus respuestas.


La solución es increíblemente complicada. Montones de código Aquí hay algunos consejos.

Primero, estás en el camino correcto.

Tienes que usar lo de HwndHost y HwndSource. Si no lo haces, obtendrás artefactos visuales. Como parpadeo. Una advertencia, si no usas el Host y la Fuente, parecerá que funcionará, pero al final no tendrá errores pequeños y estúpidos.

Echa un vistazo a esto para algunos consejos. No está completo, pero te ayudará a ir en la dirección correcta. http://microsoftdwayneneed.codeplex.com/SourceControl/changeset/view/50925#1029346

Tienes que entrar en Win32 para controlar mucho de lo que estás preguntando. Necesita capturar y reenviar mensajes. Debes controlar qué ventanas son "propias" de las ventanas secundarias.

Use Spy ++ mucho.



Tengo esto funcionando en producción y hasta ahora muy bien en una aplicación WPF. Asegúrese de llamar a SetNativeWindowInWPFWindowAsChild() desde el subproceso de la interfaz de usuario que posee la window .

public static bool SetNativeWindowInWPFWindowAsChild(IntPtr hWndNative, Window window) { UInt32 dwSyleToRemove = WS_POPUP | WS_CAPTION | WS_THICKFRAME; UInt32 dwExStyleToRemove = WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE; UInt32 dwStyle = GetWindowLong(hWndNative, GWL_STYLE); UInt32 dwExStyle = GetWindowLong(hWndNative, GWL_EXSTYLE); dwStyle &= ~dwSyleToRemove; dwExStyle &= ~dwExStyleToRemove; SetWindowLong(hWndNative, GWL_STYLE, dwStyle | WS_CHILD); SetWindowLong(hWndNative, GWL_EXSTYLE, dwExStyle); IntPtr hWndOld = SetParent(hWndNative, new WindowInteropHelper(window).Handle); if (hWndOld == IntPtr.Zero) { System.Diagnostics.Debug.WriteLine("SetParent() Failed -> LAST ERROR: " + Marshal.GetLastWin32Error() + "/n"); } return hWndOld != IntPtr.Zero; }

Aquí está la API nativa Win32 que utilicé. (Hay extras aquí porque dimensiono / enfoco la ventana después de establecerla)

[StructLayout(LayoutKind.Sequential)] private struct RECT { public Int32 left; public Int32 top; public Int32 right; public Int32 bottom; } [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); [DllImport("user32.dll")] private static extern UInt32 SetWindowLong(IntPtr hWnd, int nIndex, UInt32 dwNewLong); [DllImport("user32.dll")] private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex); [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport("user32.dll")] private static extern IntPtr SetFocus(IntPtr hWnd); [DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags); private static int GWL_STYLE = -16; private static int GWL_EXSTYLE = -20; private static UInt32 WS_CHILD = 0x40000000; private static UInt32 WS_POPUP = 0x80000000; private static UInt32 WS_CAPTION = 0x00C00000; private static UInt32 WS_THICKFRAME = 0x00040000; private static UInt32 WS_EX_DLGMODALFRAME = 0x00000001; private static UInt32 WS_EX_WINDOWEDGE = 0x00000100; private static UInt32 WS_EX_CLIENTEDGE = 0x00000200; private static UInt32 WS_EX_STATICEDGE = 0x00020000; [Flags] private enum SetWindowPosFlags : uint { SWP_ASYNCWINDOWPOS = 0x4000, SWP_DEFERERASE = 0x2000, SWP_DRAWFRAME = 0x0020, SWP_FRAMECHANGED = 0x0020, SWP_HIDEWINDOW = 0x0080, SWP_NOACTIVATE = 0x0010, SWP_NOCOPYBITS = 0x0100, SWP_NOMOVE = 0x0002, SWP_NOOWNERZORDER = 0x0200, SWP_NOREDRAW = 0x0008, SWP_NOREPOSITION = 0x0200, SWP_NOSENDCHANGING = 0x0400, SWP_NOSIZE = 0x0001, SWP_NOZORDER = 0x0004, SWP_SHOWWINDOW = 0x0040 } private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); private static readonly IntPtr HWND_TOP = new IntPtr(0); private static readonly IntPtr HWND_BOTTOM = new IntPtr(1);