(C#、unity)启用某个进程,让该进程窗口保持最上层显示,且鼠标仍能和下层窗口交互

近期实现了一个功能:调起外部exe后,让外部exe的窗口一直在最上层显示,并且此时鼠标可以和下层窗口交互,就像是qq聊天框“保持窗口最前”的功能似的。当我们用C#对进程、窗口操作时,需要调用win32的API接口,对于win32API我也是因为要实现这个功能才知道的,初次了解,写的不深。
文章的最后有 脚本的完整代码:

下面就用unity写的一个例子来讲一下:

1、创建一个新脚本和新场景,脚本中首先要调用 win 32 的API,并且定义属性字段

  ///   Win32 API 相关代码定义声明  Start

    [DllImport("User32.dll")]//根据句柄名称返回一个句柄
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]//得到上层窗口(活动窗口)
    private static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll", SetLastError = true)]//设置窗口位置
    private static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags);


    const int HWND_TOPMOST = -1;//让窗口显示在上层
    const int SWP_NOMOVE = 2;//忽略位置设置
    const int SWP_NOSIZE = 1;//忽略大小设置
    const int SWP_SHOWWINDOW = 64;//显示窗口

    ///   Win32 API 相关代码定义声明  end

2、定义其他字段:

 	 const string exePath = "要启用的Exe";//exe路径
    //const string exeIntptrName = "句柄名称";//exe进程的句柄名称

    Process pss;//启用的exe进程记录下来

    Thread setTopThread;//管理设置exe窗口置顶的线程

3、启动指定的exe进程

 //启动exe进程
 pss = Process.Start(exePath);

4、开启一个线程,让该线程专门管理exe窗口置顶显示的操作

 	/// <summary>
    /// 开启线程
    /// </summary>
    void StartThread()
    {
        setTopThread = new Thread(new ThreadStart(ExeWindowShowTop));
        setTopThread.Start();
    }

    /// <summary>
    /// 使外部exe窗口一直显示在最上层,需要循环调用
    /// </summary>
    void ExeWindowShowTop()
    {
        //判断该进程是否为空,如果是,则关闭线程,并跳出
        if (pss == null)
        {
            CloseTopThread();
            return;
        }

        ////exe进程的句柄
        //IntPtr exeHwnd = FindWindow(null, exeIntptrName);

        //当前激活的进程句柄
        IntPtr activeWndHwnd = GetForegroundWindow();

        //// 当前程序不是活动窗口,则设置窗口显示在上层
        //if (pss.MainWindowHandle != activeWndHwnd)
        //{
        //    SetWindowPos(pss.MainWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
        //}

  		SetWindowPos(pss.MainWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
        //递归:0.3s后调用该方法,持续检测
        Thread.Sleep(300);
        ExeWindowShowTop();
    }

    /// <summary>
    /// 关闭设置窗口显示在最上层的线程
    /// </summary>
    void CloseTopThread()
    {
        if (setTopThread != null)
        {
            setTopThread.Abort();
            setTopThread = null;
        }
    }

5、在继承于MonoBehaviour的Start方法启用测试

	private void Start()
    {
        //启动exe进程
        pss = Process.Start(exePath);
        //开启管理exe窗口始终显示在最上层的线程
        StartThread();
    }

6、附加一个关闭指定进程的方法:

 /// <summary>
    /// 关闭一个进程
    /// </summary>
    /// <param name="processName">进程名称</param>
    void CloseProcess(string processName)
    {
        // // 这是关闭上面记录下来的exe进程代码(已注释)
        //pss.Kill();
        //pss.WaitForExit();


        // 这是根据进程名称关闭指定进程的代码
        Process[] allPss = Process.GetProcesses();
        foreach (Process pss in allPss)
        {
            if (pss.ProcessName == processName)
            {
                pss.Kill();
            }
        }
    }

以上就能实现进程窗口显示在上层,并且可以和下层窗口交互。
附:脚本的完整代码:

 ///   Win32 API 相关代码定义声明  Start

    [DllImport("User32.dll")]//根据句柄名称返回一个句柄
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]//得到上层窗口(活动窗口)
    private static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll", SetLastError = true)]//设置窗口位置
    private static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags);


    const int HWND_TOPMOST = -1;//让窗口显示在上层
    const int SWP_NOMOVE = 2;//忽略位置设置
    const int SWP_NOSIZE = 1;//忽略大小设置
    const int SWP_SHOWWINDOW = 64;//显示窗口

    ///   Win32 API 相关代码定义声明  end



    const string exePath = "要启用的Exe";//exe路径
    //const string exeIntptrName = "句柄名称";//exe进程的句柄名称

    Process pss;//启用的exe进程记录下来

    Thread setTopThread;//管理设置exe窗口置顶的线程

    private void Start()
    {
        //启动exe进程
        pss = Process.Start(exePath);
        //开启管理exe窗口始终显示在最上层的线程
        StartThread();
    }

    /// <summary>
    /// 开启线程
    /// </summary>
    void StartThread()
    {
        setTopThread = new Thread(new ThreadStart(ExeWindowShowTop));
        setTopThread.Start();
    }

    /// <summary>
    /// 使外部exe窗口一直显示在最上层,需要循环调用
    /// </summary>
    void ExeWindowShowTop()
    {
        //判断该进程是否为空,如果是,则关闭线程,并跳出
        if (pss == null)
        {
            CloseTopThread();
            return;
        }

        ////exe进程的句柄
        //IntPtr exeHwnd = FindWindow(null, exeIntptrName);

        //当前激活的进程句柄
        IntPtr activeWndHwnd = GetForegroundWindow();

        // 当前程序不是活动窗口,则设置窗口显示在上层
        if (pss.MainWindowHandle != activeWndHwnd)
        {
            SetWindowPos(pss.MainWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
        }

        //递归:0.3s后调用该方法,持续检测
        Thread.Sleep(300);
        ExeWindowShowTop();
    }

    /// <summary>
    /// 关闭设置窗口显示在最上层的线程
    /// </summary>
    void CloseTopThread()
    {
        if (setTopThread != null)
        {
            setTopThread.Abort();
            setTopThread = null;
        }
    }

    /// <summary>
    /// 关闭一个进程
    /// </summary>
    /// <param name="processName">进程名称</param>
    void CloseProcess(string processName)
    {
        // // 这是关闭上面记录下来的exe进程代码(已注释)
        //pss.Kill();
        //pss.WaitForExit();


        // 这是根据进程名称关闭指定进程的代码
        Process[] allPss = Process.GetProcesses();
        foreach (Process pss in allPss)
        {
            if (pss.ProcessName == processName)
            {
                pss.Kill();
            }
        }
    }
发布了14 篇原创文章 · 获赞 0 · 访问量 414

猜你喜欢

转载自blog.csdn.net/a0_67/article/details/105067578
今日推荐