Unity window inside WPF application (XAML) not resizing

I’m trying to integrate a unity application (.exe) inside a WPF XAML application. I’ve managed to get the unity window to open inside the WPF window, but it’s stuck in the top left corner of the window and does not resize when I resize the WPF window.

Here is my code, the unity application is called (unityWindow.exe):

MainWindow.xaml
<Window x:Class="UnityApplicationIntegration.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:UnityApplicationIntegration"
        mc:Ignorable="d"
        Title="MainWindow" MinHeight="488" MinWidth="815">
       
    <Grid x:Name="unityContent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
               
    </Grid>

</Window>
MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;


using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Threading;

namespace UnityApplicationIntegration
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        [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);

        //Frame p = MainWindow.Instance.floatingFrame;
        Grid UnityContent;

        bool initialized = false;

        public MainWindow()
        {
            InitializeComponent();
            UnityContent = unityContent;
            //MainWindow.Instance.MainWindowClosing += Application_Exit;

            System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
            dispatcherTimer.Tick += attemptInit;
            dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
            dispatcherTimer.Start();
        }

        void attemptInit(object sender, EventArgs e)
        {

            if (initialized)
                return;

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

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

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

            IntPtr hWnd = source.Handle;

            try
            {
                _process = new Process();
                _process.StartInfo.FileName = "UnityWindow.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 ?!
                //hWnd = _process.MainWindowHandle;
                EnumChildWindows(hWnd, WindowEnum, IntPtr.Zero);

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

                // TODO: rename. What are the Args?
                UnityContentResize(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 UnityContentResize(object sender, EventArgs e)
        {
            MoveWindow(_unityHWND, 0, 0, (int)UnityContent.Width, (int)UnityContent.Height, true);
            Debug.WriteLine("RESIZED UNITY WINDOW TO: " + (int)UnityContent.Width + "x" + (int)UnityContent.Height);
            ActivateUnityWindow();
        }

        // Close Unity application
        private void ApplicationExit(object sender, EventArgs e)
        {
            try
            {
                _process.CloseMainWindow();

                Thread.Sleep(1000);
                while (!_process.HasExited)
                    _process.Kill();
            }
            catch (Exception)
            {

            }
        }

        private void UnityContentActivate(object sender, EventArgs e)
        {
            ActivateUnityWindow();
        }

        private void UnityContentDeactivate(object sender, EventArgs e)
        {
            DeactivateUnityWindow();
        }
    }
}

I’ve tried placing the unity window inside a content presenter like so:

<ContentPresenter x:Name="unityContent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">

</ContentPresenter>

But resizing still didn’t work.

I’ve read in many places to use WindowsFormsHost but since this is not a forms application, this does not work. Could anyone tell me where I’m going wrong? Thanks for your time.

Are you calling UnityContentResize every time you want it resized? It doesn’t appear so in the code snippet you posted.

Thanks for your reply, is there an event triggered when the WPF window is resized? If so, I’m supposed to call UnityContentResize everytime that event occurs? How can I do this?

Hi,
I inserted a SizeChanged event handler so that UnityContentResize is called whenever the window is resized. But now, when I resize the window the unity window disappears and the screen becomes blank. Any idea why this is happening?

This is my XAML now:

<Window x:Class="UnityApplicationIntegration.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:UnityApplicationIntegration"
        mc:Ignorable="d"
        Title="MainWindow" MinHeight="488" MinWidth="815">


    <Grid x:Name="unityContent" SizeChanged="UnityContentResize">
               
    </Grid>

</Window>

Thanks

Try calling just MoveWindow without ActivateUnityWindow call.

Hi,
Calling MoveWindow without ActivateUnityWindow didn’t make a difference. I managed to get it to resize by changing:
UnityContent.Width to UnityContent.ActualWidth and same for Height. But now I have blank space around the sides after the first resize like in the image attached. What is causing this?

7906264--1007719--Screenshot (76).png

Did you try logging the values you pass into Unity? Does the size actually match what you’re trying to resize it to?

Thanks! It turns out that the Unity window scale and the WPF scale were not the same. I solved the issue by adding the following to mainwindow.xaml.cs

double scaleX, scaleY;
if (s != null)
{
    scaleX = s.CompositionTarget.TransformToDevice.M11;
    scaleY = s.CompositionTarget.TransformToDevice.M22;
}

var actualHeight = (int)UnityContent.ActualHeight * scaleY;
var actualWidth = (int)UnityContent.ActualWidth * scaleX;
MoveWindow(_unityHWND, 0, 0, (int)actualWidth, (int)actualHeight, true);
1 Like