Unity は WPF に埋め込まれた exe を公開します (マウスとキーボードは正常に反応します)

Unity は WPF に埋め込まれた exe を公開します (マウスとキーボードが利用可能)

Visual Studioのバージョン 2019年
オペレーティング·システム ウィンドウズ10

1. 新しい WPF プロジェクトを作成する

  1. VS を開いて新しいプロジェクトを作成します
    新しい WPF プロジェクトを作成する
  2. 作成完了後、Unity が公開している exe を WPF に埋め込みたい場合、マウスとキーボードが利用可能な場合は、WPF プロジェクトのキャリアとして winform UserControl コントロールを使用する必要があるため、以下の図には 2 つの DLL があります。WindowsFormsIntegration.dll を参照できない場合は、 「WPF で Windows.System.Forms.Integration を参照する方法」の記事を参照してください。
  3. ここに画像の説明を挿入

2. WPF の .xaml ファイルに以下を追加します。

 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.unityのexeプログラムをwinformコントロールに埋め込む

  1. エンティティクラスを追加します。コンストラクターは WinForm コントロールをパラメーターとして渡します。その後、コントロールは Resize イベントをサブスクライブする必要があります。これにより、exe フォームが再アクティブ化されます。
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. 本質的には、Win32 アプリケーションを WPF アプリケーションに組み込む問題ですが、両者のウィンドウ描画原理の違いにより、Win32API、つまり皆さんがよく言う user32.dll に依存する必要があります。C# での Win32Api の使用は C++ とは少し異なります。参照 user32.dll を単純にカプセル化するために DllInput を使用する必要があります。コードは次のとおりです。
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. WPF プロジェクトの .xaml ファイルでの操作

  1. .xaml ファイルに追加します

    <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. コードビハインド
        private void Button_Click(object sender, RoutedEventArgs e)
        {
    
    
            AppContainer container = new AppContainer(this.host);

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

ここで、UnityApp は設定した名前、ストレージ パス: bin/Debug/ に置き換えることができます。

4.Unityでは公開前に設定が必要です

ここに画像の説明を挿入

5. 症例のダウンロード

事例ダウンロード

おすすめ

転載: blog.csdn.net/qq_46641769/article/details/119609975
おすすめ