控制Unity发布的PC包的窗体

大家好,我是阿赵。

用Unity发布PC包接入某些渠道时,有时候会收到一些特殊的需求,比如控制窗口最大化(比如某些情况强制显示窗体)、最小化(比如老板键)、强制规定窗体置顶等。虽然我一直认为这些需求都是流氓软件行为,但作为一个弱小的技术人员,别人有规定,我也只能去做。

所以这里分享一下一些简单的控制窗体的方法。

这里写了个测试demo,然后还有一个归纳的工具类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WindowCtrl : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        
    }

    void OnGUI()
    {
        if (OneButton("最大化"))
        {
            WindowsHelper.ShowMaxWindow();
        }
        if (OneButton("最小化"))
        {
            WindowsHelper.ShowMinWindow();
        }
        if (OneButton("还原"))
        {
            WindowsHelper.ShowRestoreWindow();
        }
        if (OneButton("锁定置顶"))
        {
            WindowsHelper.ShowTopWindow();
        }
        if (OneButton("取消置顶"))
        {
            WindowsHelper.CancelTopWindow();
        }
    }

    private bool OneButton(string content)
    {
        return GUILayout.Button(content, GUILayout.Width(100), GUILayout.Height(60));
    }

}

上面这个是demo的代码,运行的时候会出现上面截图所示的几个按钮,可以控制窗体。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

public class WindowsHelper
{

    public delegate bool WNDENUMPROC(IntPtr hwnd, uint lParam);
    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, uint lParam);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr GetParent(IntPtr hWnd);
    [DllImport("user32.dll")]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, ref uint lpdwProcessId);

    [DllImport("kernel32.dll")]
    public static extern void SetLastError(uint dwErrCode);

    [DllImport("user32.dll")]
    public static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hPos, int x, int y, int cx, int cy, uint nflags);


    const int SW_SHOWMINIMIZED = 2; //{最小化, 激活}
    const int SW_SHOWMAXIMIZED = 3;//最大化
    const int SW_SHOWRESTORE = 1;//还原


    static IntPtr HWND_TOP = new IntPtr(0);
    static IntPtr HWND_TOPMOST = new IntPtr(-1);
    static IntPtr HWND_NORMAL = new IntPtr(-2);

    private const uint SWP_NOSIZE = 0x0001;//表示此次设置不改变大小
    private const uint SWP_NOMOVE = 0x0002;//表示此次设置不改变位置



    private static IntPtr selfWindow;
    //获取当前进程的窗体句柄
    public static IntPtr GetProcessWnd()
    {
        IntPtr ptrWnd = IntPtr.Zero;
        uint pid = (uint)Process.GetCurrentProcess().Id;  // 当前进程 ID

        bool bResult = EnumWindows(new WNDENUMPROC(delegate (IntPtr hwnd, uint lParam)
        {
            uint id = 0;

            if (GetParent(hwnd) == IntPtr.Zero)
            {
                GetWindowThreadProcessId(hwnd, ref id);
                if (id == lParam)    // 找到进程对应的主窗口句柄
                {
                    ptrWnd = hwnd;   // 把句柄缓存起来
                    SetLastError(0);    // 设置无错误
                    return false;   // 返回 false 以终止枚举窗口
                }
            }

            return true;

        }), pid);

        return (!bResult && Marshal.GetLastWin32Error() == 0) ? ptrWnd : IntPtr.Zero;
    }
    //获取缓存的当前窗口
    private static IntPtr GetSelfWindow()
    {
        if(selfWindow == null||selfWindow == IntPtr.Zero)
        {
            selfWindow = GetProcessWnd();
        }
        return selfWindow;
    }

    //最大化窗口
    public static void ShowMaxWindow()
    {
        ShowWindow(GetSelfWindow(), SW_SHOWMAXIMIZED);
    }
    //最小化窗口
    public static void ShowMinWindow()
    {
        ShowWindow(GetSelfWindow(), SW_SHOWMINIMIZED);
    }
    //还原窗口
    public static void ShowRestoreWindow()
    {
        ShowWindow(GetSelfWindow(), SW_SHOWRESTORE);
    }
    //锁定窗口置顶
    public static void ShowTopWindow()
    {
        IntPtr hWnd = WindowsHelper.GetProcessWnd();
        SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
    }
    //取消锁定窗口置顶
    public static void CancelTopWindow()
    {
        IntPtr hWnd = WindowsHelper.GetProcessWnd();
        SetWindowPos(hWnd, HWND_NORMAL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);

    }
}

这个是工具类,主要用到了user32.dll,所以只能在Windows系统使用。

其中有几个方法是比较重要的,单独拿出来说一下:
1、GetProcessWnd方法

这个方法是通过当前的进程ID去获取当前应用的窗口句柄。

我从网上看了很多其他人的文章,发现有些是通过获取当前焦点的窗口,或者是通过Find方法指定窗体名称去获取窗口句柄。我觉得这些方法都不是特别的理想。

通过当前焦点获取窗口,在某些情况下是获取不到我们这个应用的窗体的,比如360游戏大厅,它的插件接入运行方式是把游戏嵌入到浏览器窗体里面的,在调用sdk初始化之前,连窗口都不显示的,更别说获取到焦点窗口了。

通过名字来获取窗体,原理上问题不大,但如果窗口不是单例,就可能会获取错误。然后把某些名字写死在代码里面,总感觉过不了自己那一关,感觉很low。

2、ShowWindow方法

这个方法可以设置窗口最大化最小化,可以通过以下枚举来指定:

const int SW_SHOWMINIMIZED = 2; //{最小化, 激活}

const int SW_SHOWMAXIMIZED = 3;//最大化

const int SW_SHOWRESTORE = 1;//还原

3、SetWindowPos

这个方法可以控制窗体的Z排序层级、位置、大小。
1.其中Z排序的参数枚举有:

HWND_BOTTOM:值为1,将窗口置于Z序的底部。如果参数hWnd标识了一个顶层窗口,则窗口失去顶级位置,并且被置在其他窗口的底部。

HWND_NOTOPMOST:值为-2,将窗口置于所有非顶层窗口之上(即在所有顶层窗口之后)。如果窗口已经是非顶层窗口则该标志不起作用。

HWND_TOP:值为0,将窗口置于Z序的顶部。

HWND_TOPMOST:值为-1,将窗口置于所有非顶层窗口之上。即使窗口未被激活窗口也将保持顶级位置。

SetWindowPos的Z排序参数有多个重载,我自己试了一下,Z参数是long类型的那种重载似乎不能达到效果,而参数类型是IntPtr 的重载是可以使用的,所以在引用方法的时候,要用

[DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hPos, int x, int y, int cx, int cy, uint nflags);

而定义枚举的时候,要这样定义:

static IntPtr HWND_TOP = new IntPtr(0);

static IntPtr HWND_TOPMOST = new IntPtr(-1);

static IntPtr HWND_NORMAL = new IntPtr(-2);

2.最后一个参数是可选项,完整的枚举有这些:

const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOZORDER = 0x0004;
const UInt32 SWP_NOREDRAW = 0x0008;
const UInt32 SWP_NOACTIVATE = 0x0010;
const UInt32 SWP_FRAMECHANGED = 0x0020;
const UInt32 SWP_SHOWWINDOW = 0x0040;
const UInt32 SWP_HIDEWINDOW = 0x0080;
const UInt32 SWP_NOCOPYBITS = 0x0100;
const UInt32 SWP_NOOWNERZORDER = 0x0200;
const UInt32 SWP_NOSENDCHANGING = 0x0400;

这些参数可以同时选择多个,用|号连接多个参数即可。比如SWP_NOSIZE | SWP_NOMOVE,这代表不改变大小,也不改变坐标。

通过不同的参数配搭,可以做出各种效果的窗体控制,各位有兴趣可以试试

相关推荐
逐·風6 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
_oP_i8 小时前
Unity Addressables 系统处理 WebGL 打包本地资源的一种高效方式
unity·游戏引擎·webgl
代码盗圣11 小时前
GODOT 4 不用scons编译cpp扩展的方法
游戏引擎·godot
Leoysq17 小时前
【UGUI】实现点击注册按钮跳转游戏场景
游戏·unity·游戏引擎·ugui
PandaQue19 小时前
《潜行者2切尔诺贝利之心》游戏引擎介绍
游戏引擎
_oP_i20 小时前
unity中 骨骼、纹理和材质关系
unity·游戏引擎·材质
Padid1 天前
Unity SRP学习笔记(二)
笔记·学习·unity·游戏引擎·图形渲染·着色器
Tp_jh1 天前
推荐一款非常好用的C/C++在线编译器
linux·c语言·c++·ide·单片机·unity·云原生
dangoxiba2 天前
[Unity Demo]从零开始制作空洞骑士Hollow Knight第十八集补充:制作空洞骑士独有的EventSystem和InputModule
游戏·unity·c#·游戏引擎·playmaker
无敌最俊朗@2 天前
unity3d————屏幕坐标,GUI坐标,世界坐标的基础注意点
开发语言·学习·unity·c#·游戏引擎