Hosting external app in WPF window

Well… if the question had been posed like 20 years ago, one would have answer, “Sure, look at ‘OLE’!”, here is a link to what is “Object Linking and Embedding”:

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

If you read this article, you will see the number of interfaces this spec defined, not because its author thought it was fun, but because it’s technically difficult to achieve in the general cases

It’s actually still supported by some apps (mostly Microsoft ones, as Microsoft was almost the only sponsor of OLE…)

You can embed these apps using something called DSOFramer (see links here on SO: MS KB311765 and DsoFramer are missing from MS site), a component that allows you to host OLE server (ie: external apps running as another process)
visually inside an application. It’s some kind of a big hack Microsoft let out a few years ago, that is not supported anymore to the point that the binaries are quite difficult to find!

It (may) still works for simple OLE servers, but I think I read somewhere it does not even work for new Microsoft applications such as Word 2010.
So, you can use DSOFramer for application that support it. You can try it.

For others applications, well, today, in the modern world we live in, you don’t host applications, ran in external process, you host components, and they are in general supposed to run inprocess.
That’s why you will have great difficulties to do what you want to do in general. One problem you will face (and not the least with recent versions of Windows) is security: how can your process I don’t trust can legitimately handle my windows and menus created by my process 🙂 ?

Still, you can do quite a lot application by application, using various Windows hack.
SetParent is basically the mother of all hacks 🙂

Here is a piece of code that extends the sample you point, adding automatic resize, and the removal of the caption box.
It demonstrates how to implicitely remove the control box, the system menu, as an example:

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;
    }
}

This is basically all Windows “traditional” hacks. You could also remove item menus you don’t like, as explained here: http://support.microsoft.com/kb/110393/en-us (How to Remove Menu Items from a Form’s Control-Menu Box).

You can also replace “notepad.exe” by “winword.exe” and it seems to work. But there are limitations to this (keyboard, mouse, focus, etc.).

Good luck!

Leave a Comment