代码之家  ›  专栏  ›  技术社区  ›  ScottishTapWater

阻止进程看到现有实例

  •  1
  • ScottishTapWater  · 技术社区  · 6 年前

    情况是这样的。

    我有一个应用程序,无论出于什么目的,我都必须把它当作一个黑盒子。

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

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

    executable.exe file1.ext 然后 executable.exe file2.ext 这干扰了我的解决方案的其余部分,这就是问题所在。

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

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

    在我开始提供文件参数和这种意外的实例行为之前,我的包装类都可以正常工作。

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


    TLDR:如何防止限制为单个实例的应用程序确定已经有一个实例在运行


    为了澄清(在可疑评论之后),我公司的研发团队写道 但我没有时间等待他们在这件事上的帮助(我只有几天而不是几个月),也没有权限做任何需要的事情来迅速交付所需的功能(我的解决方案比这个问题提到的要多得多)。


    通过一些反编译工作,我可以看到下面的代码被用来查找现有的实例。

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

    有没有什么方法可以解决这个问题,即创建多个不同名称的应用程序副本?我研究了重新命名 Process

    2 回复  |  直到 6 年前
        1
  •  1
  •   Dave Lucre    6 年前

    • 每次需要调用它时,创建一个原始.exe并将其命名为“实例”_xxxx.exe,其中xxxx是唯一的数字。
    • 根据需要执行新实例exe,完成后可以删除它
    • 您甚至可以通过创建实例池来重用实例
        2
  •  0
  •   ScottishTapWater    6 年前

    基于Dave Lucre的答案,我通过创建绑定到包装器类的可执行文件的新实例来解决这个问题。最初,我继承了 IDisposable


    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 或者如果在清单中显式设置了可执行文件名。