Files

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 };
}
}