控制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,这代表不改变大小,也不改变坐标。

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

相关推荐
一只一只40 分钟前
Unity之Invoke
unity·游戏引擎·invoke
技术小甜甜2 小时前
【Godot】【入门】信号系统从 0 到 1(UI/玩法彻底解耦的通用写法)
ui·游戏引擎·godot
技术小甜甜4 小时前
【Godot】【入门】节点生命周期怎么用(避免帧循环乱写导致卡顿的范式)
游戏引擎·godot
tealcwu4 小时前
【Unity踩坑】Simulate Touch Input From Mouse or Pen 导致检测不到鼠标点击和滚轮
unity·计算机外设·游戏引擎
ThreePointsHeat4 小时前
Unity WebGL打包后启动方法,部署本地服务器
unity·游戏引擎·webgl
erxij4 小时前
【游戏引擎之路】《古今东西4》正式立项——新的一年,开始长征
游戏引擎
迪普阳光开朗很健康4 小时前
UnityScrcpy 可以让你在unity面板里玩手机的插件
unity·游戏引擎
陈言必行1 天前
Unity 之 设备性能分级与游戏画质设置与设备自动适配指南
游戏·unity·游戏引擎
CreasyChan1 天前
Unity DOTS技术栈详解
unity·c#·游戏引擎
gshh__1 天前
SuperMap Hi-Fi 3D SDK for Unreal 如何实现横断面分析
3d·ue5·游戏引擎·supermap