Delen via


Manifestbestandsformaat

De bestandsindeling voor de manifestbestanden leent zoveel mogelijk uit C++ en IDL. Als gevolg hiervan is het vrij eenvoudig om een normaal C++ SDK-headerbestand te nemen en te wijzigen in een manifestbestand. De parser ondersteunt C- en C++-stijlopmerkingen volledig om het bestand te ordenen en te documenteren.

Als u probeert een manifestbestand toe te voegen of wijzigingen aan te brengen in een bestaand bestand, kunt u het beste experimenteren. Wanneer u een !logexts.logi - of !logexts.loge-opdracht in het foutopsporingsprogramma geeft, probeert Logger de manifestbestanden te parseren. Als er een probleem optreedt, wordt er een foutbericht weergegeven dat de fout kan aangeven.

Een manifestbestand bestaat uit de volgende basiselementen: modulelabels, categorielabels, functiedeclaraties, COM-interfacedefinities en typedefinities. Er bestaan ook andere typen elementen, maar dit zijn de belangrijkste.

Module labels

Een modulelabel declareert eenvoudigweg welke DLL de functies exporteert die hierna worden gedeclareerd. Als uw manifestbestand bijvoorbeeld is bedoeld voor het vastleggen van een groep functies uit Comctl32.dll, moet u het volgende modulelabel opnemen voordat u prototypen van functies declareren:

module COMCTL32.DLL:

Er moet een modulelabel worden weergegeven vóór functiedeclaraties in een manifestbestand. Een manifestbestand kan een willekeurig aantal modulelabels bevatten.

Categorielabels

Net als bij een modulelabel identificeert een categorielabel tot welke 'categorie' alle volgende functies en/of COM-interfaces behoren. Als u bijvoorbeeld een Comctl32.dll manifestbestand maakt, kunt u het volgende gebruiken als categorielabel:

category CommonControls:

Een manifestbestand kan een willekeurig aantal categorielabels bevatten.

Functiedeclaraties

Een functiedeclaratie is wat Logger ertoe aanzet om iets te registreren. Het is bijna identiek aan een functieprototype dat is gevonden in een C/C++-headerbestand. Er zijn enkele belangrijke toevoegingen aan de indeling, die het beste kunnen worden geïllustreerd door het volgende voorbeeld:

HANDLE [gle] FindFirstFileA(
       LPCSTR lpFileName,
       [out] LPWIN32_FIND_DATAA lpFindFileData);

De functie FindFirstFileA heeft twee parameters. De eerste is lpFileName, een volledig pad (meestal met jokertekens) waarmee wordt gedefinieerd waar een bestand of bestanden moeten worden gezocht. De tweede is een aanwijzer naar een WIN32_FIND_DATAA structuur die wordt gebruikt om de zoekresultaten te bevatten. De geretourneerde HANDLE wordt gebruikt voor toekomstige aanroepen naar FindNextFileA. Als FindFirstFileA INVALID_HANDLE_VALUE retourneert, is de functieaanroep mislukt en kan er een foutcode worden aangeschaft door de functie GetLastError aan te roepen.

Het HANDLE-type wordt als volgt gedeclareerd:

value DWORD HANDLE
{
#define NULL                       0 [fail]
#define INVALID_HANDLE_VALUE      -1 [fail]
};

Als de waarde die door deze functie wordt geretourneerd 0 of -1 (0xFFFFFFFF) is, gaat Logger ervan uit dat de functie is mislukt, omdat dergelijke waarden een [fail]-modifier hebben in de waardedeclaratie. (Zie de sectie Waardetypen verderop in deze sectie.) Omdat er een [gle]-wijzigingsfunctie is vlak voor de naam van de functie, herkent Logger dat deze functie GetLastError gebruikt om foutcodes te retourneren, zodat de foutcode wordt vastgelegd en in het logboekbestand wordt vastgelegd.

De modifier [out] op de parameter lpFindFileData informeert Logger dat de gegevensstructuur wordt ingevuld door de functie en moet worden geregistreerd wanneer de functie wordt geretourneerd.

COM-interfacedefinities

Een COM-interface is in feite een vector van functies die kunnen worden aangeroepen door de client van een COM-object. De manifestindeling wordt sterk geleend van de Interface Definition Language (IDL) die in COM wordt gebruikt om interfaces te definiëren.

Bekijk het volgende voorbeeld:

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

Hiermee declareert u een interface met de naam IDispatch die is afgeleid van IUnknown. Het bevat vier lidfuncties, die in specifieke volgorde binnen de accolades van de interface worden gedeclareerd. Logger onderschept deze lidfuncties en logt ze door de functie-aanwijzers in de v-tabel van de interface (de werkelijke binaire vector van functie-aanwijzers die tijdens runtime worden gebruikt) te vervangen met zijn eigen. Zie de sectie COM_INTERFACE_PTR Typen verderop in deze sectie voor meer informatie over hoe Logger interfaces vastlegt terwijl ze worden uitgedeeld.

Typedefinities

Het definiëren van gegevenstypen is het belangrijkste (en meest tijdrovende) deel van de ontwikkeling van manifestbestanden. Met de manifesttaal kunt u leesbare labels definiëren voor numerieke waarden die worden doorgegeven of geretourneerd vanuit een functie.

Winerror.h definieert bijvoorbeeld een type met de naam 'WinError' dat een lijst is met foutwaarden die worden geretourneerd door de meeste Microsoft Win32-functies en de bijbehorende door mensen leesbare labels. Hierdoor kunnen Logger en LogViewer niet-geïnformeerde foutcodes vervangen door zinvolle tekst.

U kunt ook afzonderlijke bits binnen een bitmasker labelen om Logger en LogViewer toe te staan een DWORD-bitmasker in de onderdelen op te splitsen.

Er worden 13 basistypen ondersteund door het manifest. Deze worden weergegeven in de volgende tabel.

Typ Lengte Voorbeeld van weergave

Aanwijzer

4 bytes

0x001AF320

ONGELDIG

0 bytes

BYTE

1 byte

0x32

WOORD

2 bytes

0x0A23

DWORD

4 bytes

-234323

BOOL

1 byte

WAAR

LPSTR

Lengte byte plus een willekeurig aantal tekens

"Snelle bruine vos"

LPWSTR

Lengte byte plus een willekeurig aantal Unicode-tekens

Sprong over de luie hond

GUID (globaal unieke identificator)

16 bytes

{0CF774D0-F077-11D1-B1BC-00C04F86C324}

COM_INTERFACE_PTR

4 bytes

0x0203404A

waarde

Afhankelijk van het basistype

ERROR_TOO_MANY_OPEN_FILES

masker

Afhankelijk van het basistype

WS_MAXIMIZED | WS_ALWAYSONTOP

struct

Afhankelijk van de grootte van ingekapselde typen

+ lpRect nLeft 34 nRight 54 nTop 100 nBottom 300

Typedefinities in manifestbestanden werken als C/C++ typedefs. De volgende instructie definieert bijvoorbeeld PLONG als een aanwijzer naar een LONG:

typedef LONG *PLONG;

De meeste eenvoudige typedefs zijn al gedeclareerd in Main.h. U moet alleen typedefs toevoegen die specifiek zijn voor uw onderdeel. Structuurdefinities hebben dezelfde indeling als C/C++-structtypen.

Er zijn vier speciale typen: waarde, masker, GUID en COM_INTERFACE_PTR.

Waardetypen
Een waarde is een basistype dat is onderverdeeld in door mensen leesbare labels. De meeste functiedocumentatie verwijst alleen naar de #define waarde van een bepaalde constante die in een functie wordt gebruikt. De meeste programmeurs weten bijvoorbeeld niet wat de werkelijke waarde is voor alle codes die worden geretourneerd door GetLastError, waardoor het niet nuttig is om een cryptische numerieke waarde te zien in LogViewer. De manifestwaarde overkomt dit door waardedeclaraties zoals in het volgende voorbeeld toe te staan:

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

Hiermee declareert u een nieuw type met de naam ChangeNotifyFlags dat is afgeleid van LONG. Als dit wordt gebruikt als functieparameter, worden de door mensen leesbare aliassen weergegeven in plaats van de onbewerkte getallen.

Maskertypen
Net als bij waardetypen is een maskertype een basistype (meestal een DWORD) dat is onderverdeeld in door mensen leesbare labels voor elk van de bits die betekenis hebben. Neem het volgende voorbeeld:

mask DWORD DirectDrawOptSurfaceDescCapsFlags
{
#define DDOSDCAPS_OPTCOMPRESSED                 0x00000001
#define DDOSDCAPS_OPTREORDERED                  0x00000002
#define DDOSDCAPS_MONOLITHICMIPMAP              0x00000004
};

Hiermee declareert u een nieuw type dat is afgeleid van DWORD. Als deze als functieparameter wordt gebruikt, worden de afzonderlijke waarden voor de gebruiker in LogViewer uitgesplitsd. Dus als de waarde 0x00000005 is, zal LogViewer het volgende weergeven:

DDOSDCAPS_OPTCOMPRESSED | DDOSDCAPS_MONOLITHICMIPMAP

GUID-typen
GUID's zijn wereldwijd unieke id's van 16 bytes die uitgebreid worden gebruikt in COM. Ze worden op twee manieren gedeclareerd:

struct __declspec(uuid("00020400-0000-0000-C000-000000000046")) IDispatch;

or

class __declspec(uuid("11219420-1768-11D1-95BE-00609797EA4F")) ShellLinkObject;

De eerste methode wordt gebruikt om een interface-id (IID) te declareren. Wanneer deze wordt weergegeven door LogViewer, wordt 'IID_' toegevoegd aan het begin van de weergavenaam. De tweede methode wordt gebruikt om een klasse-id (CLSID) te declareren. LogViewer voegt 'CLSID_' toe aan het begin van de weergavenaam.

Als een GUID-type een parameter is voor een functie, vergelijkt LogViewer de waarde met alle gedeclareerde IID's en CLSID's. Als er een overeenkomst wordt gevonden, wordt de gebruiksvriendelijke naam van IID weergegeven. Zo niet, dan wordt de waarde van 32 hexadecimale tekens weergegeven in de standaard-GUID-notatie.

COM_INTERFACE_PTR Typen
Het COM_INTERFACE_PTR type is het basistype van een COM-interfacepointer. Wanneer u een COM-interface declareert, definieert u eigenlijk een nieuw type dat is afgeleid van COM_INTERFACE_PTR. Als zodanig kan een aanwijzer naar een dergelijk type een parameter voor een functie zijn. Als een COM_INTERFACE_PTR basistype wordt gedeclareerd als een OUT-parameter voor een functie en er een afzonderlijke parameter is met een [iid]-label, vergelijkt Logger de doorgegeven in IID met alle gedeclareerde GUID's. Als er een overeenkomst is en er een COM-interface is gedeclareerd die dezelfde naam heeft als de IID, zal Logger alle functies in die interface koppelen en registreren.

Hier volgt een voorbeeld:

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

In dit voorbeeld heeft riid een [iid] modifier. Dit geeft aan Logger aan dat de aanwijzer die in ppv is geretourneerd, een COM-interfacepointer is voor de interface die wordt geïdentificeerd door riid.

Het is ook mogelijk om een functie als volgt te declareren:

DDRESULT DirectDrawCreateClipper( DWORD dwFlags, [out] LPDIRECTDRAWCLIPPER *lplpDDClipper, IUnknown *pUnkOuter );

In dit voorbeeld wordt LPDIRECTDRAWCLIPPER gedefinieerd als een aanwijzer naar de IDirectDrawClipper-interface . Omdat Logger kan identificeren welk interfacetype wordt geretourneerd in de parameter lplpDDClipper , is er geen [iid] modifier nodig voor een van de andere parameters.