wpfexewindowsformshostwindows-api-code-pack

Hosting an EXE in WPF seamlessly


I've currently been given the task of hosting a bunch of old VB6 exes insdide WPF while the seemingly infinite task or refactoring is taking place.

Hosting the EXE wasn't too tricky at all but making the process seamless has.

Currently the flow is: 1) Create View 2) Inject VM 3) Start Process and fire EXE. 4) WaitForInputIdle 5) SetParent 6) SetWindowLong 7) SetWindowPos

The issue I am getting is that with this method as far as I am aware the process has to load naturally before it can have SetParent called on it. This means that there is a flicker of the application before it lays to rest in the WPF control.
I would like to find a way to get rids of this, is there a method to open the process purely in memory or hidden (I have UseShellExecute set to true and WindowStyle = ProcessWindowStyle.Minimized).

Possibly there is a different way to open the process entirely which I am unaware of.

ViewModel Constructor

 public ShellViewModel()
    {
        WindowWidth = 600;
        WindowHeight = 500;
        MainTitle = "Main Title";
        BreadCrumb = "NA";
        IsLoading = true;

        Task.Factory.StartNew(() =>
        {
            //Process p = OpenProcess(@"C:\\Windows\\System32\\notepad.exe");
            Process p = OpenProcess(@"C:\\Program Files\\WinRAR\\WinRar.exe");

            return p;

        }).ContinueWith(r =>
        {
            try
            {
                Process p = r.Result;

                _handler = p.MainWindowHandle;
                _host.Child = _panel;
                Content = _host;

                int dwStyle = GetWindowLong(_handler, GWL_STYLE);
                SetParent(_handler, _panel.Handle);
                SetWindowLong(_handler, GWL_STYLE, new IntPtr(dwStyle & ~WS_CAPTION & ~WS_THICKFRAME));
                SetWindowPos(_handler, IntPtr.Zero, 0, 0, (int) Math.Round(WindowWidth), (int) Math.Round(WindowHeight) - 106, SWP_FRAMECHANGED);

                BreadCrumb += " Host: " + p.Id.ToString(CultureInfo.InvariantCulture);

                IsLoading = false;
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }

        }, UiTaskSchedulerHelper.Instance.UiTaskScheduler);
    }

Opening the process

private Process OpenProcess(string path)
    {

        Process p = null;
        lock (locked)
        {
            ProcessStartInfo psi = new ProcessStartInfo();
            psi.FileName = path;
            psi.UseShellExecute = true;
            psi.WindowStyle = ProcessWindowStyle.Minimized;
            p = Process.Start(psi);
            BreadCrumb += " OpenProcess: " + p.Id;

            p.EnableRaisingEvents = true;
            p.WaitForInputIdle();

        }
        return p;
    }

Uploaded the sample to github, feel free to update it.

https://github.com/OliDow/FormHostPoc/tree/master/FormHostPoc


Solution

  • So a day of pair programing and we got it loading as intended. The repo works correctly. We also had to set the loading VB6 Apps to start minimised in a config file.