Aplicaciones de la Tienda Windows y generadores de perfiles CLR
En este tema se describen las cuestiones que debe tener en cuenta al escribir herramientas de diagnóstico que analizan el código administrado que se ejecuta dentro de una aplicación de la Tienda Windows. También se proporcionan instrucciones para modificar las herramientas de desarrollo existentes para que sigan funcionando al ejecutarlas en aplicaciones de la Tienda Windows. Para entender esta información, lo mejor es que esté familiarizado con la API de generación de perfiles de Common Language Runtime, que ya haya utilizado esta API en una herramienta de diagnóstico que se ejecuta correctamente en las aplicaciones de escritorio de Windows y que ahora esté interesado en modificar la herramienta para que se ejecute correctamente en las aplicaciones de la Tienda Windows.
Introducción
Si ha pasado el párrafo introductorio, entonces está familiarizado con la API de generación de perfiles de CLR. Ya ha escrito una herramienta de diagnóstico que funciona bien con aplicaciones de escritorio administradas. Ahora tiene curiosidad por saber qué hacer para que su herramienta funcione con una aplicación administrada de la Tienda Windows. Tal vez ya haya intentado que esto funcione y haya descubierto que no es una tarea sencilla. De hecho, hay diversos aspectos que podrían no resultar evidentes para todos los desarrolladores de herramientas. Por ejemplo:
Las aplicaciones de la Tienda Windows se ejecutan en un contexto con permisos muy reducidos.
Los archivos de metadatos de Windows tienen características únicas en comparación con los módulos administrados tradicionales.
Las aplicaciones de la Tienda Windows tienen la costumbre de suspenderse cuando la interactividad cae.
Es posible que los mecanismos de comunicación entre procesos ya no funcionen por varias razones.
En este tema se enumeran los aspectos que debe tener en cuenta y cómo tratarlos correctamente.
Si no está familiarizado con la API de generación de perfiles de CLR, vaya directamente a Recursos al final de este tema para encontrar una mejor información introductoria.
La información con detalles sobre las API de Windows específicas y su uso también está fuera del ámbito de este tema. Considere este tema como un punto de partida, y consulte MSDN para obtener más información sobre cualquier API de Windows a la que se haga referencia aquí.
Arquitectura y terminología
Normalmente, una herramienta de diagnóstico tiene una arquitectura como la que se muestra en la ilustración siguiente. Usa el término "generador de perfiles", pero muchas de estas herramientas van mucho más allá de la típica generación de perfiles de rendimiento o memoria y se adentran en ámbitos como la cobertura de código, los marcos de objetos ficticios, la depuración de viajes en el tiempo, la supervisión de aplicaciones, etc. Para simplificar, este tema se seguirá refiriendo a todas estas herramientas como "generadores de perfiles".
En este tema se usa la terminología siguiente:
Aplicación
Esta es la aplicación que está analizando el generador de perfiles. Normalmente, el desarrollador de esta aplicación ahora usa el generador de perfiles para ayudar a diagnosticar problemas con la aplicación. Tradicionalmente, esta aplicación sería una aplicación de escritorio de Windows, pero en este tema, estamos viendo las aplicaciones de la Tienda Windows.
DLL del generador de perfiles
Este es el componente que se carga en el espacio de proceso de la aplicación que se está analizando. Este componente, también conocido como el "agente" del generador de perfiles, implementa las interfaces ICorProfilerCallbackICorProfilerCallback(2,3,etc.) y consume las interfaces ICorProfilerInfo(2,3,etc.) para recoger datos sobre la aplicación analizada y potencialmente modificar aspectos del comportamiento de la aplicación.
IU del generador de perfiles
Se trata de una aplicación de escritorio con la que interactúa el usuario del generador de perfiles. Es responsable de mostrar el estado de la aplicación al usuario y darle los medios para controlar el comportamiento de la aplicación analizada. Este componente siempre se ejecuta en su propio espacio de proceso, separado del espacio de proceso de la aplicación cuyo perfil se está generando. La interfaz de usuario del generador de perfiles también puede actuar como el "desencadenador de asociación", que es el proceso que llama al método ICLRProfiling::AttachProfiler para hacer que la aplicación analizada cargue la DLL del generador de perfiles en aquellos casos en los que la DLL del generador de perfiles no se haya cargado al iniciarse.
Importante
La interfaz de usuario del generador de perfiles debe permanecer en una aplicación de escritorio de Windows, incluso cuando se usa para controlar una aplicación de la Tienda Windows e informar sobre ella. No espere poder empaquetar y enviar su herramienta de diagnóstico en la Tienda Windows. La herramienta debe hacer cosas que las aplicaciones de la Tienda Windows no pueden hacer, y muchas de esas cosas residen dentro de la interfaz de usuario del generador de perfiles.
En este documento, el código de ejemplo supone que:
El archivo DLL del generador de perfiles se escribe en C++, ya que debe ser un archivo DLL nativo, según los requisitos de la API de generación de perfiles de CLR.
La interfaz de usuario del generador de perfiles está escrita en C#. Esto no es necesario, pero ya que no hay requisitos sobre el lenguaje para el proceso de su IU del generador de perfiles, ¿por qué no elegir un lenguaje que sea conciso y simple?
Dispositivos Windows RT
Los dispositivos Windows RT están bastante bloqueados. Los generadores de perfiles de terceros simplemente no se pueden cargar en dichos dispositivos. Este documento se centra en los equipos con Windows 8.
Consumo de API de Windows Runtime
En varios escenarios descritos en las secciones siguientes, la aplicación de escritorio de la interfaz de usuario del generador de perfiles debe consumir algunas API de Windows Runtime nuevas. Querrá consultar la documentación para comprender qué API de Windows Runtime se pueden usar desde aplicaciones de escritorio y si su comportamiento es diferente cuando se les llama desde aplicaciones de escritorio y aplicaciones de la Tienda Windows.
Si la interfaz de usuario del generador de perfiles está escrita en código administrado, tendrá que realizar algunos pasos para facilitar el consumo de esas API de Windows Runtime. Para más información, consulte el artículo Aplicaciones de escritorio administradas y Windows Runtime.
Carga del archivo DLL del generador de perfiles
En esta sección se describe cómo la interfaz de usuario del generador de perfiles hace que la aplicación de la Tienda Windows cargue el archivo DLL del generador de perfiles. El código descrito en esta sección pertenece a la aplicación de escritorio de la interfaz de usuario del generador de perfiles y, por lo tanto, implica el uso de API de Windows que son seguras para aplicaciones de escritorio, pero no necesariamente seguras para las aplicaciones de la Tienda Windows.
La interfaz de usuario del generador de perfiles puede hacer que el archivo DLL del generador de perfiles se cargue en el espacio de proceso de la aplicación de dos maneras:
En el inicio de la aplicación, tal como se controla mediante variables de entorno.
Al adjuntarse a la aplicación una vez completado el inicio, llamando al método ICLRProfiling::AttachProfiler.
Uno de los primeros obstáculos será conseguir que la carga de inicio y la carga de asociación de la DLL del generador de perfiles funcionen correctamente con las aplicaciones de la Tienda Windows. Ambas formas de carga comparten algunas consideraciones especiales, así que comencemos con ellas.
Consideraciones comunes para las cargas de inicio y asociación
Firma del archivo DLL del generador de perfiles
Cuando Windows intenta cargar el archivo DLL del generador de perfiles, comprueba que está firmado correctamente. Si no es así, se produce un error en la carga de forma predeterminada. Existen dos formas de hacerlo:
Asegúrese de que el archivo DLL del generar perfiles está firmado.
Indique al usuario que debe instalar una licencia de desarrollador en su máquina con Windows 8 antes de usar la herramienta. Esto se puede hacer automáticamente desde Visual Studio o manualmente desde un símbolo del sistema. Para más información, vea Obtener una licencia de desarrollador.
Permisos del sistema de archivos
La aplicación de la Tienda Windows debe tener permiso para cargar y ejecutar el archivo DLL del generador de perfiles desde la ubicación en el sistema de archivos en la que reside. De forma predeterminada, la aplicación de la Tienda Windows no tiene ese permiso en la mayoría de los directorios, y cualquier intento erróneo de cargar el archivo DLL del generador de perfiles generará una entrada en el registro de eventos de la aplicación de Windows con un aspecto similar al siguiente:
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].
Por lo general, las aplicaciones de la Tienda Windows solo pueden acceder a un conjunto limitado de ubicaciones en el disco. Cada aplicación de la Tienda Windows puede acceder a sus propias carpetas de datos de la aplicación, así como a otras áreas del sistema de archivos a las que todas las aplicaciones de la Tienda Windows tienen acceso. Es mejor instalar el archivo DLL del generador de archivos y sus dependencias en algún lugar en Archivos de programa o Archivos de programa (x86), ya que todas las aplicaciones de la Tienda Windows tienen permisos de lectura y ejecución allí de forma predeterminada.
Carga de inicio
Normalmente, en una aplicación de escritorio, la interfaz de usuario del generador de perfiles solicita una carga de inicio del archivo DLL del generador de perfiles inicializando un bloque de entorno que contiene las variables de entorno de la API de generación de perfiles de CLR necesarias (es decir, COR_PROFILER
, COR_ENABLE_PROFILING
y COR_PROFILER_PATH
) y, a continuación, crea un nuevo proceso con ese bloque de entorno. Lo mismo ocurre con las aplicaciones de la Tienda Windows, pero los mecanismos son diferentes.
No ejecución con privilegios elevados
Si el proceso A intenta generar el proceso B de la aplicación de la Tienda Windows, el proceso A se debe ejecutar en el nivel de integridad medio, no en el nivel de integridad alto (es decir, no con privilegios elevados). Esto significa que la interfaz de usuario del generador de perfiles debe ejecutarse en el nivel de integridad medio, o bien debe generar otro proceso de escritorio en el nivel de integridad medio para encargarse de iniciar la aplicación de la Tienda Windows.
Elección de una aplicación de la Tienda Windows para generar perfiles
En primer lugar, querrá preguntar al usuario del generador de perfiles qué aplicación de la Tienda Windows se va a iniciar. En el caso de las aplicaciones de escritorio, tal vez se mostraría un cuadro de diálogo para examinar archivos, y el usuario buscaría y seleccionaría un archivo .exe. Pero las aplicaciones de la Tienda Windows son diferentes, y usar un cuadro de diálogo para examinar archivos no tiene sentido. En su lugar, es mejor mostrar al usuario una lista de aplicaciones de la Tienda Windows instaladas para que ese usuario seleccione entre ellas.
Puede usar la clase PackageManager para generar esta lista. PackageManager
es una clase de Windows Runtime que está disponible para las aplicaciones de escritorio, y de hecho está solo disponible para las aplicaciones de escritorio.
El siguiente ejemplo de código de una hipotética interfaz de usuario de generador de perfiles escrita como una aplicación de escritorio en C# utiliza PackageManager
para generar una lista de aplicaciones de Windows:
string currentUserSID = WindowsIdentity.GetCurrent().User.ToString();
IAppxFactory appxFactory = (IAppxFactory) new AppxFactory();
PackageManager packageManager = new PackageManager();
IEnumerable<Package> packages = packageManager.FindPackagesForUser(currentUserSID);
Especificación del bloque de entorno personalizado
Una nueva interfaz COM, IPackageDebugSettings, permite personalizar el comportamiento de ejecución de una aplicación de la Tienda Windows para facilitar algunas formas de diagnóstico. Uno de sus métodos, EnableDebugging, permite pasar un bloque de entorno a la aplicación de la Tienda Windows cuando se inicia, junto con otros efectos útiles como la deshabilitación de la suspensión automática del proceso. El bloque de entorno es importante porque es donde necesita especificar las variables de entorno (COR_PROFILER
, COR_ENABLE_PROFILING
y COR_PROFILER_PATH)
) usadas por CLR para cargar el archivo DLL de Profiler.
Tenga en cuenta el fragmento de código siguiente:
IPackageDebugSettings pkgDebugSettings = new PackageDebugSettings();
pkgDebugSettings.EnableDebugging(packageFullName, debuggerCommandLine,
(IntPtr)fixedEnvironmentPzz);
Hay un par de elementos que tendrá que tener en cuenta:
packageFullName
se puede determinar al iterar sobre los paquetes y capturarpackage.Id.FullName
.debuggerCommandLine
es un poco más interesante. Para pasar el bloque de entorno personalizado a la aplicación de la Tienda Windows, debe escribir su propio depurador ficticio simplista. Windows genera la aplicación de la Tienda Windows suspendida y, a continuación, adjunta el depurador iniciándolo con una línea de comandos como en este ejemplo:MyDummyDebugger.exe -p 1336 -tid 1424
donde
-p 1336
significa que la aplicación de la Tienda Windows tiene el identificador de proceso 1336 y-tid 1424
significa que el identificador de subproceso 1424 es el subproceso suspendido. El depurador ficticio analizaría el identificador de subproceso desde la línea de comandos, reanudaría ese subproceso y, a continuación, se cerraría.Aquí hay un ejemplo de código C++ para hacer esto (¡asegúrese de agregar la comprobación de errores!):
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; }
Deberá implementar este depurador ficticio como parte de la instalación de la herramienta de diagnóstico y, a continuación, especificar la ruta de acceso a este depurador en el parámetro
debuggerCommandLine
.
Inicio de la aplicación de la Tienda Windows
Por fin ha llegado el momento de iniciar la aplicación de la Tienda Windows. Si ya ha intentado hacerlo por sus propios medios, es posible que haya observado que CreateProcess no es la forma de crear un proceso de aplicación de la Tienda Windows. En su lugar, deberá usar el método IApplicationActivationManager::ActivateApplication. Para ello, tendrá que obtener el identificador de modelo de usuario de la aplicación de la Tienda Windows que está iniciando. Y eso significa que tendrá que indagar un poco en el manifiesto.
Al recorrer en iteración los paquetes (consulte "Elección de una aplicación de la Tienda Windows para generar perfiles" en la sección Carga de inicio anterior), querrá obtener el conjunto de aplicaciones contenidas en el manifiesto del paquete actual:
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();
Sí, un paquete puede tener varias aplicaciones y cada aplicación tiene su propio identificador de modelo de usuario de aplicación. Por lo tanto, querrá preguntarle a su usuario de qué aplicación quiere obtener el perfil y conseguir el identificador del modelo de usuario de esa aplicación en particular:
while (appsEnum.GetHasCurrent() != 0)
{
IAppxManifestApplication app = appsEnum.GetCurrent();
string appUserModelId = app.GetAppUserModelId();
//...
}
Finalmente, ya tiene lo que necesita para iniciar la aplicación de la Tienda Windows:
IApplicationActivationManager appActivationMgr = new ApplicationActivationManager();
appActivationMgr.ActivateApplication(appUserModelId, appArgs, ACTIVATEOPTIONS.AO_NONE, out pid);
No olvide llamar a DisableDebugging
Cuando llamó a IPackageDebugSettings::EnableDebugging, hizo la promesa de que limpiaría llamando al método IPackageDebugSettings::DisableDebugging, así que asegúrese de hacerlo cuando termine la sesión de generación de perfiles.
Asociación de carga
Cuando la interfaz de usuario del generador de perfiles quiere adjuntar su archivo DLL de generador de perfiles a una aplicación que ya ha empezado a ejecutarse, usa ICLRProfiling::AttachProfiler. Lo mismo ocurre con las aplicaciones de la Tienda Windows. Pero además de las consideraciones comunes enumeradas anteriormente, asegúrese de que la aplicación de la Tienda Windows de destino no está suspendida.
EnableDebugging
Al igual que con la carga de inicio, llame al método IPackageDebugSettings::EnableDebugging. No lo necesita para pasar un bloque de entorno, pero sí una de sus otras características: deshabilitar la suspensión automática de procesos. De lo contrario, cuando la interfaz de usuario del generador de perfiles llama a AttachProfiler, se puede suspender la aplicación de la Tienda Windows de destino. De hecho, esto es probable si el usuario está interactuando ahora con la interfaz de usuario del generador de perfiles y la aplicación de la Tienda Windows no está activa en ninguna de las pantallas del usuario. Y si la aplicación de la Tienda Windows está suspendida, no podrá responder a ninguna señal que CLR le envíe para adjuntar su DLL del generador de perfiles.
Así que querrá hacer algo parecido a esto:
IPackageDebugSettings pkgDebugSettings = new PackageDebugSettings();
pkgDebugSettings.EnableDebugging(packageFullName, null /* debuggerCommandLine */,
IntPtr.Zero /* environment */);
Esta es la misma llamada que realizaría para el caso de carga de inicio, salvo que no especifica una línea de comandos del depurador ni un bloque de entorno.
DisableDebugging
Como siempre, no olvide llamar a IPackageDebugSettings::D isableDebugging cuando se complete la sesión de generación de perfiles.
Ejecución dentro de la aplicación de la Tienda Windows
Por lo tanto, la aplicación de la Tienda Windows ha cargado finalmente el archivo DLL del generador de perfiles. Ahora hay que enseñar a su DLL del generador de perfiles a jugar con las diferentes reglas que requieren las aplicaciones de la Tienda Windows, incluyendo qué API están permitidas y cómo ejecutarse con permisos reducidos.
Seguimiento de las API de la aplicación de la Tienda Windows
A medida que examine la API de Windows, observará que todas las API se documentan como aplicables a las aplicaciones de escritorio, las aplicaciones de la Tienda Windows o ambas. Por ejemplo, la sección Requisitos de la documentación de la función InitializeCriticalSectionAndSpinCount indica que la función solo se aplica a las aplicaciones de escritorio. En cambio, la función InitializeCriticalSectionEx está disponible tanto para aplicaciones de escritorio como para aplicaciones de la Tienda Windows.
Cuando desarrolle su DLL del generador de perfiles, trátela como si fuera una aplicación de la Tienda Windows y utilice únicamente las API que están documentadas como disponibles para las aplicaciones de la Tienda Windows. Analice sus dependencias (por ejemplo, puede ejecutar link /dump /imports
con su archivo DLL del generador de perfiles para auditar), y luego busque en los documentos para ver cuáles de sus dependencias están bien y cuáles no. En la mayoría de los casos, las infracciones se pueden corregir simplemente reemplazándolas por una forma más reciente de la API documentada como segura (por ejemplo, reemplazando InitializeCriticalSectionAndSpinCount por InitializeCriticalSectionEx).
Es posible que observe que su archivo DLL del generador de perfiles llama a algunas API que solo se aplican a las aplicaciones de escritorio y, sin embargo, parecen funcionar incluso cuando su DLL del generador de perfiles se carga dentro de una aplicación de la Tienda Windows. Tenga en cuenta que es arriesgado utilizar cualquier API que no esté documentada para su uso con aplicaciones de la Tienda Windows en su archivo DLL del generador de perfiles cuando se carga en un proceso de aplicación de la Tienda Windows:
No se garantiza que estas API funcionen cuando se les llama en el contexto único en el que se ejecutan las aplicaciones de la Tienda Windows.
Estas API podrían no funcionar de forma coherente cuando se les llama desde diferentes procesos de aplicación de la Tienda Windows.
Puede parecer que estas API funcionan correctamente desde aplicaciones de la Tienda Windows en la versión actual de Windows, pero pueden interrumpirse o deshabilitarse en futuras versiones de Windows.
El mejor consejo es corregir todas sus infracciones y evitar el riesgo.
Es posible que descubra que no puede prescindir en absoluto de una determinada API y que no pueda encontrar un sustituto adecuado para las aplicaciones de la Tienda Windows. En tal caso, como mínimo:
Pruebe, pruebe, pruebe hasta la saciedad el uso de esa API.
Comprenda que la API podría interrumpirse o desaparecer repentinamente si se le llama desde dentro de las aplicaciones de la Tienda Windows en futuras versiones de Windows. Microsoft no lo considerará un problema de compatibilidad y no será una prioridad admitir su uso.
Permisos reducidos
Está fuera del ámbito de este tema enumerar todas las formas en que los permisos de las aplicaciones de la Tienda Windows difieren de las aplicaciones de escritorio. Sin embargo, el comportamiento será diferente cada vez que el archivo DLL del generador de perfiles (cuando se carga en una aplicación de la Tienda Windows en comparación con una aplicación de escritorio) intenta acceder a los recursos. El sistema de archivos es el ejemplo más común. Solo hay unos pocos lugares en el disco a los que una determinada aplicación de la Tienda Windows puede acceder (consulte Acceso a archivos y permisos (aplicaciones de Windows Runtime), y su DLL del generador de perfiles estará bajo las mismas restricciones. Pruebe el código exhaustivamente.
Comunicación entre procesos
Como se muestra en el diagrama al principio de este documento, es probable que el archivo DLL del generador de perfiles (cargado en el espacio de proceso de la aplicación de la Tienda Windows) tenga que comunicarse con la interfaz de usuario del generador de perfiles (que se ejecuta en un espacio de proceso de aplicación de escritorio independiente) a través de su propio canal de comunicación entre procesos (IPC) personalizado. La interfaz de usuario del generador de perfiles envía señales al archivo DLL del generador de perfiles para modificar su comportamiento, y el archivo DLL del generador de perfiles envía datos de la aplicación de la Tienda Windows analizada de nuevo a la interfaz de usuario del generador de perfiles para su posterior procesamiento y presentación al usuario.
La mayoría de los generadores de perfiles deben funcionar de esta manera, pero las opciones para los mecanismos de IPC son más limitadas cuando el archivo DLL del generador de perfiles se carga en una aplicación de la Tienda Windows. Por ejemplo, las canalizaciones con nombre no forman parte del SDK de la aplicación de la Tienda Windows, por lo que no se pueden usar.
Pero, por supuesto, los archivos siguen estando presentes, aunque de forma más limitada. También hay eventos disponibles.
Comunicación a través de archivos
La mayoría de los datos probablemente pasarán entre el archivo DLL del generador de perfiles y la interfaz de usuario del generador de perfiles a través de archivos. La clave es elegir una ubicación de archivo a la que el archivo DLL del generador de perfiles (en el contexto de una aplicación de la Tienda Windows) y la interfaz de usuario del generador de perfiles tengan acceso de lectura y escritura. Por ejemplo, la ruta de acceso de la carpeta temporal es una ubicación a la que puede acceder tanto el archivo DLL como la interfaz de usuario del generador de perfiles, pero a la que ningún otro paquete de la aplicación de la Tienda Windows puede acceder (lo que protege cualquier información que registre desde otros paquetes de aplicaciones de la Tienda Windows).
Tanto la interfaz de usuario como el archivo DLL del generador de perfiles pueden determinar esta ruta de acceso de forma independiente. La interfaz de usuario del generador de perfiles, cuando recorre en iteración todos los paquetes instalados para el usuario actual (vea el código de ejemplo anterior), obtiene acceso a la clase PackageId
, de la cual se puede derivar la ruta de acceso de la carpeta temporal con código similar a este fragmento de código. (Como siempre, la comprobación de errores se omite para mayor brevedad).
// C# code for the Profiler UI.
ApplicationData appData =
ApplicationDataManager.CreateForPackageFamily(
packageId.FamilyName);
tempDir = appData.TemporaryFolder.Path;
Mientras tanto, el archivo DLL del generador de perfiles puede hacer básicamente lo mismo, aunque puede llegar más fácilmente a la clase ApplicationData mediante la propiedad ApplicationData.Current.
Comunicación a través de eventos
Si quiere una semántica de señalización simple entre la IU y el archivo DLL del generador de perfiles, puede usar eventos dentro de las aplicaciones de la Tienda Windows, así como de las aplicaciones de escritorio.
Desde el archivo DLL del generador de perfiles, simplemente puede llamar a la función CreateEventEx para crear un evento con nombre con cualquier nombre que quiera. Por ejemplo:
// 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);
A continuación, la interfaz de usuario del generador de perfiles debe encontrar ese evento con nombre en el espacio de nombres de la aplicación de la Tienda Windows. Por ejemplo, la interfaz de usuario del generador de perfiles podría llamar a CreateEventEx, especificando el nombre del evento como
AppContainerNamedObjects\<acSid>\MyNamedEvent
<acSid>
es el SID de AppContainer de la aplicación de la Tienda Windows. En una sección anterior de este tema se mostró cómo iterar los paquetes instalados para el usuario actual. A partir de ese código de ejemplo, puede obtener el identificador del paquete. Y a partir del identificador del paquete, puede obtener el <acSid>
con código similar al siguiente:
IntPtr acPSID;
DeriveAppContainerSidFromAppContainerName(packageId.FamilyName, out acPSID);
string acSid;
ConvertSidToStringSid(acPSID, out acSid);
string acDir;
GetAppContainerFolderPath(acSid, out acDir);
Sin notificaciones de apagado
Cuando se ejecuta dentro de una aplicación de la Tienda Windows, el archivo DLL del generador de perfiles no debe depender de ICorProfilerCallback::Shutdown o incluso dllMain (con DLL_PROCESS_DETACH
) al que se llama para notificar al archivo DLL del generador de perfiles que la aplicación de la Tienda Windows se está cerrando. De hecho, debe esperar que nunca se les llame. Históricamente, muchos archivos DLL del generador de perfiles han usado esas notificaciones como lugares prácticos donde vaciar las memorias caché en el disco, cerrar archivos, enviar notificaciones de vuelta a la interfaz de usuario del generador de perfiles, etc. Pero ahora el archivo DLL del generador de perfiles debe organizarse de forma un poco diferente.
El archivo DLL del generador de perfiles debe registrar la información a medida que avanza. Por motivos de rendimiento, es posible que desee procesar por lotes la información en memoria y vaciarla en el disco a medida que el lote crece en tamaño más allá de algún umbral. Pero supongamos que se puede perder toda la información que aún no se ha vaciado en el disco. Esto significa que tendrá que elegir el umbral pensándolo bien y que la interfaz de usuario del generador de perfiles debe protegerse para tratar la información incompleta escrita por el archivo DLL del generador de perfiles.
Archivos de metadatos de Windows Runtime
Está fuera del ámbito de este documento entrar en detalles sobre lo que son los archivos de metadatos de Windows Runtime (WinMD). Esta sección se limita a tratar la forma en que la API de generación de perfiles de CLR reacciona cuando los archivos WinMD se cargan por la aplicación de la Tienda Windows que su archivo DLL de generación de perfiles está analizando.
WinMD administrados y no administrados
Si un desarrollador usa Visual Studio para crear un nuevo proyecto de componentes de Windows Runtime, una compilación de ese proyecto genera un archivo WinMD que describe los metadatos (las descripciones de tipo de clases, interfaces, etc.) creados por el desarrollador. Si este proyecto es un proyecto de lenguaje administrado escrito en C# o Visual Basic, ese mismo archivo WinMD también contiene la implementación de esos tipos (lo que significa que contiene todo el IL compilado a partir del código fuente del desarrollador). Estos archivos se conocen como archivos WinMD administrados. Son interesantes en tanto que contienen metadatos Windows Runtime y la implementación subyacente.
Por el contrario, si un desarrollador crea un proyecto de componentes de Windows Runtime para C++, una compilación de ese proyecto genera un archivo WinMD que solo contiene metadatos y la implementación se compila en un archivo DLL nativo independiente. Del mismo modo, los archivos WinMD que se incluyen en Windows SDK solo contienen metadatos, con la implementación compilada en archivos DLL nativos independientes que se envían como parte de Windows.
La información siguiente se aplica a los winMD administrados, que contienen metadatos e implementación, y a winMD no administrados, que solo contienen metadatos.
Los archivos WinMD parecen módulos CLR
En lo que respecta a CLR, todos los archivos WinMD son módulos. La API de generación de perfiles de CLR, por lo tanto, indica al archivo DLL del generador de perfiles cuándo se cargan los archivos WinMD y cuáles son sus identificadores de módulo, de la misma manera que para otros módulos administrados.
El archivo DLL del generador de perfiles puede distinguir los archivos WinMD de otros módulos llamando al método ICorProfilerInfo3::GetModuleInfo2 e inspeccionando el parámetro de salida pdwModuleFlags
para la marca COR_PRF_MODULE_WINDOWS_RUNTIME. (Se establece si y solo si el identificador de módulo representa un WinMD).
Lectura de metadatos de WinMD
Los archivos WinMD, como los módulos normales, contienen metadatos que se pueden leer a través de las API de metadatos. Sin embargo, CLR asigna tipos de Windows Runtime a tipos de .NET Framework cuando lee los archivos WinMD para que los desarrolladores que programan en código administrado y consumen el archivo WinMD puedan tener una experiencia de programación más natural. Para obtener algunos ejemplos de estas asignaciones, consulte Compatibilidad de .NET Framework con las aplicaciones de la Tienda Windows y Windows Runtime.
Entonces, ¿qué vista obtendrá su generador de perfiles cuando utilice las API de metadatos: la vista sin procesar de Windows Runtime o la vista asignada de .NET Framework? La respuesta: depende de usted.
Si llama al método ICorProfilerInfo::GetModuleMetaData en un archivo WinMD para obtener una interfaz de metadatos, como IMetaDataImport, puede elegir establecer ofNoTransform en el parámetro dwOpenFlags
para desactivar esta asignación. De lo contrario, de forma predeterminada, se habilitará la asignación. Normalmente, un generador de perfiles mantendrá habilitada la asignación, por lo que las cadenas que el archivo DLL del generador de perfiles obtiene de los metadatos de WinMD (por ejemplo, nombres de tipos) serán familiares y naturales para el usuario del generador de perfiles.
Modificación de metadatos de WinMD
No se admite la modificación de metadatos en WinMD. Si llama al método ICorProfilerInfo::GetModuleMetaData para un archivo WinMD y especifica ofWrite en el parámetro dwOpenFlags
o pide una interfaz de metadatos de escritura como IMetaDataEmit, GetModuleMetaData producirá un error. Esto es de especial importancia para los generadores de perfiles de reescritura de IL, que necesitan modificar los metadatos para admitir su instrumentación (por ejemplo, para agregar AssemblyRefs o nuevos métodos). Por lo tanto, debería comprobar primero COR_PRF_MODULE_WINDOWS_RUNTIME (como se ha comentado en la sección anterior) y abstenerse de solicitar interfaces de metadatos que se puedan escribir en dichos módulos.
Resolución de referencias de ensamblado con WinMD
Muchos generadores de perfiles deben resolver manualmente las referencias de metadatos para ayudar con la instrumentación o la inspección de tipos. Estos generadores de perfiles deben tener en cuenta cómo CLR resuelve las referencias de ensamblado que apuntan a los archivos WinMD, ya que esas referencias se resuelven de una manera completamente diferente a las referencias de ensamblado estándar.
Generadores de perfiles de memoria
El recolector de elementos no utilizados y el montón administrado no son fundamentalmente diferentes en una aplicación de la Tienda Windows y una aplicación de escritorio. Sin embargo, hay algunas diferencias sutiles que los autores de generadores de perfiles deben tener en cuenta.
ForceGC crea un subproceso administrado
Al realizar la generación de perfiles de memoria, el archivo DLL del generador de perfiles suele crear un subproceso separado desde el que llamar al método ForceGC. Esto no es nada nuevo. Pero lo que puede resultar sorprendente es que el acto de hacer una recolección de elementos no utilizados dentro de una aplicación de la Tienda Windows puede transformar el subproceso en un subproceso administrado (por ejemplo, se creará un identificador de subproceso de API de generación de perfiles para ese subproceso).
Para comprender las consecuencias de esto, es importante comprender las diferencias entre las llamadas sincrónicas y asincrónicas definidas por la API de generación de perfiles de CLR. Tenga en cuenta que esto es muy diferente del concepto de llamadas asincrónicas en aplicaciones de la Tienda Windows. Consulte la entrada de blog Por qué tenemos CORPROF_E_UNSUPPORTED_CALL_SEQUENCE para obtener más información.
El punto pertinente es que las llamadas realizadas en subprocesos creados por el generador de perfiles siempre se consideran sincrónicas, incluso si esas llamadas se realizan desde fuera de una implementación de uno de los métodos ICorProfilerCallback del archivo DLL del generador de perfiles. Al menos, así era antes. Ahora que CLR ha convertido el subproceso del generador de perfiles en un subproceso administrado debido a la llamada al método ForceGC, ese subproceso ya no se considera el subproceso del generador de perfiles. Como tal, CLR impone una definición más estricta de lo que se califica como sincrónico para ese subproceso, esto es, que una llamada debe originarse desde dentro de uno de los métodos ICorProfilerCallback de su archivo DLL del generador de perfiles para calificarse como sincrónica.
¿Qué significa esto en la práctica? La mayoría de los métodos ICorProfilerInfo solo son seguros para llamarse de forma sincrónica y se producirá un error inmediatamente en caso contrario. Por lo tanto, si el archivo DLL del generador de perfiles reutiliza el subproceso del método ForceGC para otras llamadas realizadas normalmente en subprocesos creados por el generador de perfiles (por ejemplo, a RequestProfilerDetach, RequestReJIT o RequestRevert), tendrá problemas. Incluso una función asíncrona segura como DoStackSnapshot tiene reglas especiales cuando se llama desde subprocesos administrados. (Consulte la entrada de blog Recorrido de la pila del generador de perfiles: aspectos básicos y posteriores para obtener más información).
Por lo tanto, se recomienda que cualquier subproceso que cree el archivo DLL del generador de perfiles para llamar al método ForceGCsolo se debe usar con el fin de desencadenar las recolecciones de elementos no utilizados y, a continuación, responder a las devoluciones de llamada de la mencionada recolección. No debe llamar a la API de generación de perfiles para realizar otras tareas, como muestreo de pila o desasociación.
ConditionalWeakTableReferences
A partir de .NET Framework 4.5, hay una nueva devolución de llamada de recolección de elementos no utilizados, ConditionalWeakTableElementReferences, que proporciona al generador de perfiles información más completa sobre los manipuladores dependientes. Estos manipuladores agregan eficazmente una referencia de un objeto de origen a un objeto de destino con el fin de administrar el tiempo de vida de la recolección de elementos no utilizados. Los manipuladores dependientes no son nada nuevo, y los desarrolladores que programan en código administrado han podido crear sus propios manipuladores dependientes utilizando la clase System.Runtime.CompilerServices.ConditionalWeakTable<TKey,TValue> incluso antes de Windows 8 y de .NET Framework 4.5.
Sin embargo, las aplicaciones XAML administradas de la Tienda Windows ahora hacen un uso intensivo de los manipuladores dependientes. En concreto, CLR los usa para ayudar a administrar ciclos de referencia entre objetos administrados y objetos Windows Runtime no administrados. Esto significa que es más importante ahora que nunca que los generadores de perfiles de memoria se informen de estos manipuladores dependientes para que se puedan visualizar junto con el resto de los bordes del grafo del montón. El archivo DLL del generador de perfiles debe usar RootReferences2, ObjectReferences y ConditionalWeakTableElementReferences en conjunto para formar una vista completa del grafo del montón.
Conclusión
Es posible usar la API de generación de perfiles de CLR para analizar el código administrado que se ejecuta dentro de las aplicaciones de la Tienda Windows. De hecho, puede tomar un generador de perfiles existente que esté desarrollando y realizar algunos cambios específicos para que pueda dirigirse a aplicaciones de la Tienda Windows. La interfaz de usuario del generador de perfiles debe usar las nuevas API para activar la aplicación de la Tienda Windows en modo de depuración. Asegúrese de que el archivo DLL del generador de perfiles consume solo las API aplicables a las aplicaciones de la Tienda Windows. El mecanismo de comunicación entre la DLL y la interfaz de usuario del generador de perfiles debe escribirse teniendo en cuenta las restricciones de la API de la aplicación de la Tienda Windows y conociendo los permisos restringidos implementados para las aplicaciones de la Tienda Windows. El archivo DLL del generador de perfiles debe tener en cuenta cómo CLR trata los WinMD y en qué medida el comportamiento del recolector de elementos no utilizados es diferente con respecto a los subprocesos administrados.
Recursos
Common Language Runtime
Interacción de CLR con Windows Runtime
Aplicaciones de la Tienda Windows