Unity EXE置顶

本文介绍了一个Unity脚本,用于控制游戏窗口的样式、位置、大小和层级。通过调用Windows API,实现全屏、无边框窗口、窗口置顶等功能,并根据屏幕方向调整分辨率。
using System;

using System.Collections;

using System.Runtime.InteropServices;

using System.Diagnostics;

using UnityEngine;
public class WindowMod : MonoBehaviour
{

    [DllImport("user32.dll")]
    static extern IntPtr GetForegroundWindow();

    [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);

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

    [DllImport("User32.dll", EntryPoint = "SetWindowLong")]
    private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

    [DllImport("User32.dll", EntryPoint = "GetWindowLong")]
    private static extern int GetWindowLong(IntPtr hWnd, int dwNewLong);

    [DllImport("User32.dll", EntryPoint = "MoveWindow")]
    private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int width, int height, bool repaint);

    [DllImport("user32.dll", EntryPoint = "ShowWindow", CharSet = CharSet.Auto)]
    public static extern int ShowWindow(IntPtr hwnd, int nCmdShow);

    [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wP, IntPtr IP);

    [DllImport("user32.dll", EntryPoint = "SetParent", CharSet = CharSet.Auto)]
    public static extern IntPtr SetParent(IntPtr hChild, IntPtr hParent);

    [DllImport("user32.dll", EntryPoint = "GetParent", CharSet = CharSet.Auto)]
    public static extern IntPtr GetParent(IntPtr hChild);

    [DllImport("User32.dll", EntryPoint = "GetSystemMetrics")]
    public static extern IntPtr GetSystemMetrics(int nIndex);

    [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
    public static extern bool SetForegroundWindow(IntPtr hWnd);//设置此窗体为活动窗体

    public enum appStyle
    {
        FullScreen = 0,
        WindowedFullScreen = 1,
        Windowed = 2,
        WindowedWithoutBorder = 3,
    }
    public appStyle AppWindowStyle = appStyle.FullScreen;

    public enum zDepth
    {
        Normal = 0,
        Top = 1,
        TopMost = 2,
    }
    public zDepth ScreenDepth = zDepth.TopMost;
    public int windowLeft = 0;
    public int windowTop = 0;
    private int windowWidth = Screen.width;
    private int windowHeight = Screen.height;
    const uint SWP_SHOWWINDOW = 0x0040;
    const int GWL_STYLE = -16;
    const int WS_BORDER = 1;
    private Rect screenPosition;
    private const int GWL_EXSTYLE = (-20);
    private const int WS_CAPTION = 0xC00000;
    private const int WS_POPUP = 0x800000;
    IntPtr HWND_TOP = new IntPtr(0);
    IntPtr HWND_TOPMOST = new IntPtr(-1);
    IntPtr HWND_NORMAL = new IntPtr(-2);
    private const int SM_CXSCREEN = 0x00000000;
    private const int SM_CYSCREEN = 0x00000001;
    int Xscreen;
    int Yscreen;
    //add 2015.4.21
    public bool StartAuto = true;
    [Tooltip("打包后的EXE名字")]
    public string productName;
    public enum ScreenDirection
    {
        defaultDirection,
        horizontal,
        vertical,
    }
    public ScreenDirection CurDirection = ScreenDirection.defaultDirection;
    void Awake()
    {
        Xscreen = (int)GetSystemMetrics(SM_CXSCREEN);
        Yscreen = (int)GetSystemMetrics(SM_CYSCREEN);

        if (!StartAuto)
        {
            if (Xscreen > Yscreen)
            {
                windowWidth = 1920;
                windowHeight = 1080;
                // Global.CurDictiion = Global.EnumDiction.Horizontal;
            }
            else
            {
                windowWidth = 1080;
                windowHeight = 1920;
                //Global.CurDictiion = Global.EnumDiction.Vertical;
            }
        }
        else
        {
            if (CurDirection == ScreenDirection.horizontal)
            {
                windowWidth = 1920;
                windowHeight = 1080;
                // Global.CurDictiion = Global.EnumDiction.Horizontal;
            }
            else if (CurDirection == ScreenDirection.vertical)
            {
                windowWidth = 1080;
                windowHeight = 1920;
                //Global.CurDictiion = Global.EnumDiction.Vertical;
            }
        }
        if ((int)AppWindowStyle == 0)
        {
            Screen.SetResolution(Xscreen, Yscreen, true);
        }
        if ((int)AppWindowStyle == 1)
        {
            //Screen.SetResolution(Xscreen - 1, Yscreen - 1, false);
            //screenPosition = new Rect(0, 0, Xscreen - 1, Yscreen - 1);

            Screen.SetResolution(windowWidth, windowHeight, false);
            screenPosition = new Rect(0, 0, windowWidth, windowHeight);
        }
        if ((int)AppWindowStyle == 2)
        {
            Screen.SetResolution(windowWidth, windowWidth, false);
        }
        if ((int)AppWindowStyle == 3)
        {
            Screen.SetResolution(windowWidth, windowWidth, false);
            screenPosition = new Rect(windowLeft, windowTop, windowWidth, windowWidth);
        }
    }

    void Start()
    {
        InvokeRepeating("LaunchProjectile", 1, 0.5f);
    }

    void LaunchProjectile()
    {
        print("hello");
    }

    int i = 0;
    void Update()
    {
        IntPtr hWnd = FindWindow(null, productName);  //udpeffect 这个是我unity 打包发布后的 窗口名字。这里注意设置。

        //if(hWnd != IntPtr.Zero)
        //{
        //    System.IO.Directory.CreateDirectory("d:\\ttt");
        //}
        SetForegroundWindow(hWnd);

        if (i < 30)
        {
            if ((int)AppWindowStyle == 1)
            {
                SetWindowLong(hWnd, -16, 369164288);
                if ((int)ScreenDepth == 0)
                    SetWindowPos(hWnd, HWND_NORMAL, (int)screenPosition.x, (int)screenPosition.y, (int)screenPosition.width, (int)screenPosition.height, SWP_SHOWWINDOW);
                if ((int)ScreenDepth == 1)
                    SetWindowPos(hWnd, HWND_TOP, (int)screenPosition.x, (int)screenPosition.y, (int)screenPosition.width, (int)screenPosition.height, SWP_SHOWWINDOW);
                if ((int)ScreenDepth == 2)
                    SetWindowPos(hWnd, HWND_TOPMOST, (int)screenPosition.x, (int)screenPosition.y, (int)screenPosition.width, (int)screenPosition.height, SWP_SHOWWINDOW);
                //ShowWindow(GetForegroundWindow(), 3);
            }

            if ((int)AppWindowStyle == 2)
            {
                if ((int)ScreenDepth == 0)
                {
                    SetWindowPos(hWnd, HWND_NORMAL, 0, 0, 0, 0, 0x0001 | 0x0002);
                    SetWindowPos(hWnd, HWND_NORMAL, 0, 0, 0, 0, 0x0001 | 0x0002 | 0x0020);
                }
                if ((int)ScreenDepth == 1)
                {
                    SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, 0x0001 | 0x0002);
                    SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, 0x0001 | 0x0002 | 0x0020);
                }
                if ((int)ScreenDepth == 2)
                {
                    SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, 0x0001 | 0x0002);
                    SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, 0x0001 | 0x0002 | 0x0020);
                }
            }
            if ((int)AppWindowStyle == 3)
            {
                SetWindowLong(hWnd, -16, 369164288);
                if ((int)ScreenDepth == 0)
                    SetWindowPos(hWnd, HWND_NORMAL, (int)screenPosition.x, (int)screenPosition.y, (int)screenPosition.width, (int)screenPosition.height, SWP_SHOWWINDOW);
                if ((int)ScreenDepth == 1)
                    SetWindowPos(hWnd, HWND_TOP, (int)screenPosition.x, (int)screenPosition.y, (int)screenPosition.width, (int)screenPosition.height, SWP_SHOWWINDOW);
                if ((int)ScreenDepth == 2)
                    SetWindowPos(hWnd, HWND_TOPMOST, (int)screenPosition.x, (int)screenPosition.y, (int)screenPosition.width, (int)screenPosition.height, SWP_SHOWWINDOW);
            }
        }
        i++;

    }
}

打包后可以见到效果

<think>我们正在讨论在Unity程序中实现exe弹窗的方法。结合用户提供的引用信息,我们可以归纳出几种实现方式: 1. **使用System.Diagnostics.Process启动外部exe程序**(如引用[4]所示): - 通过`ProcessStartInfo`设置要启动的exe路径和参数 - 调用`Process.Start`方法启动程序 - 如果需要置顶,可以结合Windows API实现 2. **通过Unity与外部程序通信的方式触发外部exe**(如引用[3]所示): - 在Unity中通过按钮事件调用`Application.ExternalCall`(通常用于WebGL,但在桌面端也可通过特定方式实现通信) - 然后由外部程序(如一个包装器)来启动目标exe 3. **结合NSIS(Nullsoft Scriptable Install System)等安装包制作工具**(如引用[1]提及): - 在打包安装程序时可以定制安装过程中的弹窗行为 4. **直接使用Windows API控制窗口**(如引用[2]所示): - 启动外部exe后,可以通过Windows API获取其窗口句柄并置顶 具体到实现exe弹窗,直接的方法是第1种(启动外部exe)。下面将详细说明: ### 方法1:使用C#的Process类启动外部exe置顶 步骤: 1. 在Unity脚本中引入`System.Diagnostics`命名空间 2. 创建`ProcessStartInfo`对象,设置exe路径和参数 3. 启动进程 4. (可选)使用Windows API将启动的窗口置顶 示例代码: ```csharp using System.Diagnostics; using UnityEngine; using System.Runtime.InteropServices; // 如果需要调用Windows API public class ExeLauncher : MonoBehaviour { // 用于调用Windows API的声明(置顶功能) [DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); // 定义窗口位置常量 private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); private const uint SWP_NOSIZE = 0x0001; private const uint SWP_NOMOVE = 0x0002; private const uint TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE; // 启动外部exe置顶 public void LaunchExe(string exePath, string arguments = "") { ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = exePath; // exe的完整路径 startInfo.Arguments = arguments; // 可选参数 // 启动进程 Process process = Process.Start(startInfo); // 延时等待窗口出现,然后置顶 // 注意:直接置顶可能失败,因为窗口创建需要时间,所以需要延时 Invoke("SetWindowTopMost", 0.5f); } // 将启动的窗口置顶 private void SetWindowTopMost() { // 这里需要获取到启动的进程的窗口句柄,实际应用中可能需要根据进程名或窗口标题查找 // 假设我们只启动了一个这样的进程,并且只有一个主窗口 Process[] processes = Process.GetProcesses(); foreach (Process p in processes) { // 根据进程名或窗口标题判断,这里需要根据实际情况调整 if (!string.IsNullOrEmpty(p.MainWindowTitle)) { SetWindowPos(p.MainWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); break; } } } } ``` 注意: - 置顶操作需要获取窗口句柄,上面的示例代码是通过遍历所有进程并查找有窗口标题的进程,这在实际应用中可能不够准确。更好的做法是在启动进程后记录进程ID,然后通过进程ID查找主窗口。 - 由于窗口创建需要时间,所以置顶操作需要延时执行(如0.5秒后)。 ### 方法2:通过Application.ExternalCall与外部通信(适用于桌面端包装) 如果Unity程序是被一个桌面程序(如C#编写的WinForms/WPF程序)包装,则可以通过通信机制让外部程序启动exe。这种方式更复杂,需要建立通信机制(如通过TCP/IP、命名管道等)。引用[3]中的`Application.ExternalCall`主要用于WebGL,在桌面端不适用,但可以通过类似下面的方式: 1. 在Unity中: ```csharp public class ExeCaller : MonoBehaviour { // 声明一个外部调用的函数(需要由宿主环境实现) [DllImport("__Internal")] private static extern void LaunchExe(string exePath); public void ButtonClick() { // 调用外部函数 LaunchExe("C:\\path\\to\\your.exe"); } } ``` 2. 在宿主程序(如C++/C#编写的桌面程序)中,需要暴露一个名为`LaunchExe`的函数供Unity调用(通过Unity的Native Plugin机制)。 由于这种方式较为复杂,且需要宿主程序支持,一般推荐使用方法1。 ### 总结 在Unity中实现exe弹窗常用的方法是使用`Process.Start`启动外部程序。如果需要将弹窗置顶,则可以结合Windows API(SetWindowPos)实现。但要注意窗口创建的延时问题。 §§相关问题§§ 1. 如何确保启动的exe窗口在Unity程序上方显示? 2. 如何在Unity中捕获外部exe程序的关闭事件? 3. Unity启动外部exe时如何传递参数? 4. 如何避免启动外部exeUnity程序卡顿? </think>在Unity程序中实现exe弹窗,主要有以下几种方法: ### 📌 方法1:使用System.Diagnostics.Process启动外部exe ```csharp using System.Diagnostics; using UnityEngine; public class ExeLauncher : MonoBehaviour { public void LaunchExternalExe(string exePath) { ProcessStartInfo startInfo = new ProcessStartInfo(exePath); Process.Start(startInfo); } } ``` **使用说明**: 1. 将脚本挂载到Unity对象 2. 调用`LaunchExternalExe("完整exe路径")`即可 3. 可通过`startInfo.Arguments`传递参数 ⚠️ 注意事项: - 需要确保目标exe存在且路径正确 - 路径中使用双反斜杠`\\`(如`C:\\App\\demo.exe`) - 推荐使用`Application.streamingAssetsPath`管理路径 ### 📌 方法2:窗口置顶功能(需调用Windows API) ```csharp [DllImport("user32.dll")] static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); public void LaunchAndFocusExe(string exePath) { Process p = Process.Start(exePath); // 等待窗口初始化 p.WaitForInputIdle(); SetWindowPos(p.MainWindowHandle, (IntPtr)(-1), 0, 0, 0, 0, 0x0001 | 0x0002); } ``` **功能特点**: - `(-1)`参数使窗口置顶(类似`AlwaysOnTop`效果) - 需添加`using System.Runtime.InteropServices` ### 📌 方法3:通过按钮触发(结合UI系统) ```csharp using UnityEngine.UI; public class UIManager : MonoBehaviour { public Button exeButton; public string exePath = "test.exe"; void Start() { exeButton.onClick.AddListener(() => { Process.Start(exePath); }); } } ``` **操作流程**: 1. 创建UI按钮 2. 将脚本挂载到Canvas对象 3. 拖拽按钮到`exeButton`字段 4. 设置目标exe路径 ### ⚠️ 常见问题解决方案 1. **路径问题**: ```csharp // 推荐使用相对路径 string fullPath = Path.Combine(Application.streamingAssetsPath, "demo.exe"); ``` 2. **跨平台处理**: ```csharp #if UNITY_STANDALONE_WIN // Windows专用代码 #elif UNITY_EDITOR // 编辑器模式处理 #endif ``` 3. **权限问题**(Windows): - 在exe文件的属性中勾选"以管理员身份运行" - 在Unity播放器设置中启用"Run as Administrator" ### 📚 技术要点总结 1. **核心类**:`System.Diagnostics.Process` 2. **关键方法**:`Process.Start()` 3. **路径规范**:使用`Application.streamingAssetsPath`保证发布后路径有效 4. **窗口控制**:通过`user32.dll`的`SetWindowPos`实现高级控制[^2][^4] 5. **事件触发**:结合UnityEvent实现灵活调用[^3] ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值