unity3d 使用-parentHWND在WPF中嵌入Unity

whlutmcx  于 2023-03-23  发布在  其他
关注(0)|答案(1)|浏览(341)

我是WPF的新手,正在尝试重新创建the unity docs的EmbeddedWindow示例的功能,该示例解释了如何使用命令行和-parentHWND在WPF窗口内启动独立的Unity窗口。
该示例使用以下行(在WinForm?中):process.StartInfo.Arguments = "-parentHWND " + panel1.Handle.ToInt32() + " " + Environment.CommandLine;
我试着用以下代码来代替:
process.StartInfo.Arguments = "-parentHWND " + canvas1.Parent + " " + Environment.CommandLine;
因为我不知道还能怎么做。
当我尝试在WPF中启动程序时,我收到错误:“创建窗口失败”
有人能给予我一个代码的例子,如何在WPF中正确使用这个命令行?我如何从MDL调用SetParent作为文档的要求?

k3bvogb1

k3bvogb11#

你必须使用HwndHost类。下面是我使用的代码:

class UnityHwndHost : HwndHost
{
    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", SetLastError = true)]
    internal static extern uint GetWindowThreadProcessId(IntPtr hwnd, out uint processId);
    [DllImport("user32.dll", EntryPoint = "GetWindowLong")] // TODO: 32/64?
    internal static extern IntPtr GetWindowLongPtr(IntPtr hWnd, Int32 nIndex);
    internal const Int32 GWLP_USERDATA = -21;
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    internal static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    internal static extern IntPtr PostMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    internal const UInt32 WM_CLOSE = 0x0010;

    private string programName;
    private string arguments;
    private Process process = null;
    private IntPtr unityHWND = IntPtr.Zero;

    public UnityHwndHost(string programName, string arguments = "")
    {
        this.programName = programName;
        this.arguments = arguments;
    }

    protected override HandleRef BuildWindowCore(HandleRef hwndParent)
    {
        Debug.WriteLine("Going to launch Unity at: " + this.programName + " " + this.arguments);
        process = new Process();
        process.StartInfo.FileName = programName;
        process.StartInfo.Arguments = arguments + (arguments.Length == 0 ? "" : " ") + "-parentHWND " + hwndParent.Handle;
        process.StartInfo.UseShellExecute = true;
        process.StartInfo.CreateNoWindow = true;

        process.Start();
        process.WaitForInputIdle();

        int repeat = 50;
        while (unityHWND == IntPtr.Zero && repeat-- > 0)
        {
            Thread.Sleep(100);
            EnumChildWindows(hwndParent.Handle, WindowEnum, IntPtr.Zero);
        }
        if (unityHWND == IntPtr.Zero)
            throw new Exception("Unable to find Unity window");
        Debug.WriteLine("Found Unity window: " + unityHWND);

        repeat += 150;
        while ((GetWindowLongPtr(unityHWND, GWLP_USERDATA).ToInt32() & 1) == 0 && --repeat > 0)
        {
            Thread.Sleep(100);
            Debug.WriteLine("Waiting for Unity to initialize... "+repeat);
        }
        if (repeat == 0)
        {
            Debug.WriteLine("Timed out while waiting for Unity to initialize");
        }
        else
        {
            Debug.WriteLine("Unity initialized!");
        }

        return new HandleRef(this, unityHWND);
    }

    private int WindowEnum(IntPtr hwnd, IntPtr lparam)
    {
        if (unityHWND != IntPtr.Zero)
            throw new Exception("Found multiple Unity windows");
        unityHWND = hwnd;
        return 0;
    }

    protected override void DestroyWindowCore(HandleRef hwnd)
    {
        Debug.WriteLine("Asking Unity to exit...");
        PostMessage(unityHWND, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);

        int i = 30;
        while (!process.HasExited)
        {
            if (--i < 0)
            {
                Debug.WriteLine("Process not dead yet, killing...");
                process.Kill();
            }
            Thread.Sleep(100);
        }
    }
}

你使用它的方式是首先在XML中创建一些容器,然后每当你想启动Unity时,将其添加为子对象:

UnityDisplay.Child = new UnityHwndHost("UnityApp.exe", "-possibly -other -arguments");

另请参阅有关Win32和WPF互操作的文档。

相关问题