近期实现了一个功能:调起外部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窗口置顶显示的操作
///
/// 开启线程
///
void StartThread()
{
setTopThread = new Thread(new ThreadStart(ExeWindowShowTop));
setTopThread.Start();
}
///
/// 使外部exe窗口一直显示在最上层,需要循环调用
///
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();
}
///
/// 关闭设置窗口显示在最上层的线程
///
void CloseTopThread()
{
if (setTopThread != null)
{
setTopThread.Abort();
setTopThread = null;
}
}
5、在继承于MonoBehaviour的Start方法启用测试
private void Start()
{
//启动exe进程
pss = Process.Start(exePath);
//开启管理exe窗口始终显示在最上层的线程
StartThread();
}
6、附加一个关闭指定进程的方法:
///
/// 关闭一个进程
///
/// 进程名称
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();
}
///
/// 开启线程
///
void StartThread()
{
setTopThread = new Thread(new ThreadStart(ExeWindowShowTop));
setTopThread.Start();
}
///
/// 使外部exe窗口一直显示在最上层,需要循环调用
///
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();
}
///
/// 关闭设置窗口显示在最上层的线程
///
void CloseTopThread()
{
if (setTopThread != null)
{
setTopThread.Abort();
setTopThread = null;
}
}
///
/// 关闭一个进程
///
/// 进程名称
void CloseProcess(string processName)
{
// // 这是关闭上面记录下来的exe进程代码(已注释)
//pss.Kill();
//pss.WaitForExit();
// 这是根据进程名称关闭指定进程的代码
Process[] allPss = Process.GetProcesses();
foreach (Process pss in allPss)
{
if (pss.ProcessName == processName)
{
pss.Kill();
}
}
}