200 lines
7.4 KiB
C#
200 lines
7.4 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace XP.Common.Helpers
|
|
{
|
|
/// <summary>
|
|
/// 进程管理工具类 | Process Management Helper
|
|
/// 提供启动外部程序、窗口置前等通用功能 | Provides common functions like launching external programs and bringing windows to front
|
|
/// </summary>
|
|
public static class ProcessHelper
|
|
{
|
|
#region Win32 API
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool IsIconic(IntPtr hWnd);
|
|
|
|
private const int SW_RESTORE = 9;
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// 启动或激活外部程序 | Start or activate an external program
|
|
/// </summary>
|
|
/// <param name="exePath">可执行文件完整路径 | Full path to the executable</param>
|
|
/// <param name="singleInstance">是否单实例模式:true=已运行则置前,false=直接启动新实例 | Single instance mode: true=activate if running, false=always start new instance</param>
|
|
/// <returns>操作结果 | Operation result</returns>
|
|
public static ProcessResult StartOrActivate(string exePath, bool singleInstance = true)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(exePath))
|
|
{
|
|
return ProcessResult.Fail("程序路径未配置 | Program path not configured");
|
|
}
|
|
|
|
if (!File.Exists(exePath))
|
|
{
|
|
return ProcessResult.Fail($"程序文件不存在: {exePath} | Program file not found: {exePath}");
|
|
}
|
|
|
|
try
|
|
{
|
|
// 获取进程名(不含扩展名)| Get process name (without extension)
|
|
var processName = Path.GetFileNameWithoutExtension(exePath);
|
|
|
|
if (singleInstance)
|
|
{
|
|
var processes = Process.GetProcessesByName(processName);
|
|
|
|
if (processes.Length > 0)
|
|
{
|
|
// 程序已运行,将窗口置前 | Program already running, bring window to front
|
|
var process = processes[0];
|
|
var hWnd = process.MainWindowHandle;
|
|
|
|
if (hWnd != IntPtr.Zero)
|
|
{
|
|
BringToFront(hWnd);
|
|
}
|
|
|
|
return ProcessResult.Activated();
|
|
}
|
|
}
|
|
|
|
// 启动程序 | Start program
|
|
Process.Start(new ProcessStartInfo
|
|
{
|
|
FileName = exePath,
|
|
UseShellExecute = true
|
|
});
|
|
|
|
return ProcessResult.Started();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return ProcessResult.Fail($"操作失败: {ex.Message} | Operation failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 判断指定进程是否正在运行 | Check if a process is running
|
|
/// </summary>
|
|
/// <param name="processName">进程名(不含扩展名)| Process name (without extension)</param>
|
|
/// <returns>是否运行中 | Whether the process is running</returns>
|
|
public static bool IsProcessRunning(string processName)
|
|
{
|
|
return Process.GetProcessesByName(processName).Length > 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据可执行文件路径查找已运行的进程 | Find a running process by executable file path
|
|
/// 通过进程名匹配并验证完整路径,确保找到的是同一个程序 | Matches by process name and verifies full path
|
|
/// </summary>
|
|
/// <param name="exePath">可执行文件完整路径 | Full path to the executable</param>
|
|
/// <returns>找到的进程对象,未找到返回 null | The found process, or null if not found</returns>
|
|
public static Process FindRunningProcess(string exePath)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(exePath))
|
|
return null;
|
|
|
|
var processName = Path.GetFileNameWithoutExtension(exePath);
|
|
var normalizedTarget = NormalizePath(exePath);
|
|
|
|
try
|
|
{
|
|
var processes = Process.GetProcessesByName(processName);
|
|
foreach (var proc in processes)
|
|
{
|
|
try
|
|
{
|
|
// 验证完整路径匹配,避免同名不同路径的进程误关联
|
|
var procPath = proc.MainModule?.FileName;
|
|
if (procPath != null && string.Equals(NormalizePath(procPath), normalizedTarget, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return proc;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// 无法访问进程信息(权限不足等),跳过
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// GetProcessesByName 异常,返回 null
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 标准化文件路径,用于路径比较 | Normalize file path for comparison
|
|
/// </summary>
|
|
private static string NormalizePath(string path)
|
|
{
|
|
return Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将窗口置前显示 | Bring a window to the foreground
|
|
/// </summary>
|
|
/// <param name="hWnd">窗口句柄 | Window handle</param>
|
|
public static void BringToFront(IntPtr hWnd)
|
|
{
|
|
if (hWnd == IntPtr.Zero) return;
|
|
|
|
// 如果窗口被最小化,先恢复 | If window is minimized, restore it first
|
|
if (IsIconic(hWnd))
|
|
{
|
|
ShowWindow(hWnd, SW_RESTORE);
|
|
}
|
|
|
|
SetForegroundWindow(hWnd);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 进程操作结果 | Process Operation Result
|
|
/// </summary>
|
|
public class ProcessResult
|
|
{
|
|
/// <summary>
|
|
/// 是否成功 | Whether the operation succeeded
|
|
/// </summary>
|
|
public bool Success { get; private set; }
|
|
|
|
/// <summary>
|
|
/// 是否为新启动(false表示激活已有窗口)| Whether it was newly started (false means activated existing window)
|
|
/// </summary>
|
|
public bool IsNewlyStarted { get; private set; }
|
|
|
|
/// <summary>
|
|
/// 错误信息 | Error message
|
|
/// </summary>
|
|
public string ErrorMessage { get; private set; }
|
|
|
|
/// <summary>
|
|
/// 创建启动成功结果 | Create a started result
|
|
/// </summary>
|
|
public static ProcessResult Started() => new() { Success = true, IsNewlyStarted = true };
|
|
|
|
/// <summary>
|
|
/// 创建激活成功结果 | Create an activated result
|
|
/// </summary>
|
|
public static ProcessResult Activated() => new() { Success = true, IsNewlyStarted = false };
|
|
|
|
/// <summary>
|
|
/// 创建失败结果 | Create a failed result
|
|
/// </summary>
|
|
public static ProcessResult Fail(string errorMessage) => new() { Success = false, ErrorMessage = errorMessage };
|
|
}
|
|
}
|