1

I am using the code posted in this question with some minor changes: Embed Unity3D app inside WPF *without* having it take up the whole window

This approach worked great, but when I am looking at my task manager processes. My main WPF executable name is removed from the process when I exit the page running the unity player and load another page. The application still works just fine. Task Manager just shows an icon with no process name next to it. This is only an issue because I have another background service that monitor my WPF application and starts and stops it remotely based on its name. Any suggestion?

Page Code Shown Below.

    public System.Windows.Forms.Integration.WindowsFormsHost host = new System.Windows.Forms.Integration.WindowsFormsHost();
    [DllImport("User32.dll")]
    static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);

    internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
    [DllImport("user32.dll")]
    internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);

    [DllImport("user32.dll")]
    static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

    private Process process;
    private IntPtr unityHWND = IntPtr.Zero;

    private const int WM_ACTIVATE = 0x0006;
    private readonly IntPtr WA_ACTIVE = new IntPtr(1);
    private readonly IntPtr WA_INACTIVE = new IntPtr(0);
    System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
    bool initialized = false;
    public UnityPlayer()
    {
        InitializeComponent();
        if (this.grid1.Children.Count == 0)
        {
            this.grid1.Children.Add(host);
        }


        dispatcherTimer.Tick += attemptInit;
        dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
        dispatcherTimer.Start();

    }

    void attemptInit(object sender, EventArgs e)
    {
        if (process != null) 
        {
            if (process.HasExited)
            {
                Thread.Sleep(1000);
                FSC.GoHome();
            }

        }

        if (initialized)
            return;

        HwndSource source = (HwndSource)HwndSource.FromVisual(host);

        Console.WriteLine("attempting to get handle...");

        if (source == null)
        {
            Console.WriteLine("Failed to get handle source");
            return;
        }

        IntPtr hWnd = source.Handle;

        try
        {
            process = new Process();
            process.StartInfo.FileName = "Block Breaker.exe";
            process.StartInfo.Arguments = "-parentHWND " + hWnd.ToInt32() + " " + Environment.CommandLine;

            process.StartInfo.UseShellExecute = true;
            process.StartInfo.CreateNoWindow = true;

            process.Start();

            process.WaitForInputIdle();
            // Doesn't work for some reason ?!
            //unityHWND = process.MainWindowHandle;
            EnumChildWindows(host.Handle, WindowEnum, IntPtr.Zero);

            //unityHWNDLabel.Text = "Unity HWND: 0x" + unityHWND.ToString("X8");
            Console.WriteLine("Unity HWND: 0x" + unityHWND.ToString("X8"));

            panel1_Resize(this, EventArgs.Empty);

            initialized = true;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message + ".\nCheck if Container.exe is placed next to UnityGame.exe.");
        }
    }

    private void ActivateUnityWindow()
    {
        SendMessage(unityHWND, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero);
    }

    private void DeactivateUnityWindow()
    {
        SendMessage(unityHWND, WM_ACTIVATE, WA_INACTIVE, IntPtr.Zero);
    }

    private int WindowEnum(IntPtr hwnd, IntPtr lparam)
    {
        unityHWND = hwnd;
        ActivateUnityWindow();
        return 0;
    }

    private void panel1_Resize(object sender, EventArgs e)
    {
        MoveWindow(unityHWND, 0, 0, (int)host.Width, (int)host.Height, true);
        Console.WriteLine("RESIZED UNITY WINDOW TO: " + (int)host.Width + "x" + (int)host.Height);
        ActivateUnityWindow();
    }


    private void Page_Unloaded(object sender, RoutedEventArgs e)
    {
        dispatcherTimer.Stop();
        dispatcherTimer = null;
        host.Dispose();
    }

Image Show Process Before the pages loads

Image show Process While the page is running

Image show Process After the pages exits and loads new page

Tim
  • 11
  • 2
  • Is the process.HasExited block getting called? It might be better to use the process.exited event. https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.exited?view=netframework-4.7.2 – Leo Bartkus Feb 17 '19 at 16:59

0 Answers0