Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Модель инстантирования приложения определяет, может ли одновременно выполняться несколько экземпляров процесса приложения. API жизненного цикла приложения в Windows App SDK позволяет управлять количеством экземпляров приложения одновременно и перенаправлением активаций в другие экземпляры при необходимости.
В этой статье описывается, как использовать API жизненного цикла приложений для управления отображением приложений в приложениях WinUI 3.
Предварительные условия
Чтобы использовать API жизненного цикла приложения в приложениях WinUI, выполните следующие действия.
- Скачайте и установите последний выпуск Windows App SDK. Для получения дополнительной информации см. раздел Начало работы с WinUI.
- Следуйте инструкциям, чтобы Создать ваш первый проект WinUI или использовать Windows App SDK в существующем проекте.
Одно экземплярные приложения
Приложения выполняются одним экземпляром, если одновременно может выполняться только один основной процесс. Попытка запустить второй экземпляр одноэлементного приложения обычно приводит к активации главного окна первого экземпляра. Обратите внимание, что это относится только к основному процессу. Приложения, работающие в одиночном экземпляре, могут создавать несколько фоновых процессов и по-прежнему считаться работающими в единственном экземпляре.
Приложения WinUI по умолчанию являются несколькими экземплярами, но по умолчанию имеют возможность стать одним экземпляром, решив при запуске создать новый экземпляр или активировать существующий экземпляр.
Приложение Фотографии (Майкрософт) является хорошим примером одного экземплярного приложения WinUI. При первом запуске фотографий будет создано новое окно. При попытке повторного запуска фотографий вместо этого будет активировано существующее окно.
Пример реализации одиночного экземпляра в приложении WinUI с помощью C#, см. в статье "Создание однократного экземпляра приложения WinUI".
Приложения с несколькими экземплярами
Приложения выполняются несколькими экземплярами, если основной процесс может выполняться несколько раз одновременно. При попытке запустить второй экземпляр многоэкземплярного приложения создается новый процесс и основное окно.
Традиционно неупакованные приложения могут иметь несколько экземпляров по умолчанию, но при необходимости могут реализовать одиночное инстанцирование. Обычно это делается с помощью одного именованного мьютекса, чтобы указать, запущено ли приложение.
Блокнот является хорошим примером много экземплярного приложения. При каждом запуске Блокнота создается новый экземпляр Блокнота независимо от того, сколько экземпляров уже запущено.
Как Windows App SDK инстантирование отличается от инстантирования UWP
Поведение инстанцирования в составе Windows App SDK основано на модели и классе UWP, но с некоторыми ключевыми различиями.
Класс AppInstance
- UWP: класс Windows.ApplicationModel.AppInstance ориентирован исключительно на сценарии перенаправления экземпляров.
- Windows App SDK: класс Microsoft.Windows.AppLifeycle.AppInstance поддерживает сценарии перенаправления экземпляров и содержит дополнительные функции для поддержки новых функций в последующих выпусках.
Список экземпляров
- UWP: GetInstances возвращает только экземпляры, которые приложение явно зарегистрировало для потенциального перенаправления.
-
Windows App SDK: GetInstances возвращает все запущенные экземпляры приложения, использующие API AppInstance, независимо от того, зарегистрирован ли ключ. Это может включать текущий экземпляр. Если вы хотите, чтобы текущий экземпляр был включен в список, вызовите
AppInstance.GetCurrent. Отдельные списки поддерживаются для разных версий одного приложения, а также экземпляров приложений, запускаемых различными пользователями.
Регистрация ключей
Каждый экземпляр приложения с несколькими экземплярами может зарегистрировать произвольный ключ с помощью FindOrRegisterForKey метода. Ключи не имеют никакого смысла; приложения могут использовать ключи в любой форме или способе.
Экземпляр приложения может задать ключ в любое время, но для каждого экземпляра допускается только один ключ; При задании нового значения перезаписывается предыдущее значение.
Экземпляр приложения не может задать ключ тому же значению, что и другой экземпляр, уже зарегистрированный. Попытка зарегистрировать существующий ключ приведет к FindOrRegisterForKey возврату экземпляра приложения, который уже зарегистрировал этот ключ.
- UWP: экземпляр должен зарегистрировать ключ для включения в список, возвращаемый из GetInstances.
- Windows App SDK: регистрация ключа не связана со списком экземпляров. Экземпляру не нужно регистрировать ключ для включения в список.
Отмена регистрации ключей
Экземпляр приложения может отменить регистрацию ключа.
- UWP: если экземпляр отменяет регистрацию ключа, он больше недоступен для перенаправления активации и не включен в список экземпляров, возвращаемых из GetInstances.
- Windows App SDK: экземпляр, который отменял регистрацию ключа, по-прежнему доступен для перенаправления активации и по-прежнему включен в список экземпляров, возвращенных из GetInstances.
Целевые объекты перенаправления экземпляров
Несколько экземпляров приложения могут активировать друг друга, процесс, который называется "перенаправление активации". Например, приложение может реализовать единственный экземпляр, инициализируя себя только в том случае, если другие экземпляры приложения не найдены при запуске, в противном случае выполнить перенаправление и завершение работы, если другой экземпляр существует. Приложения с несколькими экземплярами могут перенаправлять активации, когда это уместно, в соответствии с бизнес-логикой этого приложения. При перенаправлении активации в другой экземпляр он использует обратный вызов этого экземпляра Activated , тот же обратный вызов, который используется во всех других сценариях активации.
- UWP: только экземпляры, зарегистрированные ключом, могут быть целевым объектом для перенаправления.
- Windows App SDK: любой экземпляр может быть целевым объектом перенаправления, независимо от того, имеет ли он зарегистрированный ключ.
Поведение после перенаправления
UWP: перенаправление — это операция терминала; приложение завершается после перенаправления активации, даже если перенаправление завершилось сбоем.
Windows App SDK. В Windows App SDK перенаправление не является операцией терминала. Это частично отражает потенциальные проблемы при произвольном прекращении приложения Win32, которое, возможно, уже выделило некоторую память, но также позволяет поддерживать более сложные сценарии перенаправления. Рассмотрим многоэкземплярное приложение, в котором экземпляр получает запрос на активацию при выполнении вычислительно интенсивной работы. Это приложение может перенаправить запрос активации на другой экземпляр и продолжить обработку. Такой сценарий был бы невозможен, если приложение было прекращено после перенаправления.
Запрос на активацию можно перенаправить несколько раз. Экземпляр A может перенаправляться на экземпляр B, который, в свою очередь, может перенаправляться на экземпляр C. Приложения Windows App SDK, которые используют эту функцию, должны защищаться от циклического перенаправления. Если C перенаправляется на A в приведенном выше примере, существует потенциальный бесконечный цикл активации. Это зависит от приложения, чтобы определить, как обрабатывать циклическое перенаправление в зависимости от того, что имеет смысл для рабочих процессов, поддерживаемых приложением.
События активации
Для обработки повторной активации приложение может зарегистрировать событие Активации.
- UWP: событие передает IActivatedEventArgs приложению.
-
Windows App SDK: событие передает экземпляр Microsoft.Windows.AppLifecycle.AppActivationArguments в приложение, которое содержит один из экземпляров
-ActivatedEventArgs.
Примеры
Обработка активаций
В этом примере показано, как приложение регистрирует и обрабатывает Activated событие. Когда оно получает Activated событие, это приложение использует аргументы событий для определения типа действия, вызванного активацией, и отвечает соответствующим образом.
int APIENTRY wWinMain(
_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Initialize the Windows App SDK framework package for unpackaged apps.
HRESULT hr{ MddBootstrapInitialize(majorMinorVersion, versionTag, minVersion) };
if (FAILED(hr))
{
OutputFormattedDebugString(
L"Error 0x%X in MddBootstrapInitialize(0x%08X, %s, %hu.%hu.%hu.%hu)\n",
hr, majorMinorVersion, versionTag,
minVersion.Major, minVersion.Minor, minVersion.Build, minVersion.Revision);
return hr;
}
if (DecideRedirection())
{
return 1;
}
// Connect the Activated event, to allow for this instance of the app
// getting reactivated as a result of multi-instance redirection.
AppInstance thisInstance = AppInstance::GetCurrent();
auto activationToken = thisInstance.Activated(
auto_revoke, [&thisInstance](
const auto& sender, const AppActivationArguments& args)
{ OnActivated(sender, args); }
);
// Carry on with regular Windows initialization.
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_CLASSNAME, szWindowClass, MAX_LOADSTRING);
RegisterWindowClass(hInstance);
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
MddBootstrapShutdown();
return (int)msg.wParam;
}
void OnActivated(const IInspectable&, const AppActivationArguments& args)
{
int const arraysize = 4096;
WCHAR szTmp[arraysize];
size_t cbTmp = arraysize * sizeof(WCHAR);
StringCbPrintf(szTmp, cbTmp, L"OnActivated (%d)", activationCount++);
ExtendedActivationKind kind = args.Kind();
if (kind == ExtendedActivationKind::Launch)
{
ReportLaunchArgs(szTmp, args);
}
else if (kind == ExtendedActivationKind::File)
{
ReportFileArgs(szTmp, args);
}
}
Логика перенаправления на основе типа активации
В этом примере приложение регистрирует обработчик для события Activated, а также проверяет аргументы события активации, чтобы решить, следует ли перенаправить активацию на другой экземпляр.
Для большинства типов активаций приложение продолжает процесс регулярной инициализации. Однако если активация была вызвана открытием связанного типа файла, и если другой экземпляр этого приложения уже открыт, текущий экземпляр перенаправит активацию в существующий экземпляр и завершит работу.
Это приложение использует регистрацию ключевых данных для определения того, какие файлы открыты в каких экземплярах. Когда экземпляр открывает файл, он регистрирует ключ, содержащий это имя файла. Другие экземпляры могут затем проверять зарегистрированные ключи, искать определенные имена файлов и регистрироваться как экземпляр этого файла, если еще ни один другой экземпляр не зарегистрирован.
Обратите внимание, что, хотя регистрация ключа является частью API жизненного цикла приложения в Windows App SDK, содержимое ключа указывается только в самом приложении. Приложению не нужно регистрировать имя файла или другие значимые данные. Однако это приложение решило отслеживать открытые файлы с помощью ключей на основе конкретных потребностей и поддерживаемых рабочих процессов.
bool DecideRedirection()
{
// Get the current executable filesystem path, so we can
// use it later in registering for activation kinds.
GetModuleFileName(NULL, szExePath, MAX_PATH);
wcscpy_s(szExePathAndIconIndex, szExePath);
wcscat_s(szExePathAndIconIndex, L",1");
// Find out what kind of activation this is.
AppActivationArguments args = AppInstance::GetCurrent().GetActivatedEventArgs();
ExtendedActivationKind kind = args.Kind();
if (kind == ExtendedActivationKind::Launch)
{
ReportLaunchArgs(L"WinMain", args);
}
else if (kind == ExtendedActivationKind::File)
{
ReportFileArgs(L"WinMain", args);
try
{
// This is a file activation: here we'll get the file information,
// and register the file name as our instance key.
IFileActivatedEventArgs fileArgs = args.Data().as<IFileActivatedEventArgs>();
if (fileArgs != NULL)
{
IStorageItem file = fileArgs.Files().GetAt(0);
AppInstance keyInstance = AppInstance::FindOrRegisterForKey(file.Name());
OutputFormattedMessage(
L"Registered key = %ls", keyInstance.Key().c_str());
// If we successfully registered the file name, we must be the
// only instance running that was activated for this file.
if (keyInstance.IsCurrent())
{
// Report successful file name key registration.
OutputFormattedMessage(
L"IsCurrent=true; registered this instance for %ls",
file.Name().c_str());
}
else
{
keyInstance.RedirectActivationToAsync(args).get();
return true;
}
}
}
catch (...)
{
OutputErrorString(L"Error getting instance information");
}
}
return false;
}
Произвольное перенаправление
Этот пример расширяет предыдущий, добавляя более сложные правила перенаправления. Приложение по-прежнему выполняет проверку открытого файла из предыдущего примера. Однако если в предыдущем примере всегда создавался бы новый экземпляр при отсутствии перенаправления на основе проверки открытого файла, в этом примере добавляется концепция "повторно используемого" экземпляра. Если найден повторно используемый экземпляр, текущий экземпляр перенаправляется на повторно используемый экземпляр и завершает работу. В противном случае регистрируется как пригодный для повторного использования и продолжает нормальную инициализацию.
Опять же, обратите внимание, что концепция экземпляра, доступного для повторного использования, не существует в API жизненного цикла приложения; он создается и используется только в самом приложении.
int APIENTRY wWinMain(
_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
// Initialize COM.
winrt::init_apartment();
AppActivationArguments activationArgs =
AppInstance::GetCurrent().GetActivatedEventArgs();
// Check for any specific activation kind we care about.
ExtendedActivationKind kind = activationArgs.Kind;
if (kind == ExtendedActivationKind::File)
{
// etc... as in previous scenario.
}
else
{
// For other activation kinds, we'll trawl all instances to see if
// any are suitable for redirecting this request. First, get a list
// of all running instances of this app.
auto instances = AppInstance::GetInstances();
// In the simple case, we'll redirect to any other instance.
AppInstance instance = instances.GetAt(0);
// If the app re-registers re-usable instances, we can filter for these instead.
// In this example, the app uses the string "REUSABLE" to indicate to itself
// that it can redirect to a particular instance.
bool isFound = false;
for (AppInstance instance : instances)
{
if (instance.Key == L"REUSABLE")
{
isFound = true;
instance.RedirectActivationToAsync(activationArgs).get();
break;
}
}
if (!isFound)
{
// We'll register this as a reusable instance, and then
// go ahead and do normal initialization.
winrt::hstring szKey = L"REUSABLE";
AppInstance::FindOrRegisterForKey(szKey);
RegisterClassAndStartMessagePump(hInstance, nCmdShow);
}
}
return 1;
}
Оркестрация перенаправления
В этом примере снова добавляется более сложное поведение перенаправления. Здесь экземпляр приложения может зарегистрировать себя в качестве экземпляра, обрабатывающего все активации определенного типа. Когда экземпляр приложения получает активацию Protocol, он сначала проверяет, зарегистрирован ли уже какой-либо экземпляр для обработки активаций Protocol. Если он находит такой, активация перенаправляется в этот экземпляр. Если нет, то текущий экземпляр регистрируется для активаций Protocol, а затем применяется дополнительная логика (не показана), которая может перенаправить активацию по иной причине.
void OnActivated(const IInspectable&, const AppActivationArguments& args)
{
const ExtendedActivationKind kind = args.Kind;
// For example, we might want to redirect protocol activations.
if (kind == ExtendedActivationKind::Protocol)
{
auto protocolArgs = args.Data().as<ProtocolActivatedEventArgs>();
Uri uri = protocolArgs.Uri();
// We'll try to find the instance that handles protocol activations.
// If there isn't one, then this instance will take over that duty.
auto instance = AppInstance::FindOrRegisterForKey(uri.AbsoluteUri());
if (!instance.IsCurrent)
{
instance.RedirectActivationToAsync(args).get();
}
else
{
DoSomethingWithProtocolArgs(uri);
}
}
else
{
// In this example, this instance of the app handles all other
// activation kinds.
DoSomethingWithNewActivationArgs(args);
}
}
В отличие от версии UWP RedirectActivationTo, реализация Windows App SDK RedirectActivationToAsync требует явного передачи аргументов событий при перенаправлении активаций. Это необходимо, так как в то время как UWP тесно управляет активацией и может гарантировать, что правильные аргументы активации передаются в правильные экземпляры, версия Windows App SDK поддерживает множество платформ и не может полагаться на функции UWP. Одним из преимуществ этой модели является то, что приложения, использующие Windows App SDK, могут изменять или заменять аргументы, которые будут переданы целевому экземпляру.
Перенаправление без блокировки
Большинство приложений предпочитают перенаправлять как можно раньше, прежде чем начинать лишнюю инициализацию. Для некоторых типов приложений логика инициализации выполняется в потоке STA, который не должен быть заблокирован. Метод AppInstance.RedirectActivationToAsync является асинхронным, и вызывающее приложение должно ожидать завершения метода, в противном случае перенаправление завершится ошибкой. Однако ожидание асинхронного вызова блокирует STA. В таких ситуациях вызовите RedirectActivationToAsync в другом потоке и задайте событие после завершения вызова. Затем дождитесь этого события с помощью неблокирующих API.
Ниже приведен пример C# для приложения WPF:
private static bool DecideRedirection(string key)
{
bool isRedirect = false;
// Find out what kind of activation this is.
AppActivationArguments args = AppInstance.GetCurrent().GetActivatedEventArgs();
ExtendedActivationKind kind = args.Kind;
if (kind == ExtendedActivationKind.Launch)
{
try
{
AppInstance keyInstance = AppInstance.FindOrRegisterForKey(key);
// If we successfully registered the key, we must be the
// only instance running that was activated for this key.
if (keyInstance.IsCurrent)
{
// Hook up the Activated event, to allow for this instance of the app
// getting reactivated as a result of multi-instance redirection.
keyInstance.Activated += OnKeyInstanceActivated;
}
else
{
isRedirect = true;
// Ensure we don't block the STA, by doing the redirect operation
// in another thread, and using an event to signal when it has completed.
var redirectSemaphore = new Semaphore(0, 1);
Task.Run(() =>
{
keyInstance.RedirectActivationToAsync(args).AsTask().Wait();
redirectSemaphore.Release();
});
redirectSemaphore.WaitOne();
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error getting instance information: {ex.Message}");
}
}
return isRedirect;
}
Отмена регистрации для перенаправления
Приложения, которые зарегистрировали ключ, могут отменить регистрацию этого ключа в любое время. В этом примере предполагается, что текущий экземпляр ранее зарегистрировал ключ, указывающий на текущий открытый им файл, что означает, что последующие попытки открыть этот файл будут перенаправлены на него. При закрытии файла необходимо удалить ключ, содержащий имя файла.
void CALLBACK OnFileClosed(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
AppInstance::GetCurrent().UnregisterKey();
}
Предупреждение
Хотя ключи автоматически снимаются с регистрации при завершении процесса, возможны условия гонки процессов, когда другой экземпляр мог инициировать перенаправление к завершенному экземпляру, прежде чем экземпляр был снят с регистрации. Чтобы устранить эту возможность, приложение может использовать UnregisterKey для ручной отмены регистрации ключа перед его завершением, что дает приложению возможность перенаправить активации в другое приложение, которое не находится в процессе выхода.
Сведения об экземпляре
Класс Microsoft.Windows.AppLifeycle.AppInstance представляет один экземпляр приложения. В текущей предварительной версии AppInstance включены только те методы и свойства, которые необходимы для поддержки перенаправления активации. В последующих выпусках AppInstance будет расширен, чтобы включать другие методы и свойства, относящиеся к экземпляру приложения.
void DumpExistingInstances()
{
for (AppInstance const& instance : AppInstance::GetInstances())
{
std::wostringstream sStream;
sStream << L"Instance: ProcessId = " << instance.ProcessId
<< L", Key = " << instance.Key().c_str() << std::endl;
::OutputDebugString(sStream.str().c_str());
}
}
Связанный контент
Windows developer