App-Instanziierung mit der App-Lebenszyklus-API
Das Instanzierungsmodell einer Anwendung bestimmt, ob mehrere Instanzen des Prozesses Ihrer Anwendung gleichzeitig laufen können. Die App-Lebenszyklus-API im Windows App SDK bietet eine Möglichkeit, zu steuern, wie viele Instanzen Ihrer App gleichzeitig ausgeführt werden können, und aktivierungen bei Bedarf an andere Instanzen umzuleiten.
In diesem Artikel wird beschrieben, wie Sie die App-Lebenszyklus-API verwenden, um die App-Instancing in Ihren WinUI-Apps zu steuern.
Voraussetzungen
So verwenden Sie die App-Lebenszyklus-API in WinUI 3-Apps:
- Laden Sie das neueste experimentelle Release des Windows App SDK herunter und installieren Sie es. Weitere Informationen finden Sie unter "Erste Schritte mit WinUI".
- Befolgen Sie die Anweisungen zum Erstellen Ihres ersten WinUI 3-Projekts oder zum Verwenden des Windows App SDK in einem vorhandenen Projekt.
Einzelinstanz-Anwendungen
Bei Einzelinstanz-Anwendungen kann nur ein Hauptprozess zur gleichen Zeit laufen. Der Versuch, eine zweite Instanz einer Einzelinstanz-Anwendung zu starten, führt normalerweise dazu, dass stattdessen das Hauptfenster der ersten Instanz aktiviert wird. Beachten Sie, dass dies nur für den Hauptprozess gilt. Einzelinstanz-Anwendungen können mehrere Hintergrundprozesse erstellen und werden trotzdem als Einzelinstanz-Anwendungen betrachtet.
WinUI-Apps sind standardmäßig mehrfachinstanziert, können aber einzeln instanziiert werden, indem Sie beim Start entscheiden, ob sie stattdessen eine neue Instanz erstellen oder eine vorhandene Instanz aktivieren möchten.
Die Microsoft Fotos-App ist ein gutes Beispiel für eine einzelne instanzierte WinUI-App. Wenn Sie Fotos zum ersten Mal starten, wird ein neues Fenster erstellt. Wenn Sie versuchen, Fotos erneut zu starten, wird stattdessen das vorhandene Fenster aktiviert.
Ein Beispiel für die Implementierung einer einzelnen Instancing in einer WinUI 3-App mit C# finden Sie unter Erstellen einer einzelinstanzierten WinUI-App.
Multi-Instanz-Anwendungen
Bei Multi-Instanz-Anwendungen kann der Hauptprozess mehrmals gleichzeitig ausgeführt werden. Der Versuch, eine zweite Instanz einer Multi-Instanz-Anwendung zu starten, erzeugt einen neuen Prozess und ein neues Hauptfenster.
In der Regel werden entpackte Apps standardmäßig mit mehreren Instanzen verwaltet, können aber die Einzelinstanzerstellung implementieren, wenn dies notwendigerweise der Fall ist. Normalerweise wird dazu ein einzelner benannter Mutex verwendet, der anzeigt, ob eine Anwendung bereits läuft.
Notepad ist ein gutes Beispiel für eine Multi-Instanz-Anwendung. Jedes Mal, wenn Sie versuchen, Notepad zu starten, wird eine neue Instanz von Notepad erstellt, unabhängig davon, wie viele Instanzen bereits ausgeführt werden.
Wie sich die Windows App SDK-Instanzierung von der UWP-Instanzierung unterscheidet
Das Instanzierungsverhalten im Windows App SDK basiert auf dem UWP-Modell, der Klasse, jedoch mit einigen wichtigen Unterschieden:
AppInstance-Klasse
- UWP: Die Klasse Windows.ApplicationModel.AppInstance ist rein auf Instanzumleitungsszenarien ausgerichtet.
- Windows App SDK: Die Klasse Microsoft.Windows.AppLifeycle.AppInstance unterstützt Szenarien der Instanzumleitung und enthält zusätzliche Funktionen zur Unterstützung neuer Funktionen in späteren Versionen.
Liste der Instanzen
- UWP: GetInstances gibt nur die Instanzen zurück, die die App explizit für eine mögliche Umleitung registriert hat.
- Windows App SDK: GetInstances gibt alle laufenden Instanzen der Anwendung zurück, die die AppInstance-API verwenden, unabhängig davon, ob sie einen Schlüssel registriert haben oder nicht. Dazu kann auch die aktuelle Instanz gehören. Wenn Sie möchten, dass die aktuelle Instanz in die Liste aufgenommen wird, rufen Sie
AppInstance.GetCurrent
auf. Getrennte Listen werden für verschiedene Versionen derselben Anwendung sowie für Instanzen von Anwendungen geführt, die von verschiedenen Benutzern gestartet wurden.
Registrierung von Schlüsseln
Jede Instanz einer Multi-Instanced-App kann einen beliebigen Schlüssel über die Methode FindOrRegisterForKey
registrieren. Schlüssel haben keine inhärente Bedeutung; Anwendungen können Schlüssel in jeder beliebigen Form oder Weise verwenden.
Eine Instanz einer Anwendung kann ihren Schlüssel jederzeit setzen, aber für jede Instanz ist nur ein Schlüssel erlaubt; das Setzen eines neuen Wertes überschreibt den vorherigen Wert.
Eine Instanz einer Anwendung kann ihren Schlüssel nicht auf denselben Wert setzen, den eine andere Instanz bereits registriert hat. Der Versuch, einen vorhandenen Schlüssel zu registrieren, führt dazu, dass FindOrRegisterForKey
die App-Instanz zurückgibt, die diesen Schlüssel bereits registriert hat.
- UWP: Eine Instanz muss einen Schlüssel registrieren, um in die Liste aufgenommen zu werden, die von GetInstances zurückgegeben wird.
- Windows App SDK: Die Registrierung eines Schlüssels ist von der Liste der Instanzen entkoppelt. Eine Instanz muss keinen Schlüssel registrieren, um in die Liste aufgenommen zu werden.
Aufheben der Registrierung von Schlüsseln
Eine Instanz einer Anwendung kann ihren Schlüssel abmelden.
- UWP: Wenn eine Instanz ihren Schlüssel abmeldet, steht sie nicht mehr für die Aktivierungsumleitung zur Verfügung und ist nicht in der Liste der Instanzen enthalten, die von GetInstances zurückgegeben wird.
- Windows App SDK: Eine Instanz, deren Schlüssel nicht mehr registriert ist, steht weiterhin für die Aktivierungsumleitung zur Verfügung und ist weiterhin in der Liste der Instanzen enthalten, die von GetInstances zurückgegeben wird.
Ziele der Instanzumleitung
Mehrere Instanzen einer Anwendung können sich gegenseitig aktivieren, ein Prozess, der als „Aktivierungsumleitung“ bezeichnet wird. So könnte eine Anwendung beispielsweise Single Instance implementieren, indem sie sich nur dann initialisiert, wenn beim Start keine anderen Instanzen der Anwendung gefunden werden, und stattdessen umleitet und beendet, wenn eine andere Instanz existiert. Bei Anwendungen mit mehreren Standorten können Aktivierungen entsprechend der Geschäftslogik der jeweiligen Anwendung umgeleitet werden. Wenn eine Aktivierung an eine andere Instanz weitergeleitet wird, verwendet sie den Activated
Callback dieser Instanz, den gleichen Callback, der auch in allen anderen Aktivierungsszenarien verwendet wird.
- UWP: Nur Instanzen, die einen Schlüssel registriert haben, können ein Ziel für eine Umleitung sein.
- Windows App SDK: Jede Instanz kann ein Umleitungsziel sein, unabhängig davon, ob sie einen registrierten Schlüssel hat oder nicht.
Verhalten nach der Umleitung
UWP: Die Umleitung ist ein Endvorgang; die Anwendung wird nach der Umleitung der Aktivierung beendet, auch wenn die Umleitung fehlgeschlagen ist.
Windows App SDK: Im Windows App SDK ist die Umleitung keine Terminaloperation. Dies spiegelt zum Teil die potenziellen Probleme bei der willkürlichen Beendigung einer Win32-Anwendung wider, die möglicherweise bereits Speicher zugewiesen hat, ermöglicht aber auch die Unterstützung anspruchsvollerer Umleitungsszenarien. Nehmen wir eine Anwendung mit mehreren Instanzen, bei der eine Instanz eine Aktivierungsanforderung erhält, während sie eine große Menge an CPU-intensiver Arbeit ausführt. Diese Anwendung kann die Aktivierungsanforderung an eine andere Instanz weiterleiten und ihre Bearbeitung fortsetzen. Dieses Szenario wäre nicht möglich, wenn die Anwendung nach der Umleitung beendet würde.
Eine Aktivierungsanfrage kann mehrfach umgeleitet werden. Instanz A könnte zu Instanz B umleiten, die wiederum zu Instanz C umleiten könnte. Windows App SDK-Anwendungen, die diese Funktionalität nutzen, müssen sich vor zirkulären Umleitungen schützen – wenn C im obigen Beispiel zu A umleitet, besteht die Gefahr einer unendlichen Aktivierungsschleife. Es ist Sache der Anwendung, festzulegen, wie die zirkuläre Umleitung gehandhabt werden soll, je nachdem, was für die von der Anwendung unterstützten Arbeitsabläufe sinnvoll ist.
Aktivierungsereignisse
Um die Reaktivierung zu handhaben, kann sich die App für ein Aktiviert-Ereignis registrieren.
- UWP: Das Ereignis übergibt ein IActivatedEventArgs an die Anwendung.
- Windows App SDK: Das Ereignis übergibt eine Microsoft.Windows.AppLifecycle.AppActivationArguments-Instanz an die App, die eine der
-ActivatedEventArgs
-Instanzen enthält.
Beispiele
Handhabung von Aktivierungen
Dieses Beispiel zeigt, wie sich eine Anwendung für ein Activated
-Ereignis registriert und damit umgeht. Wenn sie ein Activated
-Ereignis empfängt, verwendet diese Anwendung die Ereignisargumente, um festzustellen, welche Art von Aktion die Aktivierung verursacht hat, und reagiert entsprechend darauf.
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);
}
}
Umleitungslogik basierend auf der Aktivierungsart
In diesem Beispiel registriert die Anwendung einen Handler für das Ereignis Activated und prüft auch die Args des Aktivierungsereignisses, um zu entscheiden, ob die Aktivierung an eine andere Instanz umgeleitet werden soll.
Bei den meisten Aktivierungsarten fährt die App mit ihrem regulären Initialisierungsprozess fort. Wenn die Aktivierung jedoch durch das Öffnen eines zugehörigen Dateityps verursacht wurde und eine andere Instanz dieser Anwendung die Datei bereits geöffnet hat, leitet die aktuelle Instanz die Aktivierung an die vorhandene Instanz weiter und beendet sie.
Diese Anwendung verwendet die Schlüsselregistrierung, um festzustellen, welche Dateien in welchen Instanzen geöffnet sind. Wenn eine Instanz eine Datei öffnet, registriert sie einen Schlüssel, der diesen Dateinamen enthält. Andere Instanzen können dann die registrierten Schlüssel untersuchen und nach bestimmten Dateinamen suchen und sich selbst als Instanz dieser Datei registrieren, wenn dies noch keine andere Instanz getan hat.
Beachten Sie, dass die Schlüsselregistrierung selbst zwar Teil der App Lifecycle API im Windows App SDK ist, der Inhalt des Schlüssels aber nur in der App selbst angegeben wird. Eine Anwendung muss weder einen Dateinamen noch andere aussagekräftige Daten registrieren. Diese Anwendung hat sich jedoch aufgrund ihrer besonderen Bedürfnisse und der unterstützten Arbeitsabläufe dafür entschieden, offene Dateien über Schlüssel zu verfolgen.
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;
}
Beliebige Umleitung
Dieses Beispiel ist eine Erweiterung des vorherigen Beispiels, indem anspruchsvollere Umleitungsregeln hinzugefügt werden. Die Anwendung führt weiterhin die Prüfung auf offene Dateien aus dem vorherigen Beispiel durch. Während jedoch im vorherigen Beispiel immer eine neue Instanz erstellt wurde, wenn keine Umleitung aufgrund der Prüfung der offenen Datei erfolgte, wird in diesem Beispiel das Konzept einer „wiederverwendbaren“ Instanz hinzugefügt. Wenn eine wiederverwendbare Instanz gefunden wird, leitet die aktuelle Instanz zur wiederverwendbaren Instanz um und beendet sich. Andernfalls registriert es sich selbst als wiederverwendbar und fährt mit seiner normalen Initialisierung fort.
Auch hier ist zu beachten, dass das Konzept einer „wiederverwendbaren“ Instanz in der App-Lifecycle-API nicht existiert; sie wird nur innerhalb der App selbst erstellt und verwendet.
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;
}
Orchestrierung der Umleitung
In diesem Beispiel wird das Umleitungsverhalten weiter verfeinert. Hier kann sich eine App-Instanz als die Instanz registrieren, die alle Aktivierungen einer bestimmten Art bearbeitet. Wenn eine Instanz einer App eine Protocol
-Aktivierung erhält, prüft sie zunächst, ob es eine Instanz gibt, die bereits für die Bearbeitung von Protocol
-Aktivierungen registriert ist. Wenn es eine findet, leitet es die Aktivierung zu dieser Instanz um. Wenn nicht, registriert sich die aktuelle Instanz für Protocol
-Aktivierungen und wendet dann zusätzliche Logik an (nicht gezeigt), die die Aktivierung aus einem anderen Grund umleiten kann.
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);
}
}
Im Gegensatz zur UWP-Version von RedirectActivationTo
erfordert die Implementierung von RedirectActivationToAsync des Windows App SDK die explizite Übergabe von Ereignisargumenten bei der Umleitung von Aktivierungen. Dies ist notwendig, denn während UWP Aktivierungen streng kontrolliert und sicherstellen kann, dass die richtigen Aktivierungsargumente an die richtigen Instanzen übergeben werden, unterstützt die Version des Windows App SDK viele Plattformen und kann sich nicht auf UWP-spezifische Funktionen verlassen. Ein Vorteil dieses Modells ist, dass Anwendungen, die das Windows App SDK verwenden, die Möglichkeit haben, die Argumente zu ändern oder zu ersetzen, die an die Zielinstanz übergeben werden.
Umleitung ohne Blockierung
Die meisten Anwendungen werden so früh wie möglich umleiten wollen, bevor sie unnötige Initialisierungsarbeit leisten. Bei einigen Anwendungstypen läuft die Initialisierungslogik in einem STA-Thread, der nicht blockiert werden darf. AppInstance.RedirectActivationToAsync-Methode ist asynchron und die aufrufende Anwendung muss warten, bis die Methode abgeschlossen ist, sonst schlägt die Umleitung fehl. Wenn Sie jedoch auf einen asynchronen Aufruf warten, wird die STA blockiert. Rufen Sie in diesen Fällen RedirectActivationToAsync in einem anderen Thread auf, und setzen Sie ein Ereignis, wenn der Aufruf abgeschlossen ist. Warten Sie dann auf dieses Ereignis mit nicht blockierenden APIs wie CoWaitForMultipleObjects. Hier ist ein C#-Beispiel für eine WPF-Anwendung.
private static bool DecideRedirection()
{
bool isRedirect = false;
// Find out what kind of activation this is.
AppActivationArguments args = AppInstance.GetCurrent().GetActivatedEventArgs();
ExtendedActivationKind kind = args.Kind;
if (kind == ExtendedActivationKind.File)
{
try
{
// This is a file activation: here we'll get the file information,
// and register the file name as our instance key.
if (args.Data is IFileActivatedEventArgs fileArgs)
{
IStorageItem file = fileArgs.Files[0];
AppInstance keyInstance = AppInstance.FindOrRegisterForKey(file.Name);
// If we successfully registered the file name, we must be the
// only instance running that was activated for this file.
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 += OnActivated;
}
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.
redirectEventHandle = CreateEvent(IntPtr.Zero, true, false, null);
if (redirectEventHandle != IntPtr.Zero)
{
Task.Run(() =>
{
keyInstance.RedirectActivationToAsync(args).AsTask().Wait();
SetEvent(redirectEventHandle);
});
uint CWMO_DEFAULT = 0;
uint INFINITE = 0xFFFFFFFF;
_ = CoWaitForMultipleObjects(
CWMO_DEFAULT, INFINITE, 1,
new IntPtr[] { redirectEventHandle }, out uint handleIndex);
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error getting instance information: {ex.Message}");
}
}
return isRedirect;
}
Abmeldung zur Weiterleitung
Apps, die einen Schlüssel registriert haben, können diesen Schlüssel jederzeit wieder abmelden. In diesem Beispiel wird davon ausgegangen, dass die aktuelle Instanz zuvor einen Schlüssel registriert hat, der angibt, dass sie eine bestimmte Datei geöffnet hat, was bedeutet, dass nachfolgende Versuche, diese Datei zu öffnen, zu ihr umgeleitet werden. Wenn diese Datei geschlossen wird, muss der Schlüssel, der den Dateinamen enthält, gelöscht werden.
void CALLBACK OnFileClosed(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
AppInstance::GetCurrent().UnregisterKey();
}
Warnung
Obwohl die Registrierung von Schlüsseln automatisch aufgehoben wird, wenn ihr Prozess beendet wird, sind Wettlaufbedingungen möglich, bei denen eine andere Instanz eine Umleitung zu der beendeten Instanz initiiert haben kann, bevor die Registrierung der beendeten Instanz aufgehoben wurde. Um diese Möglichkeit abzuschwächen, kann eine Anwendung UnregisterKey verwenden, um die Registrierung ihres Schlüssels manuell aufzuheben, bevor sie beendet wird, so dass die Anwendung die Möglichkeit hat, Aktivierungen an eine andere Anwendung umzuleiten, die nicht gerade beendet wird.
Informationen zur Instanz
Die Klasse Microsoft.Windows.AppLifeycle.AppInstance stellt eine einzelne Instanz einer App dar. In der aktuellen Vorschau enthält AppInstance
nur die Methoden und Eigenschaften, die zur Unterstützung der Aktivierungsumleitung erforderlich sind. In späteren Versionen wird AppInstance
um weitere Methoden und Eigenschaften erweitert, die für eine App-Instanz relevant sind.
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());
}
}
Zugehöriger Inhalt
Windows developer