Unity publishes exe embedded in WPF (the mouse and keyboard respond normally)

Unity publishes exe embedded in WPF (mouse and keyboard are available)

Visual Studio version 2019
operating system Windows10

1. Create a new WPF project

  1. Open VS to create a new project
    Create a new WPF project
  2. After the creation is completed, if you want to embed the exe released by unity into WPF, and the mouse and keyboard are available, you need to use the winform UserControl control as the carrier in the WPF project, so you need to reference the two DLLs in the figure below. If WindowsFormsIntegration.dll cannot be referenced, please see the article: How to reference Windows.System.Forms.Integration in WPF
  3. insert image description here

2. Add the following to the .xaml file in WPF

 xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
 
 <WindowsFormsHost Margin="67,0,0,0" Grid.RowSpan="2">
            <wf:Panel x:Name="host"/>
        </WindowsFormsHost>

3. Embed the exe program of unity into the winform control

  1. Add an entity class. The constructor will pass in the WinForm control as a parameter, and then this control needs to subscribe to the Resize event, which reactivates the exe form
public class AppContainer
    {
    
    
        private System.Windows.Forms.Panel _hostPanel;
        private readonly ManualResetEvent _eventDone = new ManualResetEvent(false);
        private Process _process = null;
        internal IntPtr _embededWindowHandle;

        public AppContainer(System.Windows.Forms.Panel panel)
        {
    
    
            this._hostPanel = panel;
            this._hostPanel.Resize += _hostPanel_Resize;
        }


        private void _hostPanel_Resize(object sender, EventArgs e)
        {
    
    
            SetBounds();
        }

        public void ActivateWindow()
        {
    
    
            if (_process == null)
                return;

            if (_process.MainWindowHandle == IntPtr.Zero)
                return;

            Win32Api.SendMessage(_process.MainWindowHandle, Win32Api.WM_ACTIVATE, Win32Api.WA_ACTIVE, IntPtr.Zero);
        }

        public void SetBounds()
        {
    
    
            SetBounds(_hostPanel.Width, _hostPanel.Height);
        }

        public void SetBounds(int width, int height)
        {
    
    
            if (_process == null)
                return;

            if (_process.MainWindowHandle == IntPtr.Zero)
                return;

            if (width <= 0 || height <= 0)
                return;

            Win32Api.MoveWindow(_process.MainWindowHandle, 0, 0, width, height, true);

            ActivateWindow();//激活
        }

        public bool StartAndEmbedProcess(string processPath)
        {
    
    
            if (null != _process)
                return true;

            var isStartAndEmbedSuccess = false;
            _eventDone.Reset();

            // Start the process 
            ProcessStartInfo info = new ProcessStartInfo(processPath);
            info.WindowStyle = ProcessWindowStyle.Maximized;//默认最大化,不弹出界面。
            info.Arguments = $"-popupwindow";//Unity的命令行参数

            _process = Process.Start(info);

            if (_process == null)
            {
    
    
                return false;
            }

            // Wait for process to be created and enter idle condition 
            _process.WaitForInputIdle();

            // Get the main handle
            var thread = new Thread(() =>
            {
    
    
                while (true)
                {
    
    
                    if (_process.MainWindowHandle != (IntPtr)0)
                    {
    
    
                        _eventDone.Set();
                        break;
                    }
                    Thread.Sleep(10);
                }
            });
            thread.Start();

            //嵌入进程
            if (_eventDone.WaitOne(10000))
            {
    
    
                isStartAndEmbedSuccess = EmbedApp(_process);
                if (!isStartAndEmbedSuccess)
                {
    
    
                    CloseApp(_process);
                }
            }
            return isStartAndEmbedSuccess;
        }

        public bool EmbedExistProcess(Process process)
        {
    
    
            _process = process;
            return EmbedApp(process);
        }

        /// <summary>
        /// 将外进程嵌入到当前程序
        /// </summary>
        /// <param name="process"></param>
        private bool EmbedApp(Process process)
        {
    
    
            //是否嵌入成功标志,用作返回值
            var isEmbedSuccess = false;
            //外进程句柄
            var processHwnd = process.MainWindowHandle;
            //容器句柄
            var panelHwnd = _hostPanel.Handle;

            if (processHwnd != (IntPtr)0 && panelHwnd != (IntPtr)0)
            {
    
    
                //把本窗口句柄与目标窗口句柄关联起来
                var setTime = 0;
                while (!isEmbedSuccess && setTime < 50)
                {
    
    
                    // Put it into this form
                    isEmbedSuccess = Win32Api.SetParent(processHwnd, panelHwnd) != 0;
                    Thread.Sleep(10);
                    setTime++;
                }

                // Remove border and whatnot
                //Win32Api.SetWindowLong(processHwnd, Win32Api.GWL_STYLE, Win32Api.WS_CHILDWINDOW | Win32Api.WS_CLIPSIBLINGS | Win32Api.WS_CLIPCHILDREN | Win32Api.WS_VISIBLE);

                SetBounds();

                //Move the window to overlay it on this window
                //Win32Api.MoveWindow(_process.MainWindowHandle, 0, 0, (int)ActualWidth, (int)ActualHeight, true);
            }

            if (isEmbedSuccess)
            {
    
    
                _embededWindowHandle = _process.MainWindowHandle;
            }

            return isEmbedSuccess;
        }

        /// <summary>
        /// 关闭进程
        /// </summary>
        /// <param name="process"></param>
        private void CloseApp(Process process)
        {
    
    
            if (process != null && !process.HasExited)
            {
    
    
                process.Kill();
            }
        }

        public void CloseProcess()
        {
    
    
            CloseApp(_process);
            _process = null;
        }
    }
  1. In essence, this is a problem of embedding a Win32 application into a WPF application. Due to the difference in the window drawing principles of the two, it is necessary to rely on Win32API, which is the user32.dll that everyone often mentions. The use of Win32Api in C# is slightly different from C++. It needs to use DllInput to simply encapsulate the reference user32.dll. The code is as follows:
public class Win32Api
    {
    
    
        public const int WM_KEYDOWN = 0x0100;
        [DllImport("User32.dll", EntryPoint = "PostMessage")]
        public static extern int PostMessage(
        IntPtr hWnd,   //   handle   to   destination   window   
              int Msg,   //   message   
              IntPtr wParam,   //   first   message   parameter   
              IntPtr lParam   //   second   message   parameter   
              );

        [DllImport("USER32", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
        public static extern IntPtr GetSystemMenu(IntPtr WindowHandle, int bReset);

        [DllImport("User32.dll")]
        internal static extern int GetMenuItemCount(IntPtr hMenu);

        [DllImport("USER32", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
        public static extern int AppendMenuW(IntPtr MenuHandle, int Flags, int NewID, String Item);

        [DllImport("USER32", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
        public static extern int InsertMenuW(IntPtr hMenu, int Position, int Flags, int NewId, String Item);

        [DllImport("User32.dll")]
        internal static extern bool EnableMenuItem(IntPtr hMenu, Int32 uIDEnableItem, Int32 uEnable);

        [DllImport("user32.dll")]
        internal static extern bool DeleteMenu(IntPtr hMenu, uint uPosition, uint uFlags);

        [DllImport("User32.dll")]
        internal static extern int DrawMenuBar(IntPtr hWnd);

        internal const UInt32 MF_ENABLED = 0x00000000;
        internal const UInt32 MF_GRAYED = 0x00000001;
        internal const UInt32 MF_DISABLED = 0x00000002;
        internal const UInt32 MF_BYCOMMAND = 0x00000000;
        internal const UInt32 MF_BYPOSITION = 0x00000400;

        [DllImport("user32.dll")]
        static extern IntPtr SetActiveWindow(IntPtr hWnd);

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true,
           CharSet = CharSet.Unicode, ExactSpelling = true,
           CallingConvention = CallingConvention.StdCall)]
        public static extern long GetWindowThreadProcessId(long hWnd, long lpdwProcessId);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

        [DllImport("user32.dll")]
        public static extern uint GetWindowLong(IntPtr hwnd, int nIndex);

        [DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
        public static extern uint SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);

        [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
        public static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, int dwNewLong);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);

        [DllImport("user32.dll", EntryPoint = "ostMessageA", SetLastError = true)]
        public static extern bool PostMessage(IntPtr hwnd, uint Msg, uint wParam, uint lParam);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr GetParent(IntPtr hwnd);

        [DllImport("user32.dll", EntryPoint = "ShowWindow", SetLastError = true)]
        public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);

        public const int SWP_NOOWNERZORDER = 0x200;
        public const int SWP_NOREDRAW = 0x8;
        public const int SWP_NOZORDER = 0x4;
        public const int SWP_SHOWWINDOW = 0x0040;
        public const int WS_EX_MDICHILD = 0x40;
        public const int SWP_FRAMECHANGED = 0x20;
        public const int SWP_NOACTIVATE = 0x10;
        public const int SWP_ASYNCWINDOWPOS = 0x4000;
        public const int SWP_NOMOVE = 0x2;
        public const int SWP_NOSIZE = 0x1;
        public const int GWL_STYLE = (-16);
        public const int WS_VISIBLE = 0x10000000;
        public const int WS_MAXIMIZE = 0x01000000;
        public const int WS_BORDER = 0x00800000;
        public const int WM_CLOSE = 0x10;
        public const int WS_CHILD = 0x40000000;
        public const int WS_POPUP = -2147483648;
        public const int WS_CLIPSIBLINGS = 0x04000000;

        public const int SW_HIDE = 0; //{隐藏, 并且任务栏也没有最小化图标}
        public const int SW_SHOWNORMAL = 1; //{用最近的大小和位置显示, 激活}
        public const int SW_NORMAL = 1; //{同 SW_SHOWNORMAL}
        public const int SW_SHOWMINIMIZED = 2; //{最小化, 激活}
        public const int SW_SHOWMAXIMIZED = 3; //{最大化, 激活}
        public const int SW_MAXIMIZE = 3; //{同 SW_SHOWMAXIMIZED}
        public const int SW_SHOWNOACTIVATE = 4; //{用最近的大小和位置显示, 不激活}
        public const int SW_SHOW = 5; //{同 SW_SHOWNORMAL}
        public const int SW_MINIMIZE = 6; //{最小化, 不激活}
        public const int SW_SHOWMINNOACTIVE = 7; //{同 SW_MINIMIZE}
        public const int SW_SHOWNA = 8; //{同 SW_SHOWNOACTIVATE}
        public const int SW_RESTORE = 9; //{同 SW_SHOWNORMAL}
        public const int SW_SHOWDEFAULT = 10; //{同 SW_SHOWNORMAL}
        public const int SW_MAX = 10; //{同 SW_SHOWNORMAL}

        public const int WM_SETTEXT = 0x000C;

        public const int WM_ACTIVATE = 0x0006;
        public static readonly IntPtr WA_ACTIVE = new IntPtr(1);
        public static readonly IntPtr WA_INACTIVE = new IntPtr(0);
    }

3. Operations in the .xaml file of the WPF project

  1. In the .xaml file add

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition />
        </Grid.RowDefinitions>

        <Grid Grid.Row="0">
            <Button Margin="0,59,727,-65" Content="打开unity程序" Click="Button_Click" HorizontalAlignment="Center" Width="65"/>
        </Grid>

        <WindowsFormsHost Margin="67,0,0,0" Grid.RowSpan="2">
            <wf:Panel x:Name="host"/>
        </WindowsFormsHost>
    </Grid>
  1. code behind
        private void Button_Click(object sender, RoutedEventArgs e)
        {
    
    
            AppContainer container = new AppContainer(this.host);

            container.StartAndEmbedProcess(System.Windows.Forms.Application.StartupPath + @"\UnityApp\UnityApp.exe");
        }

Here UnityApp can be replaced with the name you set, storage path: bin/Debug/

4. In unity, you need to set up before publishing

insert image description here

5. Case download

Case download

Guess you like

Origin blog.csdn.net/qq_46641769/article/details/119609975