Архив метки: WPF



WPF: To run this application, you must install .NET Core

Обратил внимание, что в папке с запускным файлом из прошлой статьи (пример на WPF), после компиляции присутствуют ещё несколько файлов:
SingleInstance.deps.json
SingleInstance.dll
SingleInstance.exe
SingleInstance.pdb
SingleInstance.runtimeconfig.dev.json
SingleInstance.runtimeconfig.json

При этом раньше в результате компиляции получалось только три файла:
SingleInstance.exe
SingleInstance.exe.config
SingleInstance.pdb

Файлы с расширением .pdb нужны для отладки, поэтому я не понимаю, зачем они нужны в релизе. Генерацию подобных файлов можно отключить:
ProjectPropertiesBuildAdvanced[Output]Debugging Information = None

 

Я перенёс файл SingleInstance.exe на другой диск и попытался его запустить, но ничего не произошло. Тогда я перенёс также файл SingleInstance.dll. на этот раз, при попытке запустить приложение, я получил ошибку:

SingleInstance.exe

To run this application, you must install .NET Core.
Would you like to download it now?

 

Вообще у меня вроде как уже установлен необходимый пакет, ну да ладно — скачал и установил, но всё равно ошибка сохранилась.

Посмотреть список установленных сред выполнения .NET можно такой командой в PowerShell:

dotnet --list-runtimes

 

Про файлы базы данных программ (PDB) написано много всего. Но я всё равно не понял, зачем это нужно в релизе.

Приложение удалось запустить после того, как я перенёс третий файл: SingleInstance.runtimeconfig.json.

 

Вот его содержимое:

{
  "runtimeOptions": {
    "tfm": "netcoreapp3.1",
    "framework": {
      "name": "Microsoft.WindowsDesktop.App",
      "version": "3.1.0"
    }
  }
}

Это конечно поменьше, но всё равно странно, зачем столько сложностей. Хотя вот здесь или здесь всё разложено по полочкам, так что можно почитать. Покопавшись в инете, нашёл способ запаковать всё это в один файл, чтобы в дальнейшем подобных недоразумений не возникало. Делается это с помощью «публикации».

Публикация приложения

1. В открытом проекте выбираем конфигурацию сборки Release:

2. В меню выбираем BuildPublish Имя_Вашего_Проекта
3. В окошке запроса выбираем режим публикации (в папку):

4. Нажав кнопку Next, указываем папку для публикации:

5. После нажатия кнопки Finish нужно будет отобразить все настройки, нажав на кнопку Show all settings:

6. Здесь нам нужно обязательно выбрать целевую платформу (в моём случае это win-x64), а также отметить опцию Produce single file:

7. После сохранения изменений, нажимаем кнопку Publish:

Вот теперь у нас получается только один запускной файл (если вы ранее отключили генерацию файла .pdb, о котором написано в начале статьи, то его не будет).



WPF: Single Instance app реализация с помощью Mutex

В статье «Запускать только один экземпляр приложения» всё нормально работало, но минус всё же был — если приложение уже было запущено, то мы просто получали об этом уведомление в виде MessageBox. Дело в том, что мы не умели работать с другим процессом. В этот раз попробуем реализовать обращение к другому процессу и восстановление окна, если оно было свёрнуто. Давным давно мы тоже это делали, но там подключалась отдельная библиотека, исходник которой сейчас уже удалили и остались только её копии. Я не знаю причину, по которой она была удалена с оригинального сайта. Может что-то было в коде и эти ошибки (уязвимости?) не стали исправлять… На этот раз я буду использовать уже готовый класс из набора .NET, он называется Mutex. Я нашёл не так много примеров с его реализацией для моего случая, поэтому не уверен на счёт максимально эффективного кода. Но это решение мне понравилось, хотя кто-то в коментах писал, что у этого метода есть проблемы с производительностью. Тем не менее, распишу его здесь поподробнее.

Изменения для файла App.xaml.cs

Для примера мы будем генерировать уникальные строчки с помощью PowerShell утилиты guid.

 

Полный код файла App.xaml.cs будет такой:

using System;
using System.Threading;
using System.Windows;

namespace SingleInstance
{
  public partial class App
  {
    // The event mutex name.
    private const string UniqueEventName = "4380587d-6dde-47b6-bb23-68eb68a27ba5";

    // The unique mutex name.
    private const string UniqueMutexName = "5e81acba-2dd9-4a71-b8bd-625ab30309e4";

    // The event wait handle.
    private EventWaitHandle eventWaitHandle;

    // The mutex.
    private Mutex mutex;

    // The app on startup event
    private void AppOnStartup(object sender, StartupEventArgs e)
    {
      bool isOwned;
      mutex = new Mutex(true, UniqueMutexName, out isOwned);
      eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName);

      // So, R# would not give a warning that this variable is not used.
      GC.KeepAlive(mutex);

      if (isOwned)
      {
        // Spawn a thread which will be waiting for our event
        var thread = new Thread(
            () =>
            {
              while (eventWaitHandle.WaitOne())
              {
                Current.Dispatcher.BeginInvoke(
                        (Action)(() => ((MainWindow)Current.MainWindow).BringToForeground()));
              }
            });

        // It is important mark it as background otherwise it will prevent app from exiting.
        thread.IsBackground = true;

        thread.Start();
        return;
      }

      // Notify other instance so it could bring itself to foreground.
      eventWaitHandle.Set();

      // Terminate this instance.
      Shutdown();
    }
  }
}

Изменения для файла App.xaml

Чтобы запускалось не две копии приложения, а только одна, нам нужно обратить внимание на файл App.xaml, здесь свойство StartupUri дополняется подпиской на событие Startup, т.о. полный код этого файла теперь будет такой:

<Application x:Class="SingleInstance.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml"
             Startup="AppOnStartup">
    <Application.Resources>
         
    </Application.Resources>
</Application>

Изменения для файла MainWindow.xaml.cs

В файле App.xaml.cs мы обращались к методу BringToForeground, его необходимо описать в файле логики для главного окна MainWindow.xaml.cs:

using System.Windows;

namespace SingleInstance
{
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
    }

    // Brings main window to foreground.
    public void BringToForeground()
    {
      if (WindowState == WindowState.Minimized || Visibility == Visibility.Hidden)
      {
        Show();
        WindowState = WindowState.Normal;
      }

      // According to some sources these steps guarantee that an app will be brought to foreground.
      Activate();
      Topmost = true;
      Topmost = false;
      Focus();
    }
  }
}