WndProc not called in hosted process in wpf

0

Following the instructions in:

How to run an application inside wpf application?

and in the walkthrough in MSDN (https://msdn.microsoft.com/en-us/library/ms752055.aspx)

I have managed to host my console applications in wpf. (Note: there are more than 2 applications to be hosted)

In ControlHost.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
using System.Windows.Interop;

namespace Try
{
public class ControlHost : HwndHost
{
    private static List<Process> _procList = new List<Process>();
    IntPtr hwndControl;
    int hostHeight, hostWidth;
    string filePath;

    internal const int
    WS_CHILD = 0x40000000,
    GWL_STYLE = -16,
    WS_CAPTION = 0x00C00000,
    WS_THICKFRAME = 0x00040000;

    [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.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
    internal static extern bool DestroyWindow(IntPtr hwnd);

    public ControlHost(double height, double width, string filePathName)
    {
          hostHeight = (int)height;
          hostWidth = (int)width;
          filePath = filePathName;
    }

    protected override HandleRef BuildWindowCore(HandleRef hwndParent)
    {
        Process _process = new Process();
        _process.StartInfo.FileName = filePath;
        _process.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
        _process.Start();
        _procList.Add(_process);

        // The main window handle may be unavailable for a while, just wait for it
        while (_process.MainWindowHandle == IntPtr.Zero)
        {
            Thread.Yield();
        }

        hwndControl = _process.MainWindowHandle;

        int style = GetWindowLong(hwndControl, 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

        SetWindowLong(hwndControl, GWL_STYLE, style);
        SetParent(hwndControl, hwndParent.Handle);

        this.InvalidateVisual();

        HandleRef hwnd = new HandleRef(this, hwndControl);
        return hwnd;
    }

    protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        handled = false;
        return IntPtr.Zero;
    }

    protected override void DestroyWindowCore(HandleRef hwnd)
    {
        DestroyWindow(hwnd.Handle);
        if (_procList != null)
        {
            foreach (Process p in _procList)
            {
                if (p != null)
                {
                    try
                    {
                        while (!p.HasExited)
                        {
                            p.Refresh();
                            p.CloseMainWindow();
                            p.Kill();
                            Thread.Sleep(10);
                        }
                    }
                    catch
                    {

                    }
                }
            }
        }
    }

    public void Stop(IntPtr Hwnd)
    {
        HandleRef hwnd = new HandleRef(this, Hwnd);

        DestroyWindow(hwnd.Handle);
        if (_procList != null)
        {
            foreach (Process p in _procList)
            {
                if (p != null)
                {
                    try
                    {
                        while (!p.HasExited)
                        {
                            p.Refresh();
                            p.CloseMainWindow();
                            p.Kill();
                            Thread.Sleep(10);
                        }
                    }
                    catch
                    {

                    }
                }
            }
        }
    }
}
}

and in MainWindow.xaml.cs:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
   using System.Windows.Shapes;
    using Microsoft.Win32;

namespace Try
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private string[] appsList;
        private List<ControlHost> ctrlHostList = new List<ControlHost>();
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ControlHost appsControl;
            Border appsBorder;
            foreach (string app in appsList)
            {
                appsBorder = new Border();
                appsBorder.Height = double.NaN;
                appsBorder.Width = double.NaN;
                appsBorder.BorderBrush = Brushes.Silver;
                appsBorder.BorderThickness = new Thickness(1);
                appsControl = new ControlHost(appsBorder.ActualHeight, appsBorder.ActualWidth, app);
                ctrlHostList.Add(appsControl);
                appsBorder.Child = appsControl;
                WP_Apps.Children.Add(appsBorder);
            }
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            OpenFileDialog _openFileDlg = new OpenFileDialog();
            _openFileDlg.Multiselect = true;

            if (_openFileDlg.ShowDialog() == true)
            {
                appsList = _openFileDlg.FileNames;
            }
        }

        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            foreach(ControlHost CH in ctrlHostList)
            {
                CH.Stop(CH.Handle);
            }
        }
    }
}

and finally in MainWindow.xaml:

<Window x:Class="Try.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="1502" Width="1500" ResizeMode="CanMinimize" WindowStartupLocation="CenterScreen" WindowState="Maximized" >
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="76*"/>
            <ColumnDefinition Width="671*"/>
        </Grid.ColumnDefinitions>
        <Button Content="Start" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Height="20" Click="Button_Click"/>
        <Button Content="GetApps" HorizontalAlignment="Left" Height="21" Margin="10,54,0,0" VerticalAlignment="Top" Width="74" Click="Button_Click_1"/>
        <ScrollViewer Grid.Column="1">
            <WrapPanel Name="WP_Apps" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" />
        </ScrollViewer>
        <Button Content="Stop" HorizontalAlignment="Left" Margin="10,99,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_2"/>

    </Grid>
</Window>

First, I will get the apps by clicking the "Get Apps" buttons than click "Start" The result will look like this:

enter image description here

But only one hosted application accept user inputs. (In this example, its the 1st application, circled in red who have can have user inputs) The other 2 does not accept any user input. Nothing is triggered when clicking on the other 2 applications.

I know I have not handled a lot of situations. But that did not influence the problem that I am having at the moment, i presumed. This is a simple application (not the real application) that i wrote, which i hope that someone will be able to reproduce the same error that I have.

Is there anything that I'm doing wrong? Or have I missed something? Any suggestions would be greatly appreciated. Thanks in advance!

wpf
wpf-controls
hwndhost
asked on Stack Overflow Jun 11, 2015 by Kai • edited May 23, 2017 by Community

1 Answer

0

I found a workaround for this problem,

that is to use WindowsFormsHost to host the console application as in this example.

I created a System.Windows.Forms.Panel and set it as a Child of the WindowsFormsHost and once again add it into the children of a Wrap Panel.

Somehow, all the hosted applications are able to receive user inputs.

As to why, using HwndHost and Border to host console applications, do not receive user inputs, I still do not know why.

But I think its because:

  1. I set the hosted application's window style to Child which makes it unable to receive user inputs. (I know I have read it somewhere, but forgot where)
  2. WndProc only receives Form messages, it does not have access to/catch the messages going in consoles.

These are the 2 reasons that I can think of that are causing the hosted applications not being able to receive user inputs.

Correct me if I'm wrong.

Best Regards,
Kai

answered on Stack Overflow Jun 16, 2015 by Kai • edited May 23, 2017 by Community

User contributions licensed under CC BY-SA 3.0