控制Unity发布的PC包的窗体

news2024/11/19 16:35:27

  大家好,我是阿赵。
  用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,这代表不改变大小,也不改变坐标。
通过不同的参数配搭,可以做出各种效果的窗体控制,各位有兴趣可以试试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/919483.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【每日易题】七夕限定——单身狗问题以及进阶问题位运算法的深入探讨

君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 Hello,米娜桑们,这里是君兮_,在写这篇博客的前一天是七夕,也是中国传统的“情人节”,不知道各位脱单了吗?碰巧最近刷题时遇到了经典的单身狗问题想带大家深入探…

消息队列前世今生 字节跳动 Kafka #创作活动

消息队列前世今生 1.1 案例一: 系统崩溃 首先大家跟着我想象一下下面的这个的场景, 看到新出的游戏机,太贵了买不起,这个时候你突然想到,今天抖音直播搞活动,打开抖音搜索,找到直播间以后&am…

JVM——类加载与字节码技术—编译期处理+类加载阶段

3.编译期处理 编译期优化称为语法糖 3.1 默认构造器 3.2 自动拆装箱 java基本类型和包装类型之间的自动转换。 3.3泛型集合取值 在字节码中可以看见,泛型擦除就是字节码中的执行代码不区分是String还是Integer了,统一用Object. 对于取出的Object&…

【ARM】Day9 cortex-A7核I2C实验(采集温湿度)

1. 2、编写IIC协议,采集温湿度值 iic.h #ifndef __IIC_H__ #define __IIC_H__ #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h" #include "led.h" /* 通过程序模拟实现I2C总线的时序和协议* GPIOF ---> AHB4* I2C1_S…

IoT DC3 是一个基于 Spring Cloud 的开源的、分布式的物联网(IoT)平台本地部署步骤

dc3 windows 本地搭建步骤: ​​ 必要软件环境 进入原网页# 务必保证至少需要给 docker 分配:1 核 CPU 以及 4G 以上的运行内存! JDK : 推荐使用 Oracle JDK 1.8 或者 OpenJDK8,理论来说其他版本也行; Maven : 推荐…

记录《现有docker中安装spark3.4.1》

基础docker环境中存储hadoop3--方便后续查看 参考: 实践: export JAVA_HOME/opt/apache/jdk1.8.0_333 export SPARK_MASTER_IP192.168.0.220 export SPARK_WORKER_MEMORY4g export SPARK_WORKER_CORES2 export SPARK_EXECUTOR_MEMORY4g export HADOOP_H…

『SEQ日志』在 .NET中快速集成轻量级的分布式日志平台

📣读完这篇文章里你能收获到 如何在Docker中部署 SEQ:介绍了如何创建和运行 SEQ 容器,给出了详细的执行操作如何使用 NLog 接入 .NET Core 应用程序的日志:详细介绍了 NLog 和 NLog.Seq 来配置和记录日志的步骤日志记录示例&…

微服务中间件--MQ

MQ MQa.安装RabbitMQb.消息模型c.SpringAMQP发送和接收d.WorkQueue模型e.发布订阅模型1) FanoutExchange2) DirectExchange3) TopicExchange f.消息转换器 MQ 同步调用的问题 微服务间基于Feign的调用就属于同步方式,存在一些问题。 耦合度高:每次加入…

【网络安全】防火墙知识点全面图解(二)

本系列文章包含: 【网络安全】防火墙知识点全面图解(一)【网络安全】防火墙知识点全面图解(二) 防火墙知识点全面图解(二) 21、路由器的访问控制列表是什么样的?22、防火墙的安全策…

java电子病历源码 电子病历编辑器源码 病历在线制作、管理和使用

电子病历在线制作、管理和使用的一体化电子病历解决方案,通过一体化的设计,提供对住院病人的电子病历书写、保存、修改、打印等功能。电子病历系统将临床医护需要的诊疗资料以符合临床思维的方法展示。建立以病人为中心,以临床诊疗信息为主线…

【WebSocket】前端使用WebSocket实时通信

目录 前言什么是WebSocketWebSocket的工作原理WebSocket与HTTP的关系HTTP建立持久化连接WebSocket类封装 前言 最近写项目,需要实现消息通知和实时聊天的功能,就去了解了一些关于websocket的知识,总结如下。 什么是WebSocket WebSocket 是一…

Prometheus 监控系统

常用的监控系统有哪些? 老牌传统 Zabbix Nagios Cacti 新一代的 Prometheus 夜莺 Zabbix 和 Prometheus 的区别?如何选择?【重中之重】 Zabbix 更适用于传统业务架构的物理机、虚拟机环境的监控,对容器环境的支持较差&#xf…

5.从头跑一个pipeline

1.安装torch pip install torchvision torch PyTorch的torchvision.models模块中自带的很多预定义模型。torchvision 是PyTorch的一个官方库,专门用于处理计算机视觉任务。在这个库中,可以找到许多常用的卷积神经网络模型,包括ResNet、VGG、…

【Eclipse】汉化简体中文教程(官方汉化包,IDE自带软件安装功能),图文详情

目录 0.环境 1.步骤 1)查看eclipse的版本 2)在官网找语言包,并复制链接 3)将链接复制到eclipse中 4)汉化完成 0.环境 windows11,64位; eclipse 2021-6版本 1.步骤 思路:在官网找…

【FAQ】云存储EasyCVR视频汇聚平台分发rtsp流时,出现“用户已过期”提示该如何解决?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同,支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强,视频能力丰富,具体可实现视频监控直播、视频轮播、视频录像、…

2023-8-23 合并集合

题目链接&#xff1a;合并集合 #include <iostream>using namespace std;const int N 100010;int n, m; int p[N];int find(int x) {if(p[x] ! x) p[x] find(p[x]);return p[x]; }int main() {cin >> n >> m;for(int i 1; i < n; i) p[i] i;while(m…

webrtc的Sdp中的Plan-b和UnifiedPlan

在一些类似于视频会议场景下&#xff0c;媒体会话参与者需要接收或者发送多个流&#xff0c;例如一个源端&#xff0c;同时发送多个左右音轨的音频&#xff0c;或者多个摄像头的视频流&#xff1b;在2013年&#xff0c;提出了2个不同的SDP IETF草案Plan B和Unified Plan&#x…

云服务器(Centos7系统)配置JAVA+mysql+tomcat 环境

文章主要内容来源云服务器&#xff08;Centos7系统&#xff09;部署javaweb项目&#xff08;二&#xff09;配置JAVAmysqltomcat 环境_man_zuo的博客-CSDN博客 模仿途中遇到的问题 连接无效 有时连接无法下载&#xff0c;可能是过期了&#xff0c;将其更换为官网给的下载连接即…

精准高效农业作业,植保无人机显身手

中国作为农业大国&#xff0c;拥有约18亿亩的农田&#xff0c;每年都需要进行种子喷洒和农药施用等农业作业&#xff0c;对于普通农户来说&#xff0c;这是一项耗时耗力的工程&#xff0c;同时&#xff0c;人工喷洒农药极易造成农药慢性中毒&#xff0c;对农民的身体健康产生极…