Запретить процессу видеть существующий экземпляр - PullRequest
0 голосов
/ 13 сентября 2018

Вот ситуация.

У меня есть приложение, которое по всем причинам и задачам я должен рассматривать как черный ящик.

Мне нужно иметь возможность открывать несколько экземпляров этого приложения, каждый с набором файлов. Синтаксис для открытия этого executable.exe file1.ext file2.ext.

Если я запускаю executable.exe x количество раз без аргументов, новые экземпляры открываются нормально.

Если я запускаю executable.exe file1.ext, а затем executable.exe file2.ext, то второй вызов открывает файл 2 в существующем окне, а не создает новый экземпляр. Это мешает остальной части моего решения и является проблемой.

Мое решение оборачивает это приложение и выполняет над ним различные операции управления, вот один из моих классов-оболочек:

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: как запретить приложению, ограниченному одним экземпляром, определить, что уже запущен экземпляр


Чтобы уточнить (после подозрительных комментариев), команда моей компании по исследованиям и разработкам написала executable.exe, но у меня нет времени ждать их помощи в этом вопросе (у меня есть дни, а не месяцы) и у меня есть разрешение сделать все необходимое для доставки требуемая функциональность (в моем решении гораздо больше, чем упоминается в этом вопросе) быстро.


С некоторой декомпиляцией я вижу, что для поиска существующего экземпляра используется следующее.

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

Есть ли способ возиться с этим коротким созданием нескольких копий приложения с разными именами? Я смотрел на переименование Process на лету, но, видимо, это невозможно, если не писать эксплойты ядра ...

Ответы [ 2 ]

0 голосов
/ 16 сентября 2018

Опираясь на ответ Дейва Лукре, я решил его, создав новые экземпляры исполняемого файла, привязанные к моему классу-обертке. Изначально я унаследовал 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 или если в манифестах явно указано имя исполняемого файла.

0 голосов
/ 13 сентября 2018

Я решил эту проблему в прошлом, создав копии исходного исполняемого файла.В вашем случае вы можете:

  • Сохранить файл «original.exe» в определенном месте.
  • Каждый раз, когда вам нужно вызвать его, создайте копию файла original.exe иназовите его «instance_xxxx.exe», где xxxx - уникальный номер.
  • Запустите новый exe-файл экземпляра, как требуется, и когда он завершится, вы можете удалить его
  • Возможно, вы даже сможете использовать повторноэкземпляры, создавая их пул
...