WPF嵌入Unity3D之后,unity3D程序的键盘和鼠标事件无法触发(3D程序的焦点无法激活)的解决方案

Posted Core搬砖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF嵌入Unity3D之后,unity3D程序的键盘和鼠标事件无法触发(3D程序的焦点无法激活)的解决方案相关的知识,希望对你有一定的参考价值。

目前最通用的客户端调用3D的方式,就是WPF程序通过Process启动Unity3D的exe进程,直接上代码:

 //开启3D进程
        internal void Create3DProcess(string processUri)
        {
            if (string.IsNullOrWhiteSpace(processUri) || !File.Exists(processUri))
            {
                return;
                //throw new Exception("Unable to find Unity window,File was not exit");
            }
            var handle = Panel.Handle;
            this.Dispatcher.InvokeAsync(() =>
            {
                try
                {
                  
                    //判断当前要启动的进程是否还在启动,如果还在启动,先关闭进程再创建进程
                    CheckProcessByName(processUri);

                    if (process != null)
                    {
                        process.Close();
                    }
                 
                    process = new Process();             
                    process.StartInfo.Arguments = "-parentHWND " + handle.ToInt32() + " " + Environment.CommandLine + " " + Unity3DProcessArges.Replace("AutoWidth",Panel.Width.ToString()).Replace("AutoHeight",Panel.Height.ToString());
                    process.StartInfo.FileName = processUri;
                    process.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(processUri);
                    process.StartInfo.UseShellExecute = true;
                    process.StartInfo.CreateNoWindow = true;
                    process.Start();
                    process.WaitForInputIdle();
                    EnumChildWindows(handle, WindowEnum, IntPtr.Zero);
                }
                catch (Exception ex)
                {
                    LogHelper.Error(ex.ToString());
                }
            });

        }


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

 

启动进程后,在做WPF应用上的交互,比如点击了WPF上的一个按钮,当前的鼠标的焦点会被WPF程序捕获到,如果3D程序进程没有做键盘或者鼠标焦点的获取,就会出现键盘和鼠标事件无法触发!

 

大概的解决思路:在WPF程序上,获取鼠标滑动的窗体的句柄,判断当前鼠标停留的窗体的句柄如果跟3D程序的窗体句柄一样,则激活3D窗体程序(需要用到user32的API),直接上代码:

 1    private IntPtr unityHWND = IntPtr.Zero; //3D窗体的句柄
 2         /// <summary>
 3         /// 发送消息,触发激活当前窗体
 4         /// </summary>
 5         internal void ActiveWindows()
 6         {
 7          
 8             if (unityHWND!=IntPtr.Zero)
 9             {
10                 SendMessage(unityHWND, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero);
11             }
12         }
13 
14         /// <summary>
15         /// 解除激活窗体
16         /// </summary>
17         internal void UnActiveWindows()
18         {
19             if (unityHWND!=IntPtr.Zero)
20             {
21                 SendMessage(unityHWND, WM_ACTIVATE, WA_INACTIVE, IntPtr.Zero);
22             }
23         }
24 
25 
26 //检测窗体句柄
27    private void CheckWindowsActive()
28         {
29             actionTime = new DispatcherTimer();
30             actionTime.Interval = new TimeSpan(TimeSpan.TicksPerMillisecond * 300);
31             actionTime.Tick += ActionTime_Tick;
32             actionTime.Start();
33         }
34 
35         IntPtr lastIntprt = IntPtr.Zero; //获取上一次的鼠标指向的句柄,为了指向相同的位置,避免重复给窗体发送消息
36         private void ActionTime_Tick(object sender, EventArgs e)
37         {
38             try
39             {
40                 POINT pOINT;
41                 bool isSuccess = GetCursorPos(out pOINT);
42                 var intptr = WindowFromPoint(pOINT);
43                 if (isSuccess && unityHWND != IntPtr.Zero && lastIntprt != intptr)//判断当前句柄跟上一次的句柄是否一样,如果一样就不再触发
44                 {
45                     if (intptr == unityHWND)
46                     {
47                         ActiveWindows();
48                     }
49                     else
50                     {
51                         UnActiveWindows();
52                     }
53                    
54                 }
55                 lastIntprt = intptr;
56                
57             }
58             catch (Exception ex)
59             {
60                 LogHelper.Error(ex.ToString());
61             }
62         }
63 
64         [DllImport("user32.dll", CharSet = CharSet.Auto)]
65         public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
66 
67         [DllImport("user32.dll", CharSet = CharSet.Auto)]
68         public static extern bool GetCursorPos(out POINT pt);
69 
70         [DllImport("user32.dll")]
71        public static extern IntPtr WindowFromPoint(POINT Point);
72 
73       public struct POINT
74         {
75             public int X;
76             public int Y;
77             public POINT(int x, int y)
78             {
79                 this.X = x;
80                 this.Y = y;
81             }
82         }

 

以上是关于WPF嵌入Unity3D之后,unity3D程序的键盘和鼠标事件无法触发(3D程序的焦点无法激活)的解决方案的主要内容,如果未能解决你的问题,请参考以下文章

在 WPF 应用程序中嵌入 Unity3D 应用程序

求助,WPF嵌入unity3D并实现WPF与unity3D之间的通信

unity3d 能否通过控件嵌入其他软件中

wpf unity 需要啥环境

将一个 Unity .exe 文件嵌入到多个 WPF 页面中

ios原生嵌入unity