Freigeben über


CLR-Profiler und Windows Store-Apps

In diesem Thema wird erläutert, was Sie beim Schreiben von Diagnosetools beachten müssen, die verwalteten Code analysieren, der in einer Windows Store-App ausgeführt wird. Außerdem werden Richtlinien zum Ändern Ihrer vorhandenen Entwicklungstools bereitgestellt, damit sie weiterhin funktionieren, wenn Sie sie für Windows Store-Apps ausführen. Um diese Informationen zu verstehen, ist es am besten, wenn Sie mit der Common Language Runtime-Profilerstellungs-API vertraut sind, diese API bereits in einem Diagnosetool verwendet haben, das ordnungsgemäß für Windows-Desktopanwendungen ausgeführt wird, und Sie möchten nun das Tool so ändern, dass es ordnungsgemäß für Windows Store-Apps ausgeführt wird.

Einführung

Wenn Sie über den einführenden Absatz hinausgearbeitet haben, sind Sie mit der CLR-Profilerstellungs-API vertraut. Sie haben bereits ein Diagnosetool geschrieben, das gut für verwaltete Desktopanwendungen geeignet ist. Jetzt sind Sie neugierig, was Sie tun müssen, damit Ihr Tool mit einer verwalteten Windows Store-App funktioniert. Vielleicht haben Sie bereits versucht, diese Aufgabe zu machen, und haben festgestellt, dass es sich nicht um eine einfache Aufgabe handelt. In der Tat gibt es eine Reihe von Überlegungen, die möglicherweise nicht für alle Entwickler von Tools offensichtlich sind. Beispiel:

  • Windows Store-Apps werden in einem Kontext mit stark reduzierten Berechtigungen ausgeführt.

  • Windows-Metadatendateien weisen im Vergleich zu herkömmlichen verwalteten Modulen eindeutige Merkmale auf.

  • Windows Store-Apps haben die Angewohnheit, sich selbst angehalten zu haben, wenn die Interaktivität ausfällt.

  • Ihre prozessübergreifenden Kommunikationsmechanismen funktionieren aus verschiedenen Gründen möglicherweise nicht mehr.

In diesem Thema werden die Dinge aufgeführt, die Sie beachten müssen, und wie Sie damit richtig umgehen können.

Wenn Sie noch nicht mit der CLR-Profilerstellungs-API arbeiten, fahren Sie mit den Ressourcen am Ende dieses Themas fort, um bessere Einführungsinformationen zu finden.

Die Angabe von Details zu bestimmten Windows-APIs und deren Verwendung liegt ebenfalls außerhalb des Bereichs dieses Themas. Betrachten Sie dieses Thema als Ausgangspunkt, und lesen Sie MSDN, um mehr über alle Windows-APIs zu erfahren, auf die hier verwiesen wird.

Architektur und Terminologie

In der Regel verfügt ein Diagnosetool über eine Architektur wie die in der folgenden Abbildung gezeigte. Es wird der Begriff "Profiler" verwendet, aber viele solcher Tools gehen weit über die typische Leistungs- oder Speicherprofilerstellung hinaus in Bereiche wie Code Coverage, Modellobjektframeworks, Debuggen von Zeitreisen, Anwendungsüberwachung usw. Der Einfachheit halber bezieht sich dieses Thema weiterhin auf alle diese Tools als Profiler.

In diesem Thema wird die folgende Terminologie verwendet:

Anwendung

Dies ist die Anwendung, die der Profiler analysiert. In der Regel verwendet der Entwickler dieser Anwendung jetzt den Profiler, um Probleme mit der Anwendung zu diagnostizieren. In der Regel handelt es sich bei dieser Anwendung um eine Windows-Desktopanwendung, aber in diesem Thema werden Windows Store-Apps behandelt.

Profiler DLL

Dies ist die Komponente, die in den Prozessbereich der zu analysierenden Anwendung geladen wird. Diese Komponente, die auch als Profiler „Agent" bezeichnet wird, implementiert die ICorProfilerCallback-SchnittstellenICorProfilerCallback Interface(2,3 usw.) und nutzt die ICorProfilerInfo(2,3 usw.)-Schnittstellen, um Daten über die analysierte Anwendung zu sammeln und möglicherweise Aspekte des Anwendungsverhaltens zu ändern.

Profiler UI

Dies ist eine Desktopanwendung, mit der der Profilerbenutzer interagiert. Es ist dafür verantwortlich, dem Benutzer den Anwendungsstatus anzuzeigen und ihm die Möglichkeit zu geben, das Verhalten der analysierten Anwendung zu steuern. Diese Komponente wird immer in einem eigenen Prozessbereich ausgeführt, getrennt vom Prozessbereich der Anwendung, für die ein Profil erstellt wird. Die Profiler-Benutzeroberfläche kann auch als „Anfügungstrigger" fungieren, d. h. der Prozess, der die ICLRProfiling::AttachProfiler-Methode aufruft, um die analysierte Anwendung dazu zu veranlassen, die Profiler-DLL in den Fällen zu laden, in denen die Profiler-DLL beim Start nicht geladen wurde.

Wichtig

Ihre Profiler-Benutzeroberfläche sollte eine Windows-Desktopanwendung bleiben, auch wenn sie zum Steuern und Melden einer Windows Store-App verwendet wird. Erwarten Sie nicht, dass Sie Ihr Diagnose-Tool im Windows Store verpacken und versenden können. Ihr Tool muss Dinge ausführen, die Windows Store-Apps nicht ausführen können, und viele dieser Dinge befinden sich in Ihrer Profiler-Benutzeroberfläche.

In diesem Dokument wird im Beispielcode folgendes vorausgesetzt:

  • Ihre Profiler-DLL ist in C++ geschrieben, da es sich gemäß den Anforderungen der CLR-Profilerstellungs-API um eine native DLL handelt.

  • Ihre Profiler-Benutzeroberfläche ist in C# geschrieben. Dies ist nicht notwendig, aber da es keine Anforderungen an die Sprache für den Prozess Ihrer Profiler-Benutzeroberfläche gibt, warum sollten Sie nicht eine Sprache auswählen, die präzise und einfach ist?

Windows RT-Geräte

Windows RT Geräte sind ziemlich gesperrt. Profiler von Drittanbietern können einfach nicht auf solche Geräte geladen werden. Dieses Dokument konzentriert sich auf Windows 8 PCs.

Verarbeiten von Windows-Runtime-APIs

In einer Reihe von Szenarien, die in den folgenden Abschnitten erläutert werden, muss Ihre Profiler-UI-Desktopanwendung einige neue Windows-Runtime APIs nutzen. In der Dokumentation erfahren Sie, welche Windows-Runtime-APIs von Desktopanwendungen verwendet werden können und ob sich ihr Verhalten unterscheidet, wenn sie von Desktopanwendungen und Windows Store-Apps aufgerufen werden.

Wenn Ihre Profiler-Benutzeroberfläche in verwaltetem Code geschrieben ist, müssen Sie einige Schritte ausführen, um die Verwendung dieser Windows-Runtime APIs zu vereinfachen. Weitere Informationen finden Sie im Artikel Verwaltete Desktop-Apps und Windows-Runtime.

Laden der Profiler-DLL

In diesem Abschnitt wird beschrieben, wie ihre Profiler-Benutzeroberfläche bewirkt, dass die Windows Store-App Ihre Profiler-DLL lädt. Der in diesem Abschnitt erläuterte Code gehört zu Ihrer Profiler-UI-Desktop-App und umfasst daher die Verwendung von Windows-APIs, die für Desktop-Apps, aber nicht unbedingt sicher für Windows Store-Apps sind.

Ihre Profiler-Benutzeroberfläche kann dazu führen, dass Ihre Profiler-DLL auf zwei Arten in den Prozessbereich der Anwendung geladen wird:

Einer Ihrer ersten Hindernisse ist das Laden und Anfügen Ihrer Profiler-DLL, um ordnungsgemäß mit Windows Store-Apps zu funktionieren. Beide Formen des Ladens haben einige besondere Aspekte gemeinsam, also beginnen wir mit ihnen.

Allgemeine Überlegungen zum Starten und Anfügen von Ladevorgängen

Signieren der Profiler-DLL

Wenn Windows versucht, Ihre Profiler-DLL zu laden, wird überprüft, ob Ihre Profiler-DLL ordnungsgemäß signiert ist. Andernfalls tritt beim Laden standardmäßig ein Fehler auf. Hierfür gibt es zwei Möglichkeiten:

  • Stellen Sie sicher, dass Ihre Profiler-DLL signiert ist.

  • Teilen Sie Dem Benutzer mit, dass er eine Entwicklerlizenz auf dem Windows 8 Computer installieren muss, bevor er ihr Tool verwendet. Dies kann automatisch über Visual Studio oder manuell über eine Eingabeaufforderung erfolgen. Weitere Informationen finden Sie auf der Seite zum Anfordern einer Entwicklerlizenz.

Dateisystemberechtigungen

Die Windows Store-App muss über die Berechtigung zum Laden und Ausführen Ihrer Profiler-DLL aus dem Speicherort im Dateisystem verfügen, in dem sie sich befindet. Standardmäßig verfügt die Windows Store-App für die meisten Verzeichnisse nicht über eine solche Berechtigung, und jeder fehlgeschlagene Versuch, Ihre Profiler-DLL zu laden, erzeugt einen Eintrag im Windows-Anwendungsereignisprotokoll, der in etwa wie folgt aussieht:

NET Runtime version 4.0.30319.17929 - Loading profiler failed during CoCreateInstance.  Profiler CLSID: '{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}'.  HRESULT: 0x80070005.  Process ID (decimal): 4688.  Message ID: [0x2504].

Im Allgemeinen dürfen Windows Store-Apps nur auf eine begrenzte Anzahl von Speicherorten auf dem Datenträger zugreifen. Jede Windows Store-App kann auf ihre eigenen Anwendungsdatenordner sowie auf einige andere Bereiche im Dateisystem zugreifen, auf die allen Windows Store-Apps Zugriff gewährt wird. Es ist am besten, Ihre Profiler-DLL und ihre Abhängigkeiten irgendwo unter Programme oder Programme (x86) zu installieren, da alle Windows Store-Apps dort standardmäßig Über Lese- und Ausführungsberechtigungen verfügen.

Startladevorgang

In einer Desktop-App fordert Die Profiler-Benutzeroberfläche in der Regel zum Starten der Profiler-DLL auf, indem ein Umgebungsblock initialisiert wird, der die erforderlichen Umgebungsvariablen der CLR-Profilerstellungs-API (z. B. COR_PROFILER, COR_ENABLE_PROFILING und COR_PROFILER_PATH) enthält, und dann einen neuen Prozess mit diesem Umgebungsblock erstellt. Gleiches gilt für Windows Store-Apps, aber die Mechanismen sind unterschiedlich.

Führen Sie keine erhöhten Rechte aus.

Wenn Prozess A versucht, Prozess B der Windows Store-App zu erzeugen, sollte Prozess A auf mittlerer Integritätsebene und nicht auf hoher Integritätsebene (d. a. nicht mit erhöhten Rechten) ausgeführt werden. Dies bedeutet, dass ihre Profiler-Benutzeroberfläche entweder auf mittlerer Integritätsebene ausgeführt werden sollte, oder dass sie einen anderen Desktopprozess mit mittlerer Integritätsebene erzeugen muss, um die Windows Store-App zu starten.

Auswählen einer Windows Store-App zum Profil

Zunächst sollten Sie Ihren Profilerbenutzer fragen, welche Windows Store-App gestartet werden soll. Für Desktop-Apps würden Sie möglicherweise ein Dialogfeld "Datei durchsuchen" anzeigen, und der Benutzer würde eine .exe Datei suchen und auswählen. Windows Store-Apps sind jedoch unterschiedlich, und die Verwendung eines Dialogfelds "Durchsuchen" ist nicht sinnvoll. Stattdessen ist es besser, dem Benutzer eine Liste der installierten Windows Store-Apps anzuzeigen, aus denen dieser Benutzer auswählen kann.

Sie können die PackageManager -Klasse verwenden, um diese Liste zu generieren. PackageManagerist eine Windows-Runtime Klasse, die für Desktop-Apps verfügbar ist und tatsächlich nur für Desktop-Apps verfügbar ist.

Im folgenden Codebeispiel einer hypothetischen Profiler-Benutzeroberfläche, die als Desktop-App in C# geschrieben wurde, wird verwendet, PackageManager um eine Liste von Windows-Apps zu generieren:

string currentUserSID = WindowsIdentity.GetCurrent().User.ToString();
IAppxFactory appxFactory = (IAppxFactory) new AppxFactory();
PackageManager packageManager = new PackageManager();
IEnumerable<Package> packages = packageManager.FindPackagesForUser(currentUserSID);

Angeben des benutzerdefinierten Umgebungsblocks

Mit der neuen COM-Schnittstelle IPackageDebugSettings können Sie das Ausführungsverhalten einer Windows Store-App anpassen, um einige Formen der Diagnose zu vereinfachen. Mit einer ihrer Methoden, EnableDebugging, können Sie einen Umgebungsblock an die Windows Store-App übergeben, wenn sie gestartet wird, zusammen mit anderen nützlichen Effekten wie dem Deaktivieren der automatischen Prozessaufsetzung. Der Umgebungsblock ist wichtig, da Sie hier die Umgebungsvariablen (COR_PROFILER, und COR_PROFILER_PATH)) angeben müssen, COR_ENABLE_PROFILINGdie vom CLR zum Laden der Profiler-DLL verwendet werden.

Betrachten Sie den folgenden Codeausschnitt:

IPackageDebugSettings pkgDebugSettings = new PackageDebugSettings();
pkgDebugSettings.EnableDebugging(packageFullName, debuggerCommandLine,
                                                                 (IntPtr)fixedEnvironmentPzz);

Es gibt ein paar Elemente, die Sie benötigen, um richtig zu sein:

  • packageFullName kann beim Durchlaufen der Pakete und beim package.Id.FullNameAbrufen von ermittelt werden.

  • debuggerCommandLine ist ein bisschen interessanter. Um den benutzerdefinierten Umgebungsblock an die Windows Store-App zu übergeben, müssen Sie einen eigenen, vereinfachten Dummydebugger schreiben. Windows erzeugt die angehaltene Windows Store-App und fügt ihren Debugger dann an, indem der Debugger wie in diesem Beispiel über eine Befehlszeile gestartet wird:

    MyDummyDebugger.exe -p 1336 -tid 1424
    

    wobei -p 1336 bedeutet, dass die Windows Store-App über die Prozess-ID 1336 verfügt und -tid 1424 bedeutet, dass Thread-ID 1424 der Thread ist, der angehalten wird. Ihr Dummydebugger analysiert die ThreadID über die Befehlszeile, setzt diesen Thread fort und beendet dann.

    Hier sehen Sie einige Beispiel-C++-Code dazu (achten Sie darauf, die Fehlerüberprüfung hinzuzufügen!):

    int wmain(int argc, wchar_t* argv[])
    {
        // …
        // Parse command line here
        // …
    
        HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME,
                                                                  FALSE /* bInheritHandle */, nThreadID);
        ResumeThread(hThread);
        CloseHandle(hThread);
        return 0;
    }
    

    Sie müssen diesen Dummydebugger im Rahmen ihrer Diagnose Toolinstallation bereitstellen und dann den Pfad zu diesem Debugger im debuggerCommandLine Parameter angeben.

Starten der Windows Store-App

Der Moment zum Starten der Windows Store-App ist endlich da. Wenn Sie dies bereits selbst versucht haben, haben Sie möglicherweise bemerkt, dass Sie mit CreateProcess nicht einen Windows Store-App-Prozess erstellen. Stattdessen müssen Sie die IApplicationActivationManager::ActivateApplication-Methode verwenden. Dazu müssen Sie die App-Benutzermodell-ID der Windows Store-App abrufen, die Sie starten. Und das bedeutet, dass Sie ein wenig durch das Manifest graben müssen.

Beim Durchlaufen Ihrer Pakete (siehe "Auswählen einer Windows Store-App zum Profil" im Abschnitt Startladevorgang weiter oben), sollten Sie die im Manifest des aktuellen Pakets enthaltenen Anwendungen abrufen:

string manifestPath = package.InstalledLocation.Path + "\\AppxManifest.xml";

AppxPackaging.IStream manifestStream;
SHCreateStreamOnFileEx(
                    manifestPath,
                    0x00000040,     // STGM_READ | STGM_SHARE_DENY_NONE
                    0,              // file creation attributes
                    false,          // fCreate
                    null,           // reserved
                    out manifestStream);

IAppxManifestReader manifestReader = appxFactory.CreateManifestReader(manifestStream);

IAppxManifestApplicationsEnumerator appsEnum = manifestReader.GetApplications();

Ja, ein Paket kann mehrere Anwendungen haben, und jede Anwendung verfügt über eine eigene Anwendungsbenutzermodell-ID. Daher sollten Sie Ihren Benutzer fragen, welche Anwendung ein Profil erstellen soll, und die Anwendungsbenutzermodell-ID aus dieser bestimmten Anwendung abrufen:

while (appsEnum.GetHasCurrent() != 0)
{
    IAppxManifestApplication app = appsEnum.GetCurrent();
    string appUserModelId = app.GetAppUserModelId();
    //...
}

Schließlich verfügen Sie jetzt über das, was Sie zum Starten der Windows Store-App benötigen:

IApplicationActivationManager appActivationMgr = new ApplicationActivationManager();
appActivationMgr.ActivateApplication(appUserModelId, appArgs, ACTIVATEOPTIONS.AO_NONE, out pid);

Denken Sie daran, DisableDebugging aufzurufen.

Als Sie IPackageDebugSettings::EnableDebugging aufgerufen haben, haben Sie versprochen, dass Sie nach sich selbst sauber würden, indem Sie die IPackageDebugSettings::D isableDebugging-Methode aufrufen. Achten Sie also darauf, dies zu tun, wenn die Profilerstellungssitzung beendet ist.

Anhängelast

Wenn Ihre Profiler-Benutzeroberfläche die Profiler-DLL an eine Anwendung anfügen möchte, die bereits mit der Ausführung begonnen hat, verwendet sie ICLRProfiling::AttachProfiler. Gleiches gilt für Windows Store-Apps. Stellen Sie zusätzlich zu den oben aufgeführten allgemeinen Überlegungen sicher, dass die Windows Store-Ziel-App nicht angehalten wird.

EnableDebugging

Rufen Sie wie beim Laden des Startvorgangs die IPackageDebugSettings::EnableDebugging-Methode auf . Sie benötigen es nicht zum Übergeben eines Umgebungsblocks, aber Sie benötigen eines seiner anderen Features: das Deaktivieren der automatischen Prozessaufhängung. Andernfalls wird die Windows Store-Ziel-App möglicherweise angehalten, wenn Ihre Profiler-Benutzeroberfläche AttachProfiler aufruft. Dies ist wahrscheinlich, wenn der Benutzer jetzt mit Ihrer Profiler-Benutzeroberfläche interagiert und die Windows Store-App auf keinem der Bildschirme des Benutzers aktiv ist. Wenn die Windows Store-App angehalten wird, kann sie nicht auf ein Signal reagieren, das die CLR an sie sendet, um Ihre Profiler-DLL anzufügen.

Sie sollten also folgendes tun:

IPackageDebugSettings pkgDebugSettings = new PackageDebugSettings();
pkgDebugSettings.EnableDebugging(packageFullName, null /* debuggerCommandLine */,
                                                                 IntPtr.Zero /* environment */);

Dies ist derselbe Aufruf, den Sie für den Startladefall tätigen würden, mit der Ausnahme, dass Sie keine Debugger-Befehlszeile oder einen Umgebungsblock angeben.

DisableDebugging

Vergessen Sie wie immer nicht, IPackageDebugSettings::D isableDebugging aufzurufen, wenn Ihre Profilerstellungssitzung abgeschlossen ist.

Ausführen in der Windows Store-App

Daher hat die Windows Store-App endlich Ihre Profiler-DLL geladen. Jetzt muss Ihrer Profiler-DLL beigebracht werden, wie sie die verschiedenen Regeln für Windows Store-Apps wiedergeben kann, einschließlich der zulässigen APIs und der Ausführung mit reduzierten Berechtigungen.

Bleiben Sie bei den Windows Store-App-APIs.

Wenn Sie die Windows-API durchsuchen, werden Sie feststellen, dass jede API als für Desktop-Apps, Windows Store-Apps oder beides dokumentiert ist. Im Abschnitt Anforderungen der Dokumentation für die Funktion InitializeCriticalSectionAndSpinCount wird beispielsweise angegeben, dass die Funktion nur für Desktop-Apps gilt. Im Gegensatz dazu ist die InitializeCriticalSectionEx-Funktion sowohl für Desktop-Apps als auch für Windows Store-Apps verfügbar.

Behandeln Sie beim Entwickeln Ihrer Profiler-DLL so, als handelt es sich um eine Windows Store-App, und verwenden Sie nur APIs, die für Windows Store-Apps als verfügbar dokumentiert sind. Analysieren Sie Ihre Abhängigkeiten (z. B. können Sie für Ihre Profiler-DLL zur Überwachung ausführen link /dump /imports), und durchsuchen Sie dann die Dokumentation, um zu sehen, welche Ihrer Abhängigkeiten in Ordnung sind und welche nicht. In den meisten Fällen können Ihre Verstöße behoben werden, indem Sie sie einfach durch eine neuere Form der API ersetzen, die als sicher dokumentiert ist (z. B. Ersetzen von InitializeCriticalSectionAndSpinCount durch InitializeCriticalSectionEx).

Möglicherweise stellen Sie fest, dass Ihre Profiler-DLL einige APIs aufruft, die nur für Desktop-Apps gelten, und dennoch scheinen sie zu funktionieren, auch wenn Ihre Profiler-DLL in einer Windows Store-App geladen wird. Beachten Sie, dass es riskant ist, eine API zu verwenden, die nicht für die Verwendung mit Windows Store-Apps in Ihrer Profiler-DLL dokumentiert ist, wenn sie in einen Windows Store-App-Prozess geladen wird:

  • Solche APIs funktionieren nicht, wenn sie im eindeutigen Kontext aufgerufen werden, in dem Windows Store-Apps ausgeführt werden.

  • Solche APIs funktionieren möglicherweise nicht konsistent, wenn sie in verschiedenen Windows Store-App-Prozessen aufgerufen werden.

  • Solche APIs scheinen in Windows Store-Apps in der aktuellen Version von Windows einwandfrei zu funktionieren, werden aber in zukünftigen Versionen von Windows möglicherweise unterbrochen oder deaktiviert.

Der beste Rat ist, alle Ihre Verstöße zu beheben und das Risiko zu vermeiden.

Möglicherweise können Sie feststellen, dass Sie absolut nicht auf eine bestimmte API verzichten können und keinen Ersatz finden, der für Windows Store-Apps geeignet ist. In einem solchen Fall gilt mindestens Folgendes:

  • Testen, testen und testen Sie das lebende Tageslicht aus Ihrer Nutzung dieser API.

  • Verstehen Sie, dass die API in zukünftigen Versionen von Windows in Windows Store-Apps plötzlich unterbrochen oder ausgeblendet werden kann. Dies wird von Microsoft nicht als Kompatibilitätsproblem betrachtet, und die Unterstützung Ihrer Nutzung ist keine Priorität.

Reduzierte Berechtigungen

Es liegt außerhalb des Bereichs dieses Themas, alle Arten aufzulisten, in denen sich Windows Store-App-Berechtigungen von Desktop-Apps unterscheiden. Aber sicherlich ist das Verhalten jedes Mal anders, wenn Ihre Profiler-DLL (beim Laden in eine Windows Store-App im Vergleich zu einer Desktop-App) versucht, auf Ressourcen zuzugreifen. Das Dateisystem ist das häufigste Beispiel. Es gibt nur einige Stellen auf dem Datenträger, auf die eine bestimmte Windows Store-App zugreifen darf (siehe Dateizugriff und Berechtigungen (Windows-Runtime Apps), und Ihre Profiler-DLL unterliegt den gleichen Einschränkungen. Testen Sie Ihren Code gründlich.

Prozessübergreifende Kommunikation

Wie im Diagramm zu Beginn dieses Dokuments gezeigt, muss Ihre Profiler-DLL (in den Windows Store-App-Prozessbereich geladen) wahrscheinlich mit Ihrer Profiler-Benutzeroberfläche (in einem separaten Desktop-App-Prozessbereich ausgeführt) über Ihren eigenen benutzerdefinierten IPC-Kanal (Inter-Process Communication) kommunizieren. Die Profiler-Benutzeroberfläche sendet Signale an die Profiler-DLL, um ihr Verhalten zu ändern, und die Profiler-DLL sendet Daten aus der analysierten Windows Store-App zurück an die Profiler-Benutzeroberfläche zur Nachverarbeitung und Anzeige für den Profiler-Benutzer.

Die meisten Profiler müssen auf diese Weise funktionieren, aber Ihre Auswahlmöglichkeiten für IPC-Mechanismen sind eingeschränkter, wenn Ihre Profiler-DLL in eine Windows Store-App geladen wird. Named Pipes sind beispielsweise nicht Teil des Windows Store App SDK, sodass Sie sie nicht verwenden können.

Aber natürlich sind Dateien immer noch in, wenn auch in einer begrenzteren Weise. Ereignisse sind ebenfalls verfügbar.

Kommunikation über Dateien

Die meisten Daten werden wahrscheinlich über Dateien zwischen der Profiler-DLL und der Profiler-Benutzeroberfläche übergeben. Der Schlüssel besteht darin, einen Dateispeicherort zu wählen, auf den sowohl Ihre Profiler-DLL (im Kontext einer Windows Store-App) als auch die Profiler-Benutzeroberfläche Lese- und Schreibzugriff haben. Der Temporäre Ordnerpfad ist beispielsweise ein Speicherort, auf den sowohl die Profiler-DLL als auch die Profiler-Benutzeroberfläche zugreifen können, aber kein anderes Windows Store-App-Paket zugreifen kann (wodurch alle Informationen, die Sie von anderen Windows Store-App-Paketen protokollieren, abgeschirmt werden).

Sowohl Ihre Profiler-Benutzeroberfläche als auch die Profiler-DLL können diesen Pfad unabhängig bestimmen. Die Profiler-Benutzeroberfläche durchläuft alle Pakete, die für den aktuellen Benutzer installiert sind (siehe Beispielcode weiter oben), und erhält Zugriff auf die PackageId Klasse, von der der Pfad des temporären Ordners mit Code abgeleitet werden kann, der diesem Codeausschnitt ähnelt. (Wie immer wird die Fehlerüberprüfung aus Gründen der Kürze weggelassen.)

// C# code for the Profiler UI.
ApplicationData appData =
    ApplicationDataManager.CreateForPackageFamily(
        packageId.FamilyName);

tempDir = appData.TemporaryFolder.Path;

In der Zwischenzeit kann Ihre Profiler-DLL im Grunde dasselbe tun, obwohl sie mithilfe der ApplicationDataApplicationData.Current-Eigenschaft einfacher zur -Klasse gelangen kann.

Kommunikation über Ereignisse

Wenn Sie eine einfache Signalsemantik zwischen Der Profiler-Benutzeroberfläche und der Profiler-DLL wünschen, können Sie Ereignisse sowohl in Windows Store-Apps als auch in Desktop-Apps verwenden.

In Ihrer Profiler-DLL können Sie einfach die CreateEventEx-Funktion aufrufen, um ein benanntes Ereignis mit einem beliebigen Namen zu erstellen. Zum Beispiel:

// Profiler DLL in Windows Store app (C++).
CreateEventEx(
    NULL,  // Not inherited
    "MyNamedEvent"
    CREATE_EVENT_MANUAL_RESET, /* explicit ResetEvent() required; leave initial state unsignaled */
    EVENT_ALL_ACCESS);

Ihre Profiler-Benutzeroberfläche muss dann das benannte Ereignis unter dem Namespace der Windows Store-App finden. Ihre Profiler-Benutzeroberfläche könnte beispielsweise CreateEventEx aufrufen und den Ereignisnamen als angeben.

AppContainerNamedObjects\<acSid>\MyNamedEvent

<acSid> ist die AppContainer-SID der Windows Store-App. In einem früheren Abschnitt dieses Themas wurde gezeigt, wie Sie die Pakete durchlaufen, die für den aktuellen Benutzer installiert sind. Aus diesem Beispielcode können Sie die packageId abrufen. Und aus der packageId können Sie den mit einem <acSid> Code wie dem folgenden abrufen:

IntPtr acPSID;
DeriveAppContainerSidFromAppContainerName(packageId.FamilyName, out acPSID);

string acSid;
ConvertSidToStringSid(acPSID, out acSid);

string acDir;
GetAppContainerFolderPath(acSid, out acDir);

Keine Benachrichtigungen zum Herunterfahren

Wenn Sie in einer Windows Store-App ausgeführt werden, sollte Ihre Profiler-DLL weder ICorProfilerCallback::Shutdown noch DllMain (mit DLL_PROCESS_DETACH) verwenden, um Ihre Profiler-DLL darüber zu benachrichtigen, dass die Windows Store-App beendet wird. In der Tat sollten Sie davon ausgehen, dass sie nie aufgerufen werden. In der Vergangenheit haben viele Profiler-DLLs diese Benachrichtigungen als bequeme Orte zum Leeren von Caches auf dem Datenträger, zum Schließen von Dateien, zum Senden von Benachrichtigungen an die Profiler-Benutzeroberfläche usw. verwendet. Aber jetzt muss Ihre Profiler-DLL etwas anders organisiert sein.

Ihre Profiler-DLL sollte Informationen während der Zeit protokollieren. Aus Leistungsgründen können Sie Informationen im Arbeitsspeicher batchieren und auf den Datenträger leeren, wenn der Batch über einen Bestimmten Schwellenwert hinaus an Größe wächst. Angenommen, alle Informationen, die noch nicht auf den Datenträger geleert wurden, können verloren gehen. Dies bedeutet, dass Sie Ihren Schwellenwert mit Bedacht auswählen möchten und dass Ihre Profiler-Benutzeroberfläche härter sein muss, um unvollständige Informationen zu behandeln, die von der Profiler-DLL geschrieben wurden.

Windows-Runtime-Metadatendateien

Es liegt außerhalb des Geltungsbereichs dieses Dokuments, ausführliche Informationen zu Windows-Runtime Metadatendateien (WinMD) zu erhalten. Dieser Abschnitt ist auf die Reaktion der CLR-Profilerstellungs-API beschränkt, wenn WinMD-Dateien von der Windows Store-App geladen werden, die Ihre Profiler-DLL analysiert.

Verwaltete und nicht verwaltete WinMDs

Wenn ein Entwickler Visual Studio verwendet, um ein neues Windows-Runtime-Komponentenprojekt zu erstellen, erzeugt ein Build dieses Projekts eine WinMD-Datei, die die metadaten (die Typbeschreibungen von Klassen, Schnittstellen usw.) beschreibt, die vom Entwickler erstellt wurden. Wenn es sich bei diesem Projekt um ein Projekt mit verwalteter Sprache handelt, das in C# oder Visual Basic geschrieben wurde, enthält dieselbe WinMD-Datei auch die Implementierung dieser Typen (d. h. es enthält alle IL, die aus dem Quellcode des Entwicklers kompiliert wurden). Solche Dateien werden als verwaltete WinMD-Dateien bezeichnet. Sie sind interessant, da sie sowohl Windows-Runtime Metadaten als auch die zugrunde liegende Implementierung enthalten.

Wenn ein Entwickler hingegen ein Windows-Runtime Component-Projekt für C++ erstellt, erzeugt ein Build dieses Projekts eine WinMD-Datei, die nur Metadaten enthält, und die Implementierung wird in eine separate native DLL kompiliert. Ebenso enthalten die winMD-Dateien, die im Windows SDK enthalten, nur Metadaten, wobei die Implementierung in separate native DLLs kompiliert wird, die als Teil von Windows ausgeliefert werden.

Die folgenden Informationen gelten sowohl für verwaltete WinMDs, die Metadaten und Implementierungen enthalten, als auch für nicht verwaltete WinMDs, die nur Metadaten enthalten.

WinMD-Dateien sehen wie CLR-Module aus.

Was die CLR angeht, sind alle WinMD-Dateien Module. Die CLR-Profilerstellungs-API teilt Daher Ihrer Profiler-DLL mit, wenn WinMD-Dateien geladen werden und welche Module-IDs es sich handelt, auf die gleiche Weise wie bei anderen verwalteten Modulen.

Ihre Profiler-DLL kann WinMD-Dateien von anderen Modulen unterscheiden, indem sie die ICorProfilerInfo3::GetModuleInfo2-Methode aufruft und den pdwModuleFlags Ausgabeparameter auf das COR_PRF_MODULE_WINDOWS_RUNTIME-Flag überprüft. (Es wird nur festgelegt, wenn die ModuleID eine WinMD darstellt.)

Lesen von Metadaten aus WinMDs

WinMD-Dateien enthalten wie reguläre Module Metadaten, die über die Metadaten-APIs gelesen werden können. Die CLR ordnet jedoch Windows-Runtime Typen .NET Framework Typen zu, wenn winMD-Dateien gelesen werden, sodass Entwickler, die in verwaltetem Code programmieren und die WinMD-Datei nutzen, eine natürlichere Programmiererfahrung haben können. Einige Beispiele für diese Zuordnungen finden Sie unter .NET Framework Support für Windows Store-Apps und Windows-Runtime.

Welche Ansicht erhält Ihr Profiler also, wenn er die Metadaten-APIs verwendet: die Rohansicht Windows-Runtime oder die zugeordnete .NET Framework Ansicht? Die Antwort: Es liegt an Ihnen.

Wenn Sie die ICorProfilerInfo::GetModuleMetaData-Methode für eine WinMD aufrufen, um eine Metadatenschnittstelle zu erhalten, z. B. IMetaDataImport, können Sie auswählen, ob Sie im Parameter „NoTransformdwOpenFlags" festlegen möchten, um diese Zuordnung zu deaktivieren. Andernfalls wird die Zuordnung standardmäßig aktiviert. In der Regel hält ein Profiler die Zuordnung aktiviert, sodass die Zeichenfolgen, die die Profiler-DLL aus den WinMD-Metadaten abruft (z. B. Namen von Typen), dem Profilerbenutzer vertraut und natürlich aussehen.

Bearbeiten von Metadaten aus WinMDs

Das Ändern von Metadaten in WinMDs wird nicht unterstützt. Wenn Sie die ICorProfilerInfo::GetModuleMetaData-Methode für eine WinMD-Datei aufrufen und ofWrite im dwOpenFlags -Parameter angeben oder nach einer beschreibbaren Metadatenschnittstelle wie IMetaDataEmit fragen, schlägt GetModuleMetaData fehl. Dies ist von besonderer Bedeutung für IL-Umschreibungsprofiler, die Metadaten ändern müssen, um ihre Instrumentierung zu unterstützen (z. B. zum Hinzufügen von AssemblyRefs oder neuen Methoden). Daher sollten Sie zuerst nach COR_PRF_MODULE_WINDOWS_RUNTIME suchen (wie im vorherigen Abschnitt erläutert) und keine beschreibbaren Metadatenschnittstellen für solche Module anfordern.

Auflösen von Assemblyverweisen mit WinMDs

Viele Profiler müssen Metadatenverweise manuell auflösen, um die Instrumentierung oder Typprüfung zu unterstützen. Solche Profiler müssen wissen, wie die CLR Assemblyverweise auflöst, die auf WinMDs verweisen, da diese Verweise auf eine völlig andere Weise aufgelöst werden als Standardassemblyverweise.

Speicherprofiler

Der Garbage Collector und der verwaltete Heap unterscheiden sich in einer Windows Store-App und einer Desktop-App nicht grundlegend. Es gibt jedoch einige subtile Unterschiede, die Profilerautoren beachten müssen.

ForceGC erstellt einen verwalteten Thread

Bei der Speicherprofilerstellung erstellt Ihre Profiler-DLL in der Regel einen separaten Thread, von dem aus die ForceGC-Methode aufgerufen werden soll. Das ist nichts Neues. Was jedoch überraschend ist, ist, dass der Vorgang einer Garbage Collection in einer Windows Store-App Ihren Thread in einen verwalteten Thread transformieren kann (z. B. wird eine Profilerstellungs-API-ThreadID für diesen Thread erstellt).

Um die Konsequenzen zu verstehen, ist es wichtig, die Unterschiede zwischen synchronen und asynchronen Aufrufen zu verstehen, die von der CLR-Profilerstellungs-API definiert werden. Beachten Sie, dass sich dies stark vom Konzept asynchroner Aufrufe in Windows Store-Apps unterscheidet. Weitere Informationen finden Sie im Blogbeitrag Warum wir CORPROF_E_UNSUPPORTED_CALL_SEQUENCE haben .

Der relevante Punkt ist, dass Aufrufe von Threads, die von Ihrem Profiler erstellt wurden, immer als synchron betrachtet werden, auch wenn diese Aufrufe von außerhalb einer Implementierung einer der ICorProfilerCallback-Methoden Ihrer Profiler-DLL erfolgen. Zumindest war das bisher so. Nachdem die CLR den Thread Ihres Profilers aufgrund Ihres Aufrufs der ForceGC-Methode in einen verwalteten Thread umgewandelt hat, wird dieser Thread nicht mehr als der Thread Ihres Profilers betrachtet. Daher erzwingt die CLR eine strengere Definition dessen, was für diesen Thread als synchron gilt– nämlich, dass ein Aufruf aus einer der ICorProfilerCallback-Methoden Ihrer Profiler-DLL stammen muss, um als synchron zu gelten.

Was bedeutet das in der Praxis? Die meisten ICorProfilerInfo-Methoden können nur synchron aufgerufen werden und schlagen andernfalls sofort fehl. Wenn Ihre Profiler-DLL ihren ForceGC-Methodenthread also für andere Aufrufe wiederverwendet, die in der Regel für von Profilern erstellte Threads ausgeführt werden (z. B. für RequestProfilerDetach, RequestReJIT oder RequestRevert), treten Probleme auf. Auch eine asynchrone sichere Funktion wie DoStackSnapshot verfügt über spezielle Regeln, wenn sie von verwalteten Threads aufgerufen werden. (Weitere Informationen finden Sie im Blogbeitrag Profiler stack walking: Basics and beyond .)

Daher wird empfohlen, dass jeder Thread, den Ihre Profiler-DLL zum Aufrufen der ForceGC-Methode erstellt, nur zum Auslösen von GCs und dann zum Reagieren auf die GC-Rückrufe verwendet werden sollte. Die Profilerstellungs-API sollte nicht aufgerufen werden, um andere Aufgaben wie Stapelsampling oder Trennen auszuführen.

ConditionalWeakTableReferences

Ab .NET Framework 4.5 gibt es einen neuen GC-Rückruf, ConditionalWeakTableElementReferences, der dem Profiler ausführlichere Informationen zu abhängigen Handles liefert. Diese Handles fügen effektiv einen Verweis von einem Quellobjekt zu einem Zielobjekt für die Gc-Lebensdauerverwaltung hinzu. Abhängige Handles sind nichts Neues, und Entwickler, die in verwaltetem Code programmieren, konnten ihre eigenen abhängigen Handles erstellen, indem sie die System.Runtime.CompilerServices.ConditionalWeakTable<TKey,TValue> -Klasse bereits vor Windows 8 und der .NET Framework 4.5 verwenden.

Verwaltete XAML-Windows Store-Apps verwenden jedoch jetzt stark abhängige Handles. Insbesondere werden sie von der CLR verwendet, um die Verwaltung von Verweiszyklen zwischen verwalteten Objekten und nicht verwalteten Windows-Runtime-Objekten zu unterstützen. Dies bedeutet, dass es heute wichtiger denn je ist, dass Speicherprofiler über diese abhängigen Handles informiert werden, damit sie zusammen mit den restlichen Kanten im Heapdiagramm visualisiert werden können. Ihre Profiler-DLL sollte RootReferences2, ObjectReferences und ConditionalWeakTableElementReferences zusammen verwenden, um eine vollständige Ansicht des Heapdiagramms zu bilden.

Zusammenfassung

Es ist möglich, die CLR-Profilerstellungs-API zu verwenden, um verwalteten Code zu analysieren, der in Windows Store-Apps ausgeführt wird. In der Tat können Sie einen vorhandenen Profiler, den Sie entwickeln, verwenden und einige spezifische Änderungen vornehmen, damit er auf Windows Store-Apps ausgerichtet werden kann. Ihre Profiler-Benutzeroberfläche sollte die neuen APIs zum Aktivieren der Windows Store-App im Debugmodus verwenden. Stellen Sie sicher, dass Ihre Profiler-DLL nur die APIs nutzt, die für Windows Store-Apps gelten. Der Kommunikationsmechanismus zwischen Ihrer Profiler-DLL und der Profiler-Benutzeroberfläche sollte unter Berücksichtigung der Windows Store-App-API-Einschränkungen und unter Berücksichtigung der eingeschränkten Berechtigungen für Windows Store-Apps geschrieben werden. Ihre Profiler-DLL sollte wissen, wie die CLR WinMDs behandelt und wie sich das Verhalten des Garbage Collector in Bezug auf verwaltete Threads unterscheidet.

Ressourcen

Die Common Language Runtime

Die Interaktion der CLR mit dem Windows-Runtime

Windows Store-Apps