Nota
L'accés a aquesta pàgina requereix autorització. Pots provar d'iniciar sessió o canviar de directori.
L'accés a aquesta pàgina requereix autorització. Pots provar de canviar directoris.
El formato de archivo de los archivos de manifiesto toma prestado tanto de C++ como sea posible. Como resultado, es bastante fácil tomar un archivo de encabezado normal del SDK de C++ y modificarlo para que sea un archivo de manifiesto. El analizador es totalmente compatible con los comentarios de estilo de C y C++ para ayudarle a organizar y documentar el archivo.
Si está intentando agregar un archivo de manifiesto o realizar modificaciones en un archivo existente, la mejor manera de hacerlo es simplemente experimentar. Al emitir un comando !logexts.logi o !logexts.loge en el depurador, Logger intentará analizar los archivos de manifiesto. Si encuentra un problema, generará un mensaje de error que podría indicar el error.
Un archivo de manifiesto se compone de los siguientes elementos básicos: etiquetas de módulo, etiquetas de categoría, declaraciones de función, definiciones de interfaz COM y definiciones de tipo. También existen otros tipos de elementos, pero son los más importantes.
Etiquetas de módulo
Una etiqueta de módulo simplemente declara qué DLL exporta las funciones que se declaran a partir de entonces. Por ejemplo, si el archivo de manifiesto es para registrar un grupo de funciones de Comctl32.dll, incluiría la siguiente etiqueta de módulo antes de declarar los prototipos de función:
module COMCTL32.DLL:
Una etiqueta de módulo debe aparecer antes de cualquier declaración de función en un archivo de manifiesto. Un archivo de manifiesto puede contener cualquier número de etiquetas de módulo.
Etiquetas de categoría
De forma similar a una etiqueta de módulo, una etiqueta de categoría identifica a qué "categoría" pertenecen todas las funciones y/o interfaces COM posteriores. Por ejemplo, si va a crear un archivo de manifiesto de Comctl32.dll, puede usar lo siguiente como etiqueta de categoría:
category CommonControls:
Un archivo de manifiesto puede contener cualquier número de etiquetas de categoría.
Declaraciones de función
Una declaración de función es lo que realmente pide a Logger que registre algo. Es casi idéntico a un prototipo de función que se encuentra en un archivo de encabezado de C/C++. Hay algunas adiciones notables al formato, que se pueden ilustrar mejor mediante el ejemplo siguiente:
HANDLE [gle] FindFirstFileA(
LPCSTR lpFileName,
[out] LPWIN32_FIND_DATAA lpFindFileData);
La función FindFirstFileA toma dos parámetros. El primero es lpFileName, que es una ruta de acceso completa (normalmente con caracteres comodín) que define dónde buscar un archivo o archivos. El segundo es un puntero a una estructura WIN32_FIND_DATAA que se usará para contener los resultados de la búsqueda. El HANDLE devuelto se usa para futuras llamadas a FindNextFileA. Si FindFirstFileA devuelve INVALID_HANDLE_VALUE, se produce un error en la llamada a la función y se puede adquirir un código de error mediante una llamada a la función GetLastError .
El tipo HANDLE se declara de la siguiente manera:
value DWORD HANDLE
{
#define NULL 0 [fail]
#define INVALID_HANDLE_VALUE -1 [fail]
};
Si el valor devuelto por esta función es 0 o -1 (0xFFFFFFFF), logger asume que se produjo un error en la función, ya que estos valores tienen un modificador [fail] en la declaración de valor. (Vea la sección Tipos de valor más adelante en esta sección). Dado que hay un modificador [gle] justo antes del nombre de la función, Logger reconoce que esta función usa GetLastError para devolver códigos de error, por lo que captura el código de error y lo registra en el archivo de registro.
El modificador [out] del parámetro lpFindFileData informa a Logger de que la función rellena la estructura de datos y se debe registrar cuando la función devuelve.
Definiciones de interfaz COM
Una interfaz COM es básicamente un vector de funciones a las que puede llamar el cliente de un objeto COM. El formato de manifiesto toma prestado en gran medida el lenguaje de definición de interfaz (IDL) usado en COM para definir interfaces.
Considere el ejemplo siguiente:
interface IDispatch : IUnknown
{
HRESULT GetTypeInfoCount( UINT pctinfo );
HRESULT GetTypeInfo(
UINT iTInfo,
LCID lcid,
LPVOID ppTInfo );
HRESULT GetIDsOfNames(
REFIID riid,
LPOLECHAR* rgszNames,
UINT cNames,
LCID lcid,
[out] DISPID* rgDispId );
HRESULT Invoke(
DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pDispParams,
VARIANT* pVarResult,
EXCEPINFO* pExcepInfo,
UINT* puArgErr );
};
Esto declara una interfaz denominada IDispatch que se deriva de IUnknown. Contiene cuatro funciones miembro, que se declaran en orden específico dentro de las llaves de la interfaz. El registrador interceptará y registrará estas funciones miembro reemplazando los punteros de función en la tabla virtual de la interfaz (el vector binario real de punteros de función usados en tiempo de ejecución) por sus propios. Consulte la sección Tipos de COM_INTERFACE_PTR más adelante en esta sección para obtener más información sobre cómo el registrador captura las interfaces a medida que se entregan.
Definiciones de tipos
Definir tipos de datos es la parte más importante (y más tediosa) del desarrollo de archivos de manifiesto. El lenguaje de manifiesto permite definir etiquetas legibles para valores numéricos que se pasan o devuelven desde una función.
Por ejemplo, Winerror.h define un tipo denominado "WinError" que es una lista de valores de error devueltos por la mayoría de las funciones win32 de Microsoft y sus etiquetas legibles humanas correspondientes. Esto permite al registrador y LogViewer reemplazar los códigos de error noformantes por texto significativo.
También puede etiquetar bits individuales dentro de una máscara de bits para permitir que logger y LogViewer interrumpan una máscara de bits DWORD en sus componentes.
El manifiesto admite 13 tipos básicos. Se muestran en la tabla siguiente.
| Tipo | Length | Ejemplo de visualización |
|---|---|---|
Puntero |
4 bytes |
0x001AF320 |
VACÍO |
0 bytes |
|
BYTE |
1 byte |
0x32 |
WORD |
2 bytes |
0x0A23 |
DWORD |
4 bytes |
-234323 |
BOOL |
1 byte |
TRUE |
LPSTR |
Byte de longitud más cualquier número de caracteres |
"Zorro marrón rápido" |
LPWSTR |
Byte de longitud más cualquier número de caracteres Unicode |
"Saltó sobre el perro diferido" |
GUID |
16 bytes |
{0CF774D0-F077-11D1-B1BC-00C04F86C324} |
COM_INTERFACE_PTR |
4 bytes |
0x0203404A |
value |
Dependiente del tipo base |
ERROR_TOO_MANY_OPEN_FILES |
mask |
Dependiente del tipo base |
WS_MAXIMIZED | WS_ALWAYSONTOP |
struct |
Depende del tamaño de los tipos encapsulados |
+ lpRect nLeft 34 nRight 54 nTop 100 nBottom 300 |
Las definiciones de tipo en los archivos de manifiesto funcionan como definiciones de tipo de C/C++. Por ejemplo, la siguiente instrucción define PLONG como puntero a un long:
typedef LONG *PLONG;
La mayoría de las definiciones de tipo básicas ya se han declarado en Main.h. Solo debe tener que agregar definiciones de tipo específicas del componente. Las definiciones de estructura tienen el mismo formato que los tipos de estructura de C/C++.
Hay cuatro tipos especiales: value, mask, GUID y COM_INTERFACE_PTR.
Tipos de valor
Un valor es un tipo básico que se divide en etiquetas legibles por personas. La mayoría de la documentación de la función solo hace referencia al valor #define de una constante determinada usada en una función. Por ejemplo, la mayoría de los programadores no saben cuál es el valor real para todos los códigos devueltos por GetLastError, lo que hace que no sea útil ver un valor numérico críptico en LogViewer. El valor del manifiesto supera esto al permitir declaraciones de valor como en el ejemplo siguiente:
value LONG ChangeNotifyFlags
{
#define SHCNF_IDLIST 0x0000 // LPITEMIDLIST
#define SHCNF_PATHA 0x0001 // path name
#define SHCNF_PRINTERA 0x0002 // printer friendly name
#define SHCNF_DWORD 0x0003 // DWORD
#define SHCNF_PATHW 0x0005 // path name
#define SHCNF_PRINTERW 0x0006 // printer friendly name
};
Esto declara un nuevo tipo denominado "ChangeNotifyFlags" derivado de LONG. Si se usa como parámetro de función, se mostrarán los alias legibles en lugar de los números sin formato.
Tipos de máscara
De forma similar a los tipos de valor, un tipo de máscara es un tipo básico (normalmente un DWORD) que se divide en etiquetas legibles para cada uno de los bits que tienen significado. Considere el ejemplo siguiente:
mask DWORD DirectDrawOptSurfaceDescCapsFlags
{
#define DDOSDCAPS_OPTCOMPRESSED 0x00000001
#define DDOSDCAPS_OPTREORDERED 0x00000002
#define DDOSDCAPS_MONOLITHICMIPMAP 0x00000004
};
Esto declara un nuevo tipo derivado de DWORD que, si se usa como parámetro de función, tendrá los valores individuales separados para el usuario en LogViewer. Por lo tanto, si el valor es 0x00000005, LogViewer mostrará:
DDOSDCAPS_OPTCOMPRESSED | DDOSDCAPS_MONOLITHICMIPMAP
Tipos GUID
Los GUID son identificadores únicos globales de 16 bytes que se usan ampliamente en COM. Se declaran de dos maneras:
struct __declspec(uuid("00020400-0000-0000-C000-000000000046")) IDispatch;
o
class __declspec(uuid("11219420-1768-11D1-95BE-00609797EA4F")) ShellLinkObject;
El primer método se usa para declarar un identificador de interfaz (IID). Cuando lo muestra LogViewer, "IID_" se anexa al principio del nombre para mostrar. El segundo método se usa para declarar un identificador de clase (CLSID). LogViewer anexa "CLSID_" al principio del nombre para mostrar.
Si un tipo GUID es un parámetro para una función, LogViewer comparará el valor con todos los IID y CLSID declarados. Si se encuentra una coincidencia, se mostrará el nombre descriptivo de IID. Si no es así, mostrará el valor de carácter 32-hexadecimal en notación GUID estándar.
tipos de COM_INTERFACE_PTR
El tipo COM_INTERFACE_PTR es el tipo base de un puntero de interfaz COM. Cuando se declara una interfaz COM, se define realmente un nuevo tipo que se deriva de COM_INTERFACE_PTR. Por lo tanto, un puntero a este tipo puede ser un parámetro para una función. Si un tipo básico de COM_INTERFACE_PTR se declara como un parámetro OUT en una función y hay un parámetro independiente que tiene una etiqueta [iid], Logger comparará el valor de IID pasado con todos los GUID declarados. Si hay una coincidencia y se declaró una interfaz COM que tiene el mismo nombre que el IID, Logger enlazará todas las funciones de esa interfaz y las registrará.
Este es un ejemplo:
STDAPI CoCreateInstance(
REFCLSID rclsid, //Class identifier (CLSID) of the object
LPUNKNOWN pUnkOuter, //Pointer to controlling IUnknown
CLSCTX dwClsContext, //Context for running executable code
[iid] REFIID riid, //Reference to the identifier of the interface
[out] COM_INTERFACE_PTR * ppv
//Address of output variable that receives
//the interface pointer requested in riid
);
En este ejemplo, riid tiene un modificador [iid]. Esto indica a Logger que el puntero devuelto en ppv es un puntero de interfaz COM para la interfaz identificada por riid.
También es posible declarar una función de la siguiente manera:
DDRESULT DirectDrawCreateClipper( DWORD dwFlags, [out] LPDIRECTDRAWCLIPPER *lplpDDClipper, IUnknown *pUnkOuter );
En este ejemplo, LPDIRECTDRAWCLIPPER se define como un puntero a la interfaz IDirectDrawClipper . Puesto que logger puede identificar qué tipo de interfaz se devuelve en el parámetro lplpDDClipper , no es necesario un modificador [iid] en ninguno de los demás parámetros.