Unity调用Windows API备忘

发布于 2021-05-18  502 次阅读


最近接了个麻将游戏项目,需要用到一些透明的异形窗体。然后就需要用Interop库提供的功能引入一些windows api来调用。

首先要把unity的窗口做成有透明部分需要至少调用2个主要API,一个是SetWindowLong,一个是DwmExtendFrameIntoClientArea。前者是设置窗口显示模式,需要加上WS_EX_LAYERED风格。后者是设置可绘制客户区,把标题栏也归入可绘制控制区域。类似这样的调用方式:

var margins = new MARGINS() { cxLeftWidth = -1 };
SetWindowLong(s_hwnd, -16, WS_EX_LAYERED | WS_POPUP | WS_VISIBLE);
DwmExtendFrameIntoClientArea(s_hwnd, ref margins);

随后需要离开半透明载入的场景后停止窗口的透明状态,需要重新调用一次SetWindowLong和DwmExtendFrameIntoClientArea

            var margins = new MARGINS() {};
            SetLayeredWindowAttributes(s_hwnd, 0, 255, LWA_ALPHA);
            SetWindowLong(s_hwnd, -16, WS_THICKFRAME | WS_VISIBLE);
            DwmExtendFrameIntoClientArea(s_hwnd, ref margins);

另外加上的这行对SetLayeredWindowAttributes的调用,是把窗体的透明度恢复为不透明,如果没有这个调用,在unity里半透明渲染的东西可能会和unity窗口之后的屏幕内容发生混色,非常的难看。

然后是获取当前窗口句柄的方法:

string UnityWindowClassName = "UnityWndClass";
uint threadId = GetCurrentThreadId();
EnumThreadWindows(threadId, (hWnd, lParam) =>
{
    var classText = new StringBuilder(UnityWindowClassName.Length + 1);
    GetClassName(hWnd, classText, classText.Capacity);
    if (classText.ToString() == UnityWindowClassName)
    {
        s_hwnd = hWnd;
        return false;
    }
    return true;
}, IntPtr.Zero);

这样s_hwnd就存入了当前unity窗口的句柄。注意这里只对已经打包后的PC WINDOWS端有效,对于编辑器里的窗口是无效的,因为注册的类名就不同。

windows api中的char*类型参数可以用StringBuilder来获取,注意要多预留一个字节的容量用于存储C风格字符串的结尾字符“\0”

如果你需要获取其他类型的窗口,可以打开spy++去查找窗口,记录下窗口的类名就可以用上面的方法获取句柄了,很通用。还可以结合窗体的标题名。

具体的常量数值可以参考windows的头文件或者msdn手册。



点击数:285


一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。