【Unity】用Windows Api控制窗口置顶、窗口风格等操作

文章不用看了,因为发现了更好用的插件,用起来很方便:https://github.com/kirurobo/uniwindowcontroller

-----------------------------------文章分隔线--------------------------------------

 WindowTool.cs如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;

/// <summary>
/// 改变游戏窗口的风格、大小、层级
/// </summary>
public static class WindowTool
{
    #region Win32Api

    [DllImport("User32.dll")]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);

    [DllImport("user32.dll")]
    private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hPos, int x, int y, int cx, int cy, uint nflags);
    [DllImport("User32.dll")]
    private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
    [DllImport("User32.dll")]
    private static extern int GetWindowLong(IntPtr hWnd, int dwNewLong);

    [DllImport("user32.dll")]
    private static extern int ShowWindow(IntPtr hwnd, int nCmdShow);

    [DllImport("User32.dll")]
    private static extern IntPtr GetSystemMetrics(int nIndex);

    #endregion

    /// <summary>
    /// 窗口风格
    /// </summary>
    public static class WindowStyle
    {
        public const uint WS_BORDER = 0x00800000,
        WS_CAPTION = 0x00C00000,
        WS_CHILD = 0x40000000,
        WS_CHILDWINDOW = 0x40000000,
        WS_CLIPCHILDREN = 0x02000000,
        WS_CLIPSIBLINGS = 0x04000000,
        WS_DISABLED = 0x08000000,
        WS_DLGFRAME = 0x00400000,
        WS_GROUP = 0x00020000,
        WS_HSCROLL = 0x00100000,
        WS_ICONIC = 0x20000000,
        WS_MAXIMIZE = 0x01000000,
        WS_MAXIMIZEBOX = 0x00010000,
        WS_MINIMIZE = 0x20000000,
        WS_MINIMIZEBOX = 0x00020000,
        WS_OVERLAPPED = 0x00000000,
        WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
        WS_POPUP = 0x80000000,
        WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU,
        WS_SIZEBOX = 0x00040000,
        WS_SYSMENU = 0x00080000,
        WS_TABSTOP = 0x00010000,
        WS_THICKFRAME = 0x00040000,
        WS_TILED = 0x00000000,
        WS_TILEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
        WS_VISIBLE = 0x10000000,
        WS_VSCROLL = 0x00200000;
    }

    /// <summary>
    /// 窗口扩展风格
    /// </summary>
    public static class WindowStyleEx
    {
        public const uint WS_EX_ACCEPTFILES = 0x00000010,
        WS_EX_APPWINDOW = 0x00040000,
        WS_EX_CLIENTEDGE = 0x00000200,
        WS_EX_COMPOSITED = 0x02000000,
        WS_EX_CONTEXTHELP = 0x00000400,
        WS_EX_CONTROLPARENT = 0x00010000,
        WS_EX_DLGMODALFRAME = 0x00000001,
        WS_EX_LAYERED = 0x00080000,
        WS_EX_LAYOUTRTL = 0x00400000,
        WS_EX_LEFT = 0x00000000,
        WS_EX_LEFTSCROLLBAR = 0x00004000,
        WS_EX_LTRREADING = 0x00000000,
        WS_EX_MDICHILD = 0x00000040,
        WS_EX_NOACTIVATE = 0x08000000,
        WS_EX_NOINHERITLAYOUT = 0x00100000,
        WS_EX_NOPARENTNOTIFY = 0x00000004,
        WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
        WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
        WS_EX_RIGHT = 0x00001000,
        WS_EX_RIGHTSCROLLBAR = 0x00000000,
        WS_EX_RTLREADING = 0x00002000,
        WS_EX_STATICEDGE = 0x00020000,
        WS_EX_TOOLWINDOW = 0x00000080,
        WS_EX_TOPMOST = 0x00000008,
        WS_EX_TRANSPARENT = 0x00000020,
        WS_EX_WINDOWEDGE = 0x00000100;
    }

    private const int GWL_STYLE = -16;//表示与他相关的参数是窗口风格
    private const int GWL_EXSTYLE = -20;//表示与他相关的参数是窗口扩展风格

    //表示用于Win32Api的SetWindowPos方法的某个参数
    private const uint SWP_NOSIZE = 0x0001;//表示此次设置不改变大小
    private const uint SWP_NOMOVE = 0x0002;//表示此次设置不改变位置
    private const uint SWP_NOZORDER = 0x0004;//表示此次设置不改变ZOrder
    private const uint SWP_FRAMECHANGED = 0x0020;
    private const uint SWP_SHOWWINDOW = 0x0040;

    /// <summary>
    /// 窗口类型
    /// </summary>
    public enum WindowType
    {
        ExclusiveFullScreen,//独占全屏
        FullScreenWindow,//窗口全屏
        ResizableWindow,//普通可调节大小的窗口
        FixedSizeWindow,//固定大小的窗口
    }

    /// <summary>
    /// 窗口的Z排序设置。
    /// </summary>
    public enum ZOrder
    {
        /// <summary>
        /// 让窗口变为当时的最顶层,相当于给窗口设置了一个"置顶"标志,
        /// 与其他有这个标志的窗口竞争最顶层的位置(鼠标点击可切换哪个窗口成为当时的最顶层),
        /// 所有带这个标志的窗口处在所有不带这个标志的窗口的上面,离用户更近。
        /// </summary>
        TopMost = -1,

        /// <summary>
        /// 取消窗口的"置顶"标志,于是这个窗口就变成了普通窗口,置顶窗口们就不和它一起玩了,它之后便和其他普通窗口一桌竞争了。
        /// 这个设置只对本来就是置顶窗口的窗口有用,对普通窗口没效果。
        /// </summary>
        NoTopMost = -2,

        /// <summary>
        /// 将窗口移动到普通窗口的顶部,依然处在置顶窗口们的下面,依然是普通窗口,不会一直待在顶部,会在以后鼠标点来点去的时候跑到其他窗口下面。
        /// </summary>
        Top = 0,

        /// <summary>
        /// 将窗口移动到普通窗口的底部。其他与Top同理。
        /// </summary>
        Bottom = 1,
    }

    private static IntPtr _hWndSelf = new IntPtr(0);//自己的窗口句柄
    public static IntPtr hWndSelf
    {
        get
        {
            #if UNITY_EDITOR

            #elif UNITY_STANDALONE_WIN

            if (_hWndSelf.ToInt32() == 0)
            {
                Debug.Log("窗口句柄为0,无法操作窗口。");
            }

            #endif
            return _hWndSelf;
        }
    }

    public static void Init() 
    {
#if UNITY_EDITOR
        _hWndSelf = new IntPtr(0);//在编辑器里面,让窗口句柄为0,这样就相当于没有指定窗口,调用Win32Api就不会有任何效果。
#elif UNITY_STANDALONE_WIN
        _hWndSelf = FindWindow(null, Application.productName);//打Windows包之后才有效果
#endif
    }

    public static void SetWindow(int width, int height, WindowType windowType, ZOrder zOrder = ZOrder.NoTopMost)
    {
        switch (windowType)
        {
            case WindowType.ExclusiveFullScreen:
                //独占全屏的时候,其他TopMost的窗口无法出现在它上面
                Screen.SetResolution(width, height, FullScreenMode.ExclusiveFullScreen);
                break;

            case WindowType.FullScreenWindow:
                CoroutineManager.Instance.StartCoroutine(SetFullScreenWindow(width, height));
                break;

            case WindowType.ResizableWindow:                
                CoroutineManager.Instance.StartCoroutine(SetResizableWindow(width, height, zOrder));                
                break;

            case WindowType.FixedSizeWindow:
                CoroutineManager.Instance.StartCoroutine(SetFixedSizeWindow(width, height, zOrder));    
                break;

            default:
                break;
        }
    }


    /// <summary>
    /// 设置为窗口全屏模式。在做实验的时候,发现有时从FullScreenMode.ExclusiveFullScreen转到FullScreenMode.FullScreenWindow(比如从720P到更高分辨率)
    /// 要两次才能成功(第一次没有完全成功)(可能与Screen.SetResolution的效果实际执行时刻有关?),所以使用协程执行两次。
    /// </summary>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <returns></returns>
    private static IEnumerator SetFullScreenWindow(int width, int height)
    {       
        //好像FullScreenMode.FullScreenWindow这种模式ZOrder默认就是TopMost,不用改。
        Screen.SetResolution(width, height, FullScreenMode.FullScreenWindow);
        yield return new WaitUntil(() => { return Screen.fullScreenMode == FullScreenMode.FullScreenWindow; });
        Screen.SetResolution(width, height, FullScreenMode.FullScreenWindow);
    }

    /// <summary>
    /// 设置为可调整大小的窗口模式
    /// </summary>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <returns></returns>
    private static IEnumerator SetResizableWindow(int width, int height, ZOrder zOrder = ZOrder.NoTopMost)
    {
        //SetWindowLong(hWndSelf, GWL_STYLE, (int)WindowStyle.WS_OVERLAPPEDWINDOW);
        //SetSizeAndZOrder(width, height, zOrder);
        //直接使用SetWindowLong进行设置可能并不准,因为Unity做的可能比我想象的更多,
        //使用Screen.SetResolution进行设置的时候,可能Unity做了很多操作,
        //比如会劫持某些窗口消息(比如能控制窗口大小变化的消息),而直接使用SetWindowLong可能无法消除Unity的干扰,
        //于是可以像下面一样先使用Screen.SetResolution方法,再使用SetWindowLong方法添加可调节大小的属性(或者减少某些属性)。

        //这样操作默认是设置为固定大小、不可调节大小的窗口
        Screen.SetResolution(width, height, FullScreenMode.Windowed);
        yield return new WaitUntil(() => { return Screen.fullScreenMode == FullScreenMode.Windowed; });
        //为窗口风格添加可调节大小以及激活最大化按钮的风格
        SetWindowLong(hWndSelf, GWL_STYLE, (int)(GetWindowLong(hWndSelf, GWL_STYLE) | WindowStyle.WS_SIZEBOX | WindowStyle.WS_MAXIMIZEBOX));
        SetZOrder(zOrder);
    }

    /// <summary>
    /// 设置为固定尺寸,不可调整大小的窗口模式
    /// </summary>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <returns></returns>
    private static IEnumerator SetFixedSizeWindow(int width, int height, ZOrder zOrder = ZOrder.NoTopMost)
    {
        Screen.SetResolution(width, height, FullScreenMode.Windowed);
        yield return new WaitUntil(() => { return Screen.fullScreenMode == FullScreenMode.Windowed; });
        //为窗口风格删除可调节大小以及激活最大化按钮的风格
        SetWindowLong(hWndSelf, GWL_STYLE, (int)(GetWindowLong(hWndSelf, GWL_STYLE) & ~WindowStyle.WS_SIZEBOX & ~WindowStyle.WS_MAXIMIZEBOX));
        SetZOrder(zOrder);
    }

    public static void SetSizeAndZOrder(int width, int height, ZOrder zOrder)
    {
        SetWindowPos(hWndSelf, new IntPtr((int)zOrder), 0, 0, width, height, SWP_NOMOVE | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
    }

    /// <summary>
    /// 设置的是整个窗口的大小。而非客户区(也就是游戏画面)的尺寸的大小。
    /// </summary>
    /// <param name="width"></param>
    /// <param name="height"></param>
    public static void SetSize(int width, int height)
    {
        SetWindowPos(hWndSelf, IntPtr.Zero, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
    }

    public static void SetZOrder(ZOrder zOrder)
    {
        SetWindowPos(hWndSelf, new IntPtr((int)zOrder), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
    }

    /// <summary>
    /// 设置为置顶窗口
    /// </summary>
    public static void SetTopMost()
    {
        SetZOrder(ZOrder.TopMost);
    }

    /// <summary>
    /// 取消窗口置顶
    /// </summary>
    public static void CancelTopMost()
    {
        SetZOrder(ZOrder.NoTopMost);
    }
}

里面使用的协程管理器CoroutineManager.cs如下:

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

/// <summary>
/// 协程管理
/// </summary>
public class CoroutineManager : MonoBehaviour
{
    public static CoroutineManager Instance;
    private void Awake()
    {
        Instance = this;
    }
}

使用方法:刚开始的时候初始化一次:WindowTool.Init() ;

以后可以直接调用WindowTool.SetWindow()等方法进行设置。

千言万语都在代码注释里面。代码比较浅显,不一定好用,但勉强能用。

  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在Unity中将窗口强制置顶,我们可以使用以下代码来实现: 首先,在需要实现强制置顶的脚本中,引入以下命名空间: ```csharp using System; using System.Runtime.InteropServices; ``` 然后,在脚本的Awake或Start方法中,调用下面的方法: ```csharp void Awake() { SetWindowPos(GetUnityWindowHandle(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } ``` 接下来,我们需要在脚本中定义这三个调用Win32 API函数的方法: ```csharp [DllImport("user32.dll")] private static extern IntPtr GetActiveWindow(); [DllImport("user32.dll", EntryPoint = "FindWindow")] private static extern IntPtr FindWindow(string className, string windowName); [DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); ``` 最后,我们还需要定义一些常量: ```csharp private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); private static readonly uint SWP_NOMOVE = 0x0002; private static readonly uint SWP_NOSIZE = 0x0001; ``` 这样,当游戏运行时,Unity窗口将会被强制置顶。 需要注意的是,这种方法只适用于Windows平台,而且可能与其他操作系统的窗口管理器产生冲突。另外,强制置顶窗口可能会干扰用户的操作,并且违反了一些用户体验设计原则,所以在使用之前请慎重考虑。 ### 回答2: Unity窗口强制置顶是指将Unity程序窗口设置为始终位于屏幕最顶层的状态。在Unity开发中,有时我们需要保持游戏窗口始终置顶,这样可以确保游戏在其他窗口的前面显示,提供更好的游戏体验。 在Unity中,实现窗口强制置顶的方法有多种途径。以下是其中一种简单的实现方式: 1. 首先,在Unity的脚本中创建一个新的C#脚本文件,命名为"WindowManager"。 2. 打开WindowManager脚本,添加以下代码实现窗口强制置顶的功能: ``` using System; using System.Runtime.InteropServices; using UnityEngine; public class WindowManager : MonoBehaviour { // 导入Windows API库,用于设置窗口的显示状态 [DllImport("user32.dll")] private static extern IntPtr SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags); // 定义常量,用于设置窗口状态和显示层级 private const int HWND_TOPMOST = -1; // 窗口置于最顶层 private const uint SWP_SHOWWINDOW = 0x0040; // 显示窗口的常量值 void Start() { // 获取Unity窗口的句柄(handle) IntPtr handle = GetUnityWindowHandle(); // 将窗口置于最顶层 SetWindowPos(handle, (IntPtr)HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW); } // 获取Unity窗口的句柄 private IntPtr GetUnityWindowHandle() { #if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN IntPtr handle = GetActiveWindow(); return handle; #else Debug.LogError("平台不支持"); return IntPtr.Zero; #endif } // 导入Windows API库,获取活动窗口句柄 [DllImport("user32.dll")] private static extern IntPtr GetActiveWindow(); } ``` 3. 在Unity编辑器中,将WindowManager脚本添加到一个空的GameObject上。 4. 运行游戏,Unity窗口将会始终置顶显示。 需要注意的是,该方法仅适用于Windows平台和Unity编辑器,对于其他平台可能需要使用不同的方法实现窗口置顶的功能。使用该方法时,要确保在发布游戏之前检查平台兼容性,并提供替代方案以确保在不同平台上能够正确运行。 ### 回答3: Unity可以使用以下代码将窗口强制置顶: ```csharp using System; using System.Runtime.InteropServices; using UnityEngine; public class WindowUtil : MonoBehaviour { #if UNITY_STANDALONE_WIN // 导入Windows API函数 [DllImport("user32.dll", EntryPoint = "SetWindowPos")] private static extern bool SetWindowPos(IntPtr hwnd, int hwndInsertAfter, int x, int y, int cx, int cy, int flags); // 定义窗口层级 private const int HWND_TOPMOST = -1; private const int SWP_NOMOVE = 0x0002; private const int SWP_NOSIZE = 0x0001; private void Start() { // 获取当前Unity窗口的句柄 IntPtr hwnd = GetUnityWindowHandle(); // 设置窗口大小和位置 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } // 获取Unity窗口句柄 private IntPtr GetUnityWindowHandle() { IntPtr hwnd = IntPtr.Zero; // 获取Unity窗口句柄 if (Application.platform == RuntimePlatform.WindowsEditor) { hwnd = GetForegroundWindow(); } else if (Application.platform == RuntimePlatform.WindowsPlayer) { hwnd = GetActiveWindow(); } return hwnd; } // 导入Windows API函数 [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr GetActiveWindow(); [DllImport("user32.dll", EntryPoint = "GetForegroundWindow")] private static extern IntPtr GetForegroundWindow(); #endif } ``` 以上代码使用Windows API函数SetWindowPos来设置窗口的层级,将其置顶。通过获取Unity窗口的句柄,可以在Windows平台上实现窗口强制置顶的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值