【Unity】用Windows Api控制窗口置顶、窗口风格等操作_unity 窗口置顶_Thechuang的博客-程序员宅基地

技术标签: c#  unity  unity3d  windows  

文章不用看了,因为发现了更好用的插件,用起来很方便: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()等方法进行设置。

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Thechuang/article/details/119494721

智能推荐

台式电脑怎么添加计算机硬盘,台式机如何添加硬盘扩大存储空间|如何对新添加的硬盘进行分区..._看你真有意思的博客-程序员宅基地

‍如果是家庭使用的电脑,很多人会选择台式机,在使用的过程中随着存储量不断增加,电脑系统里可用空间也越来越少,我们在不想让原有硬盘提前下岗的情况下,就可以在电脑上添加一个硬盘来扩大系统的磁盘容量。下面介绍如何在台式机上添加硬盘扩大存储空间。1、打开电脑机箱,用螺丝把新硬盘固定到硬盘槽里;2、给新装的硬盘插上电源插头,用sata串口硬盘线把硬盘的sata接口和主板上的sata接口连接;3、启动电脑,新..._给台式机加硬盘

break、continue、return 跳转语句的异同_说明跳转语句break、continue和return语句区别;-程序员宅基地

跳转语句比较Java 语言中支持3种类型的跳转语句:break 语句、continue 语句、return 语句。使用这些跳转语句可以把控制转移到循环甚至程序的其他部分。break 语句break 语句在循环中的作用是终止当前循环,在switch 语句中的作用是终止switch。实例请实现输出数字1到10,若遇到4的倍数程序自动退出。// break 的使用public static void main(String[] args) { for (int i=1;i<10_说明跳转语句break、continue和return语句区别;

关于子进程继承父进程属性的小问题总结-程序员宅基地

今天看书,看到用fork创建子进程的一段样例代码:#include #include #include int main(void){ pid_t pid; char* msg; int k; printf("Process Creation Study\

Mysql——操作mysql外键错误(ERROR:1833)_used in a foreign key constraint 'project manageme-程序员宅基地

前言: 错误发生在tornado项目一个接口操作mysql数据表时候,插入时候报了:pymysql.err.InternalError: (1364, "Field 'up_user_id' doesn't have a default value"),很明显插入时候缺少一个默认值,但是这个字段原则上是不需要传的,所以需要修改为自增字段; 然后数据库中修改这个表字段为自..._used in a foreign key constraint 'project management department 1

NI Multisim 14.0蜂鸣器为什么不响_Fe-Ni-Mn日案例分享吴航案例分享-程序员宅基地

Fe-Ni-Mn 日案例分享吴航案例分享_multisim的蜂鸣器不叫

残差网络ResNet的实现与数据测试-程序员宅基地

残差网络的实现与数据测试参考文献为什么提出残差网络随着卷积神经网络的发展和普及,网络深度和架构研究早已经成为人们常见的问题,所以,现在卷积神经网络的趋势发展趋势就是:足够深、足够广。我们就考虑网络足够深,是不是效果会更好?经过许多科研大佬们研究,也发现随着网络的深入,一些经典的问题也就随之出现,例如梯度弥散和梯度爆炸。在这科普一下什么交梯度弥散和梯度爆炸。下面看看网友们给出的解释:梯度...

随便推点

iText例子-程序员宅基地

参考:http://itextpdf.com/book/examples.phpdaniel@daniel-mint ~/latex/linux/itext/daniel $ cat HelloWorldNarrow.java import java.io.FileOutputStream;import java.io.IOException; import com...._5.5.12 2000-2017 itext group nv (agpl-version)

C++程序设计实验报告(三十二)---第三周任务二_参考实验指导书第三章的实验任务 2-程序员宅基地

第三周报告2-1::实验目的:找出程序错误,并改正。实验内容:运行时正确输入时分秒。* 程序头部注释开始* 程序的版权和版本声明部分* Copyright (c) 2012烟台大学计算机学院学生* All rights reserved.* 文件名称: array.cpp * 作 者: 刘镇 * 完成日期: 2012 年 3 月 4 日* 版 本 号: 1._参考实验指导书第三章的实验任务 2

实验1-FPGA编程入门_fpga板子怎么和电脑连-程序员宅基地

基于Quartus件完成一个1位全加器的设计,分别采用:1)原理图输入 以及 2)Verilog编程 这两种设计方法。开发板基于Intel DE2-115。_fpga板子怎么和电脑连

惠普打印机故障代码_惠普打印机常见故障解决办法【图文详解】-程序员宅基地

Hp打印机,也就是我们常说的惠普打印机,目前已经成了打印机行业中的最为人们熟知的品牌之一,不过对于大多数打印机来说,无论品牌好坏,总会在使用的过程中出现这样那样的问题,而惠普打印机在使用过程中是故障率也是居高不下,如此看来掌握一定的维修技巧就显得十分必要了。今天小编就为大家总结几种惠普打印机常见的故障解决办法:1、打印时显示遇到DOT400X端口错误这种情况大多是由于USB端口出现了问题,要么是没..._惠普打印机故障编码

水务公司太恶心了!!!-程序员宅基地

昨天比较郁闷,家里的水给停了。在楼下发现了一张欠费单,原因是欠了水务公司半年的水费。无奈,只好联系房东(因为欠水费的时间段发生在我住进来之前),房东也比较恶心,昨天满口答应今天过来,又放鸽子了 (房东这人一向比较恶心,总爱放鸽子)。继续无奈,为了今天能用上水只好自己亲自前往水务公司缴债!到了以后更是奇怪,那的工作人员居然冒出一句“这是以前的单子。。。”,我日啊。。。就这么一张破

考研数据结构之线性表(1.7)——练习题之求差集A-B(C表示)_差集例题-程序员宅基地

题目已知递增有序的单链表A、B(A、B中元素个数分别为m、n,且A、B都带有头结点)分别存储了一个集合,请设计算法,以求出两个集合A和B的差集A-B(仅由在A中出现而不在B中出现的元素所构成的集合)。将差集保存在单链表A中,并保持元素的递增有序性。分析只需从A中删去A与B中共有的元素即可。由于两个链表中的元素是递增有序的,因此可以这么做:设置两个指针p、q开始时分别指向A和B的开始结点。循环进行以下判断和操作:如果p所指结点的值小于q所指结点的值,则p后移一位:如果q所指结点的值小于p所指结.._差集例题