防止进程看到现有实例

Prevent process seeing existing instance

情况是这样的。

我有一个应用程序,出于所有意图和目的,我必须将其视为黑匣子。

我需要能够打开此应用程序的多个实例,每个实例都包含一组文件。打开它的语法是 executable.exe file1.ext file2.ext.

如果我 运行 executable.exe x 没有参数的次数,新实例可以正常打开。

如果我 运行 executable.exe file1.ext 后跟 executable.exe file2.ext 然后第二个调用在现有 window 中打开文件 2 而不是创建新实例。 这会干扰我的其余解决方案,这就是问题所在。

我的解决方案包装这个应用程序并对其执行各种管理操作,这是我的包装器之一 classes:

public class myWrapper
{
    public event EventHandler<IntPtr> SplashFinished;
    public event EventHandler ProcessExited;
    private const string aaTrendLocation = @"redacted";
    //private const string aaTrendLocation = "notepad";
    private readonly Process _process;
    private readonly Logger _logger;

    public myWrapper(string[] args, Logger logger =null)
    {
        _logger = logger;
        _logger?.WriteLine("Intiialising new wrapper object...");
        if (args == null || args.Length < 1) args = new[] {""};
        ProcessStartInfo info = new ProcessStartInfo(aaTrendLocation,args.Aggregate((s,c)=>$"{s} {c}"));
        _process = new Process{StartInfo = info};

    }

    public void Start()
    {
        _logger?.WriteLine("Starting process...");
        _logger?.WriteLine($"Process: {_process.StartInfo.FileName} || Args: {_process.StartInfo.Arguments}");
        _process.Start();
        Task.Run(()=>MonitorSplash());
        Task.Run(() => MonitorLifeTime());
    }

    private void MonitorLifeTime()
    {
        _logger?.WriteLine("Monitoring lifetime...");
        while (!_process.HasExited)
        {
            _process.Refresh();
            Thread.Sleep(50);
        }
        _logger?.WriteLine("Process exited!");
        _logger?.WriteLine("Invoking!");
        ProcessExited?.BeginInvoke(this, null, null, null);
    }

    private void MonitorSplash()
    {
        _logger?.WriteLine("Monitoring Splash...");
        while (!_process.MainWindowTitle.Contains("Trend"))
        {
            _process.Refresh();
            Thread.Sleep(500);
        }
        _logger?.WriteLine("Splash finished!");
        _logger?.WriteLine("Invoking...");
        SplashFinished?.BeginInvoke(this,_process.MainWindowHandle,null,null);
    }

    public void Stop()
    {
        _logger?.WriteLine("Killing trend...");
        _process.Kill();
    }

    public IntPtr GetHandle()
    {
        _logger?.WriteLine("Fetching handle...");
        _process.Refresh();
        return _process.MainWindowHandle;
    }

    public string GetMainTitle()
    {
        _logger?.WriteLine("Fetching Title...");
        _process.Refresh();
        return _process.MainWindowTitle;
    }
}

我的包装器 class 一切正常,直到我开始提供文件参数并且出现这种意外的实例化行为。

我无法修改目标应用程序,也无法访问其源代码以确定此实例是使用互斥锁还是通过其他一些功能进行管理。因此,我需要确定是否有办法防止新实例看到现有实例。有人有什么建议吗?


TLDR:如何防止仅限于单个实例的应用程序确定已经存在一个实例 运行ning


为了澄清(在可疑评论之后),我公司的研发团队写道 executable.exe 但我没有时间在这件事上等待他们的帮助(我有几天而不是几个月)并且有权这样做快速提供所需功能所需的任何东西(我的解决方案比这个问题提到的要多得多)。


通过一些反编译工作,我可以看到以下内容用于查找现有实例。

Process[] processesByName = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName);

有什么方法可以解决这个问题,即创建具有不同名称的应用程序的多个副本?我研究了如何即时重命名 Process,但显然如果不编写内核漏洞,这是不可能的...

我过去通过创建源可执行文件的副本解决了这个问题。对于您的情况,您可以:

  • 将 'original.exe' 保存在特定位置。
  • 每次需要调用它时,创建一个 original.exe 的副本并将其命名为 'instance_xxxx.exe',其中 xxxx 是一个唯一编号。
  • 根据需要执行您的新实例 exe,完成后您可以将其删除
  • 您甚至可以通过创建实例池来重新使用这些实例

基于 Dave Lucre 的回答,我通过创建绑定到我的包装器 class 的可执行文件的新实例解决了这个问题。最初,我继承了 IDisposable 并删除了 Disposer 中的临时文件,但由于某种原因导致清理会阻止应用程序的问题,所以现在我的主程序在最后执行清理。


我的构造函数现在看起来像:

public AaTrend(string[] args, ILogger logger = null)
{
    _logger = logger;
    _logger?.WriteLine("Initialising new aaTrend object...");
    if (args == null || args.Length < 1) args = new[] { "" };
    _tempFilePath = GenerateTempFileName();
    CreateTempCopy(); //Needed to bypass lazy single instance checks
    HideTempFile(); //Stops users worrying
    ProcessStartInfo info = new ProcessStartInfo(_tempFilePath, args.Aggregate((s, c) => $"{s} {c}"));
    _process = new Process { StartInfo = info };
}

使用两个新方法:

    private void CreateTempCopy()
    {
        _logger?.WriteLine("Creating temporary file...");
        _logger?.WriteLine(_tempFilePath);
        File.Copy(AaTrendLocation, _tempFilePath);
    }

    private string GenerateTempFileName(int increment = 0)
    {
        string directory = Path.GetDirectoryName(AaTrendLocation); //Obtain pass components.
        string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(AaTrendLocation);
        string extension = Path.GetExtension(AaTrendLocation);
        string tempName = $"{directory}\{fileNameWithoutExtension}-{increment}{extension}"; //Re-assemble path with increment inserted.
        return File.Exists(tempName) ? GenerateTempFileName(++increment) : tempName; //If this name is already used, increment an recurse otherwise return new path.
    }

然后在我的主程序中:

    private static void DeleteTempFiles()
    {
        string dir = Path.GetDirectoryName(AaTrend.AaTrendLocation);
        foreach (string file in Directory.GetFiles(dir, "aaTrend-*.exe", SearchOption.TopDirectoryOnly))
        {
            File.Delete(file);
        }
    }

附带说明一下,此方法仅适用于具有依赖于 Process.GetProcessByName() 的(惰性)确定实例化方法的应用程序;如果使用 Mutex 或在清单中明确设置可执行文件名称,它将不起作用。