Home > Mobile >  Unity window inside WPF application (XAML) not resizing
Unity window inside WPF application (XAML) not resizing

Time:02-24

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.

EDIT: I managed to get the grid with the unity window to resize by changing: MoveWindow(_unityHWND, 0, 0, (int)UnityContent.Width, (int)UnityContent.Height, true);

to

MoveWindow(_unityHWND, 0, 0, (int)UnityContent.ActualWidth, (int)UnityContent.ActualHeight, true);

But now, after the first resize I get blank space on the sides of the unity window. I need it to fill all available space. Please see the image.Blank space around unity window

CodePudding user response:

I fixed this issue by doing 2 things:

  1. By changing MoveWindow(_unityHWND, 0, 0, (int)UnityContent.Width, (int)UnityContent.Height, true);

to

MoveWindow(_unityHWND, 0, 0, (int)UnityContent.ActualWidth, (int)UnityContent.ActualHeight, true);

  1. The unity window was not the same scale as the WPF window, so I had to add 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);
  • Related