Share via


Profileurs CLR et applications du Windows Store

Cette rubrique aborde les éléments à prendre en compte lorsque vous créez des outils de diagnostic en vue d’analyser le code managé qui s’exécute dans une application du Windows Store. Elle explique également comment modifier vos outils de développement existants afin qu’ils continuent à fonctionner lorsque vous les exécutez sur des applications du Windows Store. Pour comprendre ces informations, il est préférable que vous soyez familiarisé avec l’API de profilage Common Language Runtime, que vous ayez déjà utilisé cette API dans un outil de diagnostic qui fonctionne sur les applications de bureau Windows et que vous souhaitiez modifier l’outil pour qu’il s’exécute correctement sur les applications du Windows Store.

Introduction

Si vous avez tout compris de l’introduction, c’est que vous êtes familiarisé avec l’API de profilage CLR. Vous avez déjà écrit un outil de diagnostic qui fonctionne bien sur les applications de bureau managées. Vous êtes maintenant curieux de savoir comment faire pour que votre outil fonctionne avec une application managée du Windows Store. Peut-être avez-vous déjà essayé de le faire pour vous rendre compte que ce n’est pas une tâche aisée. En effet, il existe un certain nombre d’éléments à prendre en compte qui peuvent ne pas être évidents pour tous les développeurs d’outils. Par exemple :

  • Les applications du Windows Store s’exécutent dans un contexte où les autorisations sont très restreintes.

  • Les fichiers de métadonnées Windows présentent des caractéristiques uniques par rapport aux modules managés traditionnels.

  • Les applications du Windows Store ont pour habitude de se mettre en pause quand l’interactivité est interrompue.

  • Vos mécanismes de communication interprocessus peuvent ne plus fonctionner pour diverses raisons.

Cette rubrique liste les éléments que vous devez connaître et explique comment les traiter correctement.

Si vous débutez avec l’API de profilage CLR, passez directement aux ressources situées à la fin de cette rubrique pour obtenir une présentation plus détaillée.

L’apport d’informations concernant certaines API Windows et la façon d’utiliser ces informations ne seront pas abordés dans cette rubrique. Considérez cette rubrique comme un point de départ et reportez-vous à la documentation MSDN pour en savoir plus sur les API Windows référencées ici.

Architecture et terminologie

En règle générale, un outil de diagnostic a une architecture similaire à celle présentée dans l’illustration suivante. Le terme « profileur » est utilisé, cependant, de nombreux outils de ce type vont bien au-delà des performances ou du profilage de mémoire classiques. En effet, ils peuvent être utilisés pour la couverture du code, les frameworks d’objets fictifs, le débogage par voyage dans le temps, le monitoring des applications, etc. Par souci de simplicité, cette rubrique continuera de faire référence à tous ces outils en tant que profileurs.

La terminologie suivante est utilisée dans cette rubrique :

Application

Il s’agit de l’application que le profileur analyse. En règle générale, le développeur de cette application utilise le profileur pour diagnostiquer les problèmes liés à celle-ci. Traditionnellement, cette application est une application de bureau Windows. Cependant, dans cette rubrique, nous allons utiliser des applications du Windows Store.

DLL du profileur

Il s’agit du composant qui est chargé dans l’espace de processus de l’application en cours d’analyse. Ce composant, également appelé « agent », implémente les interfaces ICorProfilerCallbackICorProfilerCallback Interface(2,3,etc.) et consomme les interfaces ICorProfilerInfo(2,3,etc.) pour collecter des données à propos de l’application analysée et éventuellement modifier certains aspects du comportement de l’application.

Interface utilisateur du profileur

Il s’agit d’une application de bureau avec laquelle l’utilisateur du profileur interagit. Elle est chargée d’afficher l’état de l’application pour l’utilisateur et de donner à celui-ci les moyens de contrôler le comportement de l’application analysée. Ce composant s’exécute toujours dans son propre espace de processus, séparément de l’espace de processus de l’application en cours de profilage. L’interface utilisateur du profileur peut également agir comme un « déclencheur d’attachement », qui correspond au processus qui appelle la méthode ICLRProfiling::AttachProfiler pour provoquer le chargement de l’application analysée dans la DLL du profileur, dans les cas où celle-ci n’aurait pas été chargée au démarrage.

Important

Votre interface utilisateur profileur doit rester une application de bureau Windows, même lorsqu’elle est utilisée pour contrôler et signaler une application du Windows Store. Ne vous attendez pas à pouvoir empaqueter et expédier votre outil de diagnostic dans le Windows Store. Votre outil doit effectuer des opérations que les applications du Windows Store ne peuvent pas faire, et la plupart de ces opérations résident dans l’interface utilisateur de votre profileur.

Tout au long de ce document, l’exemple de code part du principe que :

  • La DLL du profileur est écrite en C++, car elle doit être une DLL native, conformément aux exigences de l’API de profilage CLR.

  • L’interface utilisateur du profileur est écrite en C#. Cela n’est pas une obligation. Toutefois, étant donné qu’il n’existe aucune exigence concernant le langage à utiliser pour le processus de l’interface utilisateur du profileur, pourquoi ne pas choisir un langage concis et simple ?

Appareils Windows RT

Les appareils Windows RT sont relativement verrouillés. Les profils tiers ne peuvent simplement pas être chargés sur ces appareils. Ce document se concentre sur les PC Windows 8.

Consommation des API Windows Runtime

Dans plusieurs scénarios décrits dans les sections suivantes, l’application de bureau d’interface utilisateur du profileur doit consommer de nouvelles API Windows Runtime. Vous pouvez consulter la documentation pour savoir quelles API Windows Runtime peuvent être utilisées à partir d’applications de bureau, et si leur comportement est différent de celui obtenu lorsqu’elles sont appelées à partir d’applications du Windows Store.

Si l’interface utilisateur du profileur est écrite en code managé, vous devrez effectuer quelques étapes pour faciliter la consommation de ces API Windows Runtime. Pour plus d’informations, consultez l’article Applications de bureau managées et Windows Runtime.

Chargement de la DLL du profileur

Cette section explique comment l’interface utilisateur du profileur entraîne le chargement de la DLL du profileur par l’application du Windows Store. Le code décrit dans cette section appartient à l’application de bureau de l’interface utilisateur du profileur, et implique donc l’utilisation d’API Windows qui soient sécurisées pour les applications de bureau, mais pas nécessairement pour les applications du Windows Store.

L’interface utilisateur du profileur peut entraîner le chargement de la DLL du profileur dans l’espace de processus de l’application de deux façons :

  • Au démarrage de l’application, comme le déterminent les variables d’environnement.

  • En l’attachant à l’application une fois le démarrage terminé, en appelant la méthode ICLRProfiling::AttachProfiler.

L’une de vos premières difficultés sera de faire fonctionner le chargement au démarrage et l’attachement de la DLL du profileur avec les applications du Windows Store. Les deux formes de chargement ont en commun certaines considérations, que nous allons voir maintenant.

Considérations communes au démarrage et à l’attachement des charges

Signature de la DLL du profileur

Lorsque Windows tente de charger la DLL du profileur, il vérifie que celle-ci est correctement signée. Si ce n’est pas le cas, la charge échouera par défaut. Il existe deux façons d'effectuer cette opération :

  • Vérifiez que la DLL du profileur est signée.

  • Indiquez à votre utilisateur qu’il doit installer une licence de développeur sur son ordinateur Windows 8 avant d’utiliser votre outil. Cette opération peut être effectuée automatiquement à partir de Visual Studio ou manuellement à partir d’une invite de commandes. Pour plus d’informations, consultez Obtenir une licence de développeur.

Autorisations du système de fichiers

L’application du Windows Store doit avoir l’autorisation de charger et d’exécuter la DLL du profileur à partir de l’emplacement du système de fichiers où elle réside. Par défaut, l’application du Windows Store n’a pas cette autorisation sur la plupart des répertoires, et toute tentative de chargement de la DLL du profileur générera une entrée dans le journal des événements de l’application Windows, similaire à celle-ci :

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].

En règle générale, les applications du Windows Store sont autorisées à accéder à un ensemble limité d’emplacements sur le disque. Chaque application du Windows Store peut accéder à ses propres dossiers de données d’application, ainsi qu’à quelques autres zones du système de fichiers auxquelles toutes les applications du Windows Store ont accès. Il est préférable d’installer la DLL du profileur et ses dépendances dans un emplacement situé sous Program Files ou Program Files (x86), car toutes les applications du Windows Store disposent par défaut d’autorisations de lecture et d’exécution dans ces répertoires.

Chargement au démarrage

En règle générale, dans une application de bureau, l’interface utilisateur du profileur invite à charger la DLL du profileur au démarrage en initialisant un bloc d’environnement contenant les variables d’environnement de l’API de profilage CLR nécessaires (par exemple, COR_PROFILER, COR_ENABLE_PROFILING et COR_PROFILER_PATH), puis en créant un processus avec ce bloc d’environnement. Il en va de même pour les applications du Windows Store, cependant, les mécanismes sont différents.

N’utilisez pas le niveau d’intégrité élevé pour l’exécution

Si le processus A tente de générer le processus B de l’application du Windows Store, le processus A doit être exécuté à un niveau d’intégrité moyen, et non à un niveau d’intégrité élevé. Cela signifie que l’interface utilisateur du profileur doit s’exécuter à un niveau d’intégrité moyen, ou qu’elle doit générer un autre processus de bureau à un niveau d’intégrité moyen pour prendre en charge le lancement de l’application du Windows Store.

Choix de l’application du Windows Store à profiler

Tout d’abord, vous devez demander à l’utilisateur de votre profileur quelle application du Windows Store il souhaite lancer. Pour les applications de bureau, vous pouvez afficher une boîte de dialogue Parcourir les fichiers, où l’utilisateur pourra trouver et sélectionner un fichier .exe. Toutefois, les applications du Windows Store sont différentes et l’utilisation d’une boîte de dialogue de type Parcourir… n’a pas de sens. Il est préférable d’afficher la liste des applications du Windows Store qui sont installées pour que l’utilisateur puisse les sélectionner.

Vous pouvez utiliser la classe PackageManager pour générer cette liste. PackageManager est une classe Windows Runtime qui est disponible uniquement pour les applications de bureau.

L’exemple de code suivant, qui est issu d’un exemple d’interface utilisateur de profileur écrite en tant qu’application de bureau en C#, utilise PackageManager pour générer une liste d’applications Windows :

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

Spécification du bloc d’environnement personnalisé

Une nouvelle interface COM, IPackageDebugSettings, vous permet de personnaliser le comportement d’exécution d’une application du Windows Store afin de faciliter certaines formes de diagnostic. L’une de ses méthodes , EnableDebugging, vous permet de passer un bloc d’environnement à l’application du Windows Store lors de son lancement. Elle permet également de désactiver la suspension automatique des processus, par exemple. Le bloc d’environnement est important, car c’est là que vous devez spécifier les variables d’environnement (COR_PROFILERet COR_ENABLE_PROFILINGCOR_PROFILER_PATH)) utilisées par le CLR pour charger votre DLL Profiler.

Prenez l'exemple de l'extrait de code suivant :

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

Il y a quelques points qu’il est nécessaire de bien comprendre :

  • packageFullName peut être déterminé lors de l’itération sur les packages et de la récupération de package.Id.FullName.

  • debuggerCommandLine est un peu plus intéressant. Pour passer le bloc d’environnement personnalisé à l’application du Windows Store, vous devez écrire votre propre débogueur factice et simple. Windows génère l’application du Windows Store suspendue, puis attache votre débogueur en le lançant à l’aide d’une ligne de commande, comme dans cet exemple :

    MyDummyDebugger.exe -p 1336 -tid 1424
    

    -p 1336 signifie que l’application du Windows Store a l’ID de processus 1336, et où -tid 1424 signifie que l’ID de thread 1424 est le thread qui a été suspendu. Votre débogueur factice analyse le ThreadID à partir de la ligne de commande, reprend ce thread, puis se ferme.

    Voici un exemple de code C++ qui permet d’effectuer cette opération (n’oubliez pas de vérifier les erreurs) :

    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;
    }
    

    Vous devez déployer ce débogueur factice dans le cadre de l’installation de votre outil de diagnostic, puis spécifier le chemin de ce débogueur dans le paramètre debuggerCommandLine.

Lancement de l’application du Windows Store

Le moment est venu de lancer l’application du Windows Store. Si vous avez déjà essayé de le faire vous-même, vous avez peut-être remarqué que CreateProcess n’est pas ce qui permet de créer un processus d’application du Windows Store. C’est la méthode IApplicationActivationManager::ActivateApplication que vous devez utiliser. Pour ce faire, vous devez obtenir l’ID de modèle utilisateur de l’application du Windows Store que vous lancez. Cela signifie que vous devrez analyser le manifeste un peu plus en profondeur.

Lors d’une itération sur vos packages (voir « Choix de l’application du Windows Store à profiler » dans la section Chargement au démarrage située plus haut), vous devrez récupérer l’ensemble d’applications qui se trouvent dans le manifeste du package actuel :

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();

Oui, un package peut avoir plusieurs applications, et chaque application a son propre ID de modèle utilisateur d’application. Vous devez donc demander à votre utilisateur quelle application profiler, et récupérer l’ID de modèle utilisateur de l’application à partir de cette application :

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

Vous avez désormais tout ce dont vous avez besoin pour lancer l’application du Windows Store :

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

N’oubliez pas d’appeler DisableDebugging

Lorsque vous avez appelé IPackageDebugSettings::EnableDebugging, vous avez promis d’effectuer un nettoyage en appelant la méthode IPackageDebugSettings::DisableDebugging. N’oubliez donc pas de le faire une fois la session de profilage terminée.

Attacher le chargement

Lorsque l’interface utilisateur du profileur souhaite attacher sa DLL de profileur à une application qui a déjà commencé à s’exécuter, elle utilise ICLRProfiling::AttachProfiler. Il en va de même avec les applications du Windows Store. Toutefois, outre les considérations communes listées précédemment, vérifiez que l’application du Windows Store cible n’est pas suspendue.

EnableDebugging

Comme pour le chargement au démarrage, appelez la méthode IPackageDebugSettings::EnableDebugging. Vous n’en avez pas besoin pour passer un bloc d’environnement, mais vous avez besoin de l’une de ses autres fonctionnalités, qui est la désactivation de la suspension automatique du processus. Sinon, lorsque l’interface utilisateur du profileur appellera AttachProfiler, l’application du Windows Store cible risque d’être suspendue. En fait, ce risque est probable si l’utilisateur interagit avec l’interface utilisateur du profileur et que l’application du Windows Store n’est active sur aucun des écrans de l’utilisateur. Et si l’application du Windows Store est suspendue, elle ne pourra répondre à aucun signal que le CLR lui enverra pour attacher la DLL du profileur.

Vous devrez donc faire quelque chose comme ceci :

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

Il s’agit du même appel que vous effectueriez pour le chargement au démarrage, sauf que vous ne spécifiez pas de ligne de commande de débogueur ni de bloc d’environnement.

DisableDebugging

Comme toujours, n’oubliez pas d’appeler IPackageDebugSettings::DisableDebugging lorsque votre session de profilage est terminée.

Exécution à l’intérieur de l’application du Windows Store

L’application du Windows Store a enfin chargé la DLL du profileur. À présent, la DLL du profileur doit apprendre les différentes règles exigées par les applications du Windows Store, y compris les API qui sont autorisées et comment s’exécuter avec des autorisations restreintes.

Continuer à utiliser les API d’applications du Windows Store

Lorsque vous parcourrez l’API Windows, vous remarquerez que chaque API est documentée comme étant applicable aux applications de bureau, aux applications du Windows Store, ou aux deux. Par exemple, la section Exigences de la documentation sur la fonction InitializeCriticalSectionAndSpinCount indique que la fonction s’applique uniquement aux applications de bureau. En revanche, la fonction InitializeCriticalSectionEx est disponible pour les applications de bureau et les applications du Windows Store.

Lorsque vous développez la DLL du profileur, traitez-la comme s’il s’agissait d’une application du Windows Store et utilisez uniquement des API documentées comme étant disponibles pour les applications du Windows Store. Analysez vos dépendances (par exemple, vous pouvez exécuter link /dump /imports sur la DLL du profileur à des fins d’audit), puis effectuez des recherches dans la documentation pour voir quelles dépendances sont correctes et lesquelles sont incorrectes. Dans la plupart des cas, les problèmes peuvent être corrigés en les remplaçant simplement par la forme la plus récente de l’API documentée comme étant sûre (par exemple, en remplaçant InitializeCriticalSectionAndSpinCount par InitializeCriticalSectionEx).

Vous remarquerez peut-être que la DLL du profileur appelle certaines API qui s’appliquent uniquement aux applications de bureau, et qu’elles semblent fonctionner même lorsque la DLL du profileur est chargée dans une application du Windows Store. Sachez toutefois qu’il est risqué d’utiliser une API non documentée pour une utilisation avec des applications du Windows Store dans la DLL du profileur, lorsque vous la chargez dans un processus d’application du Windows Store :

  • Il n’est pas garanti que ces API fonctionnent lorsqu’elles sont appelées dans le contexte unique où sont exécutées les applications du Windows Store.

  • Ces API peuvent ne pas fonctionner de manière uniforme lorsqu’elles sont appelées par les différents processus d’application du Windows Store.

  • Ces API peuvent sembler fonctionner correctement avec les applications du Windows Store dans la version actuelle de Windows, mais peuvent cesser de fonctionner ou être désactivées dans les versions ultérieures de Windows.

La meilleure chose à faire est de corriger tous vos problèmes afin d’éviter les risques.

Vous pouvez vous rendre compte que vous ne pouvez absolument pas vous passer d’une API en particulier et ne pas trouver d’API de remplacement qui soit adaptée aux applications du Windows Store. Dans ce cas, vous devez au minimum :

  • Testez absolument toutes vos utilisations de cette API.

  • Sachez que l’API peut soudainement s’arrêter ou disparaître si elle est appelée à partir d’applications du Windows Store dans les prochaines versions de Windows. Cela ne sera pas considéré comme un problème de compatibilité par Microsoft, et la prise en charge de votre utilisation ne sera pas une priorité.

Autorisations restreintes

Cette rubrique n’a pas pour but de lister toutes les différences qui existent entre les autorisations des applications du Windows Store et celles des applications de bureau. Cependant, il est certain que le comportement sera différent chaque fois que la DLL du profileur (lorsqu’elle est chargée dans une application du Windows Store et non dans une application de bureau) tentera d’accéder à l’une des ressources. Le système de fichiers en est l’exemple le plus courant. Il existe toutefois quelques emplacements sur le disque auxquels une application du Windows Store est autorisée à accéder (voir Accès aux fichiers et autorisations (applications Windows Runtime). La DLL du profileur sera soumise aux mêmes restrictions. Testez l’intégralité de votre code.

Communication entre processus

Comme indiqué dans le diagramme au début de ce document, la DLL du profileur (chargée dans l’espace de processus de l’application du Windows Store) devra probablement communiquer avec l’interface utilisateur du profileur (qui s’exécute dans un espace de processus d’application de bureau distinct) via votre propre canal de communication interprocessus personnalisé (IPC). L’interface utilisateur du profileur envoie des signaux à la DLL du profileur afin de modifier son comportement, et la DLL du profileur envoie des données sur l’application du Windows Store analysée à l’interface utilisateur du profileur à des fins de post-traitement et d’affichage pour l’utilisateur du profileur.

La plupart des profileurs doivent fonctionner de cette façon. Toutefois, vos choix pour les mécanismes IPC sont plus limités lorsque la DLL du profileur est chargée dans une application du Windows Store. Par exemple, les canaux nommés ne font pas partie du SDK de l’application du Windows Store. Vous ne pouvez donc pas les utiliser.

Bien entendu, les fichiers sont toujours là, bien que de manière plus restreinte. Les événements sont également disponibles.

Communication via des fichiers

La plupart de vos données passeront probablement de la DLL du profileur à l’interface utilisateur du profileur via des fichiers. La clé est de choisir un emplacement de fichier auquel la DLL du profileur (dans le contexte d’une application du Windows Store) et l’interface utilisateur du profileur ont accès, aussi bien en lecture qu’en écriture. Par exemple, le chemin du dossier Temp est un emplacement auquel la DLL du profileur et l’interface utilisateur du profileur peuvent accéder, mais auquel aucun autre package d’application du Windows Store ne peut accéder (ce qui protège les informations que vous avez journalisées à partir d’autres packages d’applications du Windows Store).

L’interface utilisateur du profileur et la DLL du profileur peuvent déterminer ce chemin indépendamment. L’interface utilisateur du profileur, lorsqu’elle effectue une itération dans tous les packages installés pour l’utilisateur actuel (voir l’exemple de code précédent), obtient l’accès à la classe PackageId à partir de laquelle le chemin du dossier temporaire peut être dérivé avec du code similaire à cet extrait de code. (Comme toujours, la vérification des erreurs a été omise par souci de concision.)

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

tempDir = appData.TemporaryFolder.Path;

Pendant ce temps, la DLL du profileur peut faire la même chose, même si elle peut plus facilement accéder à la classe ApplicationData à l’aide de la propriété ApplicationData.Current.

Communication via des événements

Si vous souhaitez une sémantique de signalisation simple entre l’interface utilisateur du profileur et la DLL du profileur, vous pouvez utiliser des événements dans des applications du Windows Store ainsi que des applications de bureau.

À partir de la DLL du profileur, vous pouvez simplement appeler la fonction CreateEventEx pour créer un événement nommé portant le nom de votre choix. Par exemple :

// 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);

L’interface utilisateur du profileur doit ensuite rechercher cet événement nommé sous l’espace de noms de l’application du Windows Store. Par exemple, l’interface utilisateur du profileur peut appeler CreateEventEx, en spécifiant le nom de l’événement en tant que :

AppContainerNamedObjects\<acSid>\MyNamedEvent

<acSid> est le SID AppContainer de l’application du Windows Store. Dans une précédente section de cette rubrique, nous avons vu comment itérer sur les packages installés pour l’utilisateur actuel. À partir de cet exemple de code, vous pouvez obtenir le packageId. À partir du packageId, vous pouvez obtenir le <acSid> avec du code similaire à celui-ci :

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

string acSid;
ConvertSidToStringSid(acPSID, out acSid);

string acDir;
GetAppContainerFolderPath(acSid, out acDir);

Aucune notification d’arrêt

Lorsqu’elle s’exécute à l’intérieur d’une application du Windows Store, la DLL du profileur ne doit pas compter sur ICorProfilerCallback::Shutdown ni même sur DllMain (avec DLL_PROCESS_DETACH) pour être appelés dans le but d’avertir la DLL du profileur que l’application du Windows Store est en cours de fermeture. En fait, vous devez vous attendre à ce qu’ils ne soient jamais appelés. Historiquement, de nombreuses DLL de profileur ont utilisé ces notifications comme des emplacements pratiques où vider leurs caches sur le disque, mais également pour fermer des fichiers, renvoyer des notifications à l’interface utilisateur du profileur, etc. À présent, la DLL du profileur doit être organisée un peu différemment.

La DLL du profileur doit journaliser les informations au fur et à mesure. Pour des raisons de performances, vous pouvez souhaiter traiter les informations par lot dans la mémoire, et les vider sur le disque lorsque le lot s’agrandit et dépasse un certain seuil. Partez toutefois du principe que toute information non encore vidée sur le disque risque d’être perdue. Cela signifie que vous devez choisir votre seuil de manière judicieuse, et que l’interface utilisateur du profileur doit être renforcée pour savoir gérer les informations incomplètes qui sont écrites par la DLL du profileur.

Fichiers de métadonnées Windows Runtime

Dans ce document, nous n’aborderons pas en détail les fichiers de métadonnées Windows Runtime (WinMD). Cette section se limite à la façon dont l’API de profilage CLR réagit lorsque les fichiers WinMD sont chargés par l’application Windows Store que la DLL du profileur analyse.

WinMD managés et non managés

Si un développeur utilise Visual Studio pour créer un projet de composant Windows Runtime, une build de ce projet produira un fichier WinMD décrivant les métadonnées (descriptions des types de classes, interfaces, etc.) créées par le développeur. Si ce projet est un projet de langage managé écrit en C# ou en Visual Basic, ce même fichier WinMD contiendra également l’implémentation de ces types (ce qui signifie qu’il contiendra tout le langage intermédiaire compilé à partir du code source du développeur). Ces fichiers sont appelés « fichiers WinMD managés ». Ils sont intéressants car ils contiennent à la fois les métadonnées Windows Runtime et l’implémentation sous-jacente.

En revanche, si un développeur crée un projet de composant Windows Runtime pour C++, une build de ce projet produira un fichier WinMD qui contiendra uniquement des métadonnées, et l’implémentation sera compilée dans une DLL native distincte. De même, les fichiers WinMD qui sont fournis dans le SDK Windows contiennent uniquement des métadonnées, et l’implémentation est compilée dans des DLL natives distinctes qui sont fournies comme faisant partie de Windows.

Les informations ci-dessous s’appliquent aux WinMD managés, qui contiennent des métadonnées et une implémentation, ainsi qu’aux WinMD non managés, qui contiennent uniquement des métadonnées.

Les fichiers WinMD ressemblent à des modules CLR

En ce qui concerne le CLR, tous les fichiers WinMD sont des modules. L’API de profilage CLR indique donc à la DLL du profileur à quel moment les fichiers WinMD sont chargés et quel est leur ModuleID, comme elle le fait pour les autres modules managés.

La DLL du profileur peut distinguer les fichiers WinMD des autres modules en appelant la méthode ICorProfilerInfo3::GetModuleInfo2 et en inspectant le paramètre de sortie pdwModuleFlags pour l’indicateur COR_PRF_MODULE_WINDOWS_RUNTIME. (Elle est définie si, et uniquement si, le ModuleID représente un WinMD.)

Lecture des métadonnées à partir des WinMD

Les fichiers WinMD, comme les autres modules, contiennent des métadonnées qui peuvent être lues via les API de métadonnées. Toutefois, le CLR mappe les types Windows Runtime aux types .NET Framework lorsqu’il lit des fichiers WinMD, afin que les développeurs qui programment avec du code managé et consomment le fichier WinMD puissent avoir une expérience de programmation plus naturelle. Pour obtenir des exemples de ces mappages, consultez Prise en charge de .NET Framework pour les applications du Windows Store et Windows Runtime.

Quelle vue aura donc votre profileur lorsqu’il utilisera les API de métadonnées : l’affichage brut Windows Runtime ou la vue .NET Framework mappée ? Réponse : c’est à vous de choisir.

Lorsque vous appelez la méthode ICorProfilerInfo::GetModuleMetaData sur un fichier WinMD pour obtenir une interface de métadonnées telle que IMetaDataImport, vous pouvez choisir de définir ofNoTransform dans le paramètre dwOpenFlags pour désactiver ce mappage. Sinon, par défaut, le mappage sera activé. En règle générale, un profileur garde le mappage activé pour que les chaînes que la DLL du profileur obtient à partir des métadonnées WinMD (par exemple, les noms des types) semblent familières et naturelles à l’utilisateur du profileur.

Modification des métadonnées à partir des WinMD

La modification des métadonnées dans les fichiers WinMD n’est pas prise en charge. Si vous appelez la méthode ICorProfilerInfo::GetModuleMetaData pour un fichier WinMD, et spécifiez ofWrite dans le paramètre dwOpenFlags ou demandez une interface de métadonnées accessible en écriture comme IMetaDataEmit, GetModuleMetaData échouera. Ceci est très important pour les profileurs de réécriture de langage intermédiaire, car ils doivent modifier les métadonnées pour prendre en charge leur instrumentation (par exemple, pour ajouter AssemblyRefs ou de nouvelles méthodes). Vous devez donc d’abord vérifier COR_PRF_MODULE_WINDOWS_RUNTIME (comme indiqué dans la section précédente) et éviter de demander des interfaces de métadonnées pouvant être écrites sur ces modules.

Résolution des références d’assembly avec des WinMD

De nombreux profileurs doivent résoudre manuellement les références de métadonnées pour faciliter l’instrumentation ou l’inspection des types. Ces profileurs doivent savoir comment le CLR résout les références d’assembly qui pointent vers les WinMD, car ces références sont résolues d’une manière complètement différente des références d’assembly standard.

Profileurs de mémoire

Le garbage collector et le tas managé ne sont pas fondamentalement différents dans une application Windows Store et dans une application de bureau. Toutefois, il existe quelques différences subtiles que les auteurs de profileurs doivent connaître.

ForceGC crée un thread managé

Lorsque vous effectuez un profilage de mémoire, la DLL du profileur crée généralement un thread distinct à partir duquel appeler la méthode ForceGC. Tout ceci n’est pas nouveau. Cependant, ce qui peut paraître surprenant, est que le fait d’effectuer un garbage collection à l’intérieur d’une application du Windows Store peut transformer votre thread en thread managé (par exemple, un ThreadID d’API de profilage sera créé pour ce thread).

Pour en comprendre les conséquences, il est important de connaître les différences entre les appels synchrones et les appels asynchrones qui sont définis par l’API de profilage CLR. Notez que ceci est très différent du concept des appels asynchrones dans les applications du Windows Store. Pour plus d’informations, consultez le billet de blog Why we have CORPROF_E_UNSUPPORTED_CALL_SEQUENCE.

Ce qu’il faut retenir est que les appels effectués sur les threads créés par votre profileur sont toujours considérés comme synchrones, même si ces appels sont effectués en dehors de l’implémentation de l’une des méthodes ICorProfilerCallback de la DLL de profileur. Du moins, c’était le cas précédemment. Maintenant que le CLR a transformé le thread de votre profileur en thread managé en raison de votre appel à la méthode ForceGC, ce thread n’est plus considéré comme le thread de votre profileur. Par conséquent, le CLR applique une définition plus stricte de ce qui est qualifié de synchrone pour ce thread, à savoir qu’un appel doit provenir de l’une des méthodes ICorProfilerCallback de la DLL de profileur pour être synchrone.

Qu’est-ce que cela signifie dans la pratique ? La plupart des méthodes ICorProfilerInfo sont sûres uniquement si elles sont appelées de manière synchrone. Dans le cas contraire, elles échouent immédiatement. Par conséquent, si la DLL du profileur réutilise le thread de la méthode ForceGC pour d’autres appels généralement effectués sur des threads créés par un profileur (par exemple, pour RequestProfilerDetach, RequestReJIT ou RequestRevert), vous rencontrerez des problèmes. Même une fonction asynchrone sûre telle que DoStackSnapshot a des règles spéciales lorsqu’elle est appelée à partir de threads managés. (Pour plus d’informations, consultez le billet de blog Profiler stack walking: Basics and beyond.)

Par conséquent, il est recommandé que tous les threads créés par la DLL du profileur pour appeler la méthode ForceGC soient utilisés uniquement pour déclencher des nettoyages de la mémoire, puis pour répondre aux rappels de nettoyage de la mémoire. Ils ne doivent pas appeler l’API de profilage pour effectuer d’autres tâches telles que l’échantillonnage de pile ou le détachement.

ConditionalWeakTableReferences

À partir de .NET Framework 4.5, il existe un nouveau rappel de nettoyage de la mémoire (ConditionalWeakTableElementReferences) qui fournit aux profileurs des informations plus complètes sur les descripteurs dépendants. Ces descripteurs ajoutent une référence d’un objet source à un objet cible dans le cadre de la gestion de la durée de vie des nettoyages de mémoire. Les descripteurs dépendants ne sont pas nouveaux, et les développeurs qui programment avec du code managé ont pu créer leurs propres descripteurs dépendants à l’aide de la classe System.Runtime.CompilerServices.ConditionalWeakTable<TKey,TValue>, même avant Windows 8 et .NET Framework 4.5.

Toutefois, les applications du Windows Store en XAML managé font désormais un usage important des descripteurs dépendants. En effet, le CLR les utilise pour faciliter la gestion des cycles de références entre les objets managés et les objets Windows Runtime non managés. Cela signifie qu’il est plus important que jamais pour les profileurs de mémoire d’être informés de ces descripteurs dépendants, afin qu’ils puissent les visualiser avec le reste des bords dans le graphe de tas. La DLL du profileur doit utiliser RootReferences2, ObjectReferences et ConditionalWeakTableElementReferences conjointement pour former une vue complète du graphe de tas.

Conclusion

Il est possible d’utiliser l’API de profilage CLR pour analyser le code managé qui s’exécute dans les applications du Windows Store. En fait, vous pouvez prendre un profileur existant que vous développez et apporter des modifications spécifiques afin qu’il puisse cibler des applications du Windows Store. L’interface utilisateur du profileur doit utiliser les nouvelles API pour activer l’application du Windows Store en mode débogage. Veillez à ce que la DLL du profileur consomme uniquement les API applicables aux applications du Windows Store. Pour écrire le mécanisme de communication entre la DLL du profileur et l’interface utilisateur du profileur, vous devez prendre en compte les restrictions des API d’application du Windows Store et connaître les autorisations restreintes qui sont en place pour les applications du Windows Store. La DLL du profileur doit savoir comment le CLR traite les WinMD et en quoi le comportement du garbage collector est différent au niveau des threads managés.

Ressources

Common Language Runtime

Interaction du CLR avec le Windows Runtime

Applications Windows Store