매니페스트 파일 형식
매니페스트 파일의 파일 형식은 C++ 및 IDL에서 최대한 많이 차용됩니다. 따라서 일반 C++ SDK 헤더 파일을 가져와 매니페스트 파일로 수정하는 것이 매우 쉽습니다. 파서는 C 및 C++ 스타일 주석을 완벽하게 지원하여 파일을 구성하고 문서화하는 데 도움이 됩니다.
매니페스트 파일을 추가하거나 기존 파일을 변경하려는 경우 이를 수행하는 가장 좋은 방법은 실험만 하는 것입니다. 디버거에서 !logexts.logi 또는 !logexts.loge 명령을 실행하면 로거가 매니페스트 파일을 구문 분석하려고 시도합니다. 문제가 발생하면 오류를 나타낼 수 있는 오류 메시지가 생성됩니다.
매니페스트 파일은 모듈 레이블, 범주 레이블, 함수 선언, COM 인터페이스 정의 및 형식 정의와 같은 기본 요소로 구성됩니다. 다른 유형의 요소도 존재하지만 가장 중요합니다.
모듈 레이블
모듈 레이블은 이후에 선언된 함수를 내보내는 DLL을 선언합니다. 예를 들어 매니페스트 파일이 Comctl32.dll 함수 그룹을 로깅하는 경우 함수 프로토타입을 선언하기 전에 다음 모듈 레이블을 포함합니다.
module COMCTL32.DLL:
모듈 레이블은 매니페스트 파일의 함수 선언 앞에 나타나야 합니다. 매니페스트 파일에는 여러 모듈 레이블이 포함될 수 있습니다.
범주 레이블
모듈 레이블과 마찬가지로 범주 레이블은 모든 후속 함수 및/또는 COM 인터페이스가 속한 "범주"를 식별합니다. 예를 들어 Comctl32.dll 매니페스트 파일을 만드는 경우 범주 레이블로 다음을 사용할 수 있습니다.
category CommonControls:
매니페스트 파일에는 여러 범주 레이블이 포함될 수 있습니다.
함수 선언
함수 선언은 실제로 로거에게 무언가를 기록하라는 메시지를 표시합니다. C/C++ 헤더 파일에 있는 함수 프로토타입과 거의 동일합니다. 형식에 몇 가지 주목할 만한 추가 사항이 있으며 다음 예제에서 가장 잘 설명될 수 있습니다.
HANDLE [gle] FindFirstFileA(
LPCSTR lpFileName,
[out] LPWIN32_FIND_DATAA lpFindFileData);
FindFirstFileA 함수는 두 개의 매개 변수를 사용합니다. 첫 번째는 파일 또는 파일을 검색할 위치를 정의하는 전체 경로(일반적으로 와일드카드 포함)인 lpFileName입니다. 두 번째는 검색 결과를 포함하는 데 사용할 WIN32_FIND_DATAA 구조체에 대한 포인터입니다. 반환된 HANDLE은 FindNextFileA에 대한 이후 호출에 사용됩니다. FindFirstFileA가 INVALID_HANDLE_VALUE 반환하면 함수 호출이 실패하고 GetLastError 함수를 호출하여 오류 코드를 조달할 수 있습니다.
HANDLE 형식은 다음과 같이 선언됩니다.
value DWORD HANDLE
{
#define NULL 0 [fail]
#define INVALID_HANDLE_VALUE -1 [fail]
};
이 함수에서 반환된 값이 0 또는 -1(0xFFFFFFFF)이면 로거는 값 선언에 [fail] 한정자가 있으므로 함수가 실패했다고 가정합니다. (이 섹션의 뒷부분에 있는 값 형식 섹션을 참조하세요.) 함수 이름 바로 앞에 [gle] 한정자가 있으므로 Logger는 이 함수가 GetLastError 를 사용하여 오류 코드를 반환한다는 것을 인식하므로 오류 코드를 캡처하고 로그 파일에 기록합니다.
lpFindFileData 매개 변수의 [out] 한정자는 로거에게 데이터 구조가 함수에 의해 채워지고 함수가 반환될 때 기록되어야 한다는 것을 알려줍니다.
COM 인터페이스 정의
COM 인터페이스는 기본적으로 COM 개체의 클라이언트에서 호출할 수 있는 함수의 벡터입니다. 매니페스트 형식은 COM에서 인터페이스를 정의하는 데 사용되는 IDL(인터페이스 정의 언어)에서 크게 차용됩니다.
다음 예제를 살펴보겠습니다.
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 );
};
IUnknown에서 파생된 IDispatch라는 인터페이스를 선언합니다. 인터페이스의 중괄호 내에서 특정 순서로 선언되는 4개의 멤버 함수가 포함되어 있습니다. 로거는 인터페이스의 vtable(런타임에 사용되는 함수 포인터의 실제 이진 벡터)에 있는 함수 포인터를 자체적으로 대체하여 이러한 멤버 함수를 가로채고 기록합니다. 로거가 전달되는 인터페이스를 캡처하는 방법에 대한 자세한 내용은 이 섹션의 뒷부분에 있는 COM_INTERFACE_PTR 형식 섹션을 참조하세요.
형식 정의
데이터 형식 정의는 매니페스트 파일 개발에서 가장 중요하고 가장 지루한 부분입니다. 매니페스트 언어를 사용하면 함수에서 전달되거나 반환되는 숫자 값에 대해 사람이 읽을 수 있는 레이블을 정의할 수 있습니다.
예를 들어 Winerror.h는 대부분의 Microsoft Win32 함수 및 해당 사람이 읽을 수 있는 레이블에서 반환하는 오류 값 목록인 "WinError"라는 형식을 정의합니다. 이를 통해 Logger 및 LogViewer는 잘못된 오류 코드를 의미 있는 텍스트로 바꿀 수 있습니다.
또한 로거와 LogViewer가 DWORD 비트 마스크를 해당 구성 요소로 분리할 수 있도록 비트 마스크 내에서 개별 비트에 레이블을 지정할 수 있습니다.
매니페스트에서 지원하는 13가지 기본 형식이 있습니다. 이러한 필드는 다음 표에 나열되어 있습니다.
형식 | 길이 | 표시 예제 |
---|---|---|
포인터 |
4바이트 |
0x001AF320 |
VOID |
0바이트 |
|
BYTE |
1바이트 |
0x32 |
WORD |
2바이트 |
0x0A23 |
DWORD |
4바이트 |
-234323 |
BOOL |
1바이트 |
TRUE |
LPSTR |
길이 바이트와 임의의 문자 수 |
"빠른 갈색 여우" |
LPWSTR |
길이 바이트 및 모든 수의 유니코드 문자 |
"게으른 개 위에 뛰어" |
GUID |
16바이트 |
{0CF774D0-F077-11D1-B1BC-00C04F86C324} |
COM_INTERFACE_PTR |
4바이트 |
0x0203404A |
값 |
기본 형식에 종속 |
ERROR_TOO_MANY_OPEN_FILES |
마스크 |
기본 형식에 종속 |
WS_MAXIMIZED | WS_ALWAYSONTOP |
struct |
캡슐화된 형식의 크기에 따라 달라집니다. |
+ lpRect nLeft 34 nRight 54 nTop 100 nBottom 300 |
매니페스트 파일의 형식 정의는 C/C++ typedefs처럼 작동합니다. 예를 들어 다음 문은 PLONG을 LONG에 대한 포인터로 정의합니다.
typedef LONG *PLONG;
대부분의 기본 typedef는 Main.h에서 이미 선언되었습니다. 구성 요소와 관련된 typedef만 추가하면 됩니다. 구조체 정의의 형식은 C/C++ 구조체 형식과 동일합니다.
value, mask, GUID 및 COM_INTERFACE_PTR 네 가지 특수 형식이 있습니다.
값 형식
값은 사람이 읽을 수 있는 레이블로 구분되는 기본 형식입니다. 대부분의 함수 설명서는 함수에 사용되는 특정 상수의 #define 값만 나타냅니다. 예를 들어 대부분의 프로그래머는 GetLastError에서 반환된 모든 코드의 실제 값을 인식하지 못하므로 LogViewer에서 비밀 숫자 값을 보는 것은 도움이 되지 않습니다. 매니페스트 값은 다음 예제와 같이 값 선언을 허용하여 이를 극복합니다.
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
};
이렇게 하면 LONG에서 파생된 "ChangeNotifyFlags"라는 새 형식이 선언됩니다. 함수 매개 변수로 사용되는 경우 사람이 읽을 수 있는 별칭이 원시 숫자 대신 표시됩니다.
마스크 형식
값 형식과 마찬가지로 마스크 형식은 의미가 있는 각 비트에 대해 사람이 읽을 수 있는 레이블로 구분되는 기본 형식(일반적으로 DWORD)입니다. 다음 예제를 참조하세요.
mask DWORD DirectDrawOptSurfaceDescCapsFlags
{
#define DDOSDCAPS_OPTCOMPRESSED 0x00000001
#define DDOSDCAPS_OPTREORDERED 0x00000002
#define DDOSDCAPS_MONOLITHICMIPMAP 0x00000004
};
그러면 함수 매개 변수로 사용되는 경우 LogViewer에서 사용자에 대해 개별 값이 구분되는 DWORD에서 파생된 새 형식을 선언합니다. 따라서 값이 0x00000005 경우 LogViewer는 다음을 표시합니다.
DDOSDCAPS_OPTCOMPRESSED | DDOSDCAPS_MONOLITHICMIPMAP
GUID 형식
GUID는 COM에서 광범위하게 사용되는 16비트 전역 고유 식별자입니다. 두 가지 방법으로 선언됩니다.
struct __declspec(uuid("00020400-0000-0000-C000-000000000046")) IDispatch;
또는
class __declspec(uuid("11219420-1768-11D1-95BE-00609797EA4F")) ShellLinkObject;
첫 번째 메서드는 IID(인터페이스 식별자)를 선언하는 데 사용됩니다. LogViewer에 의해 표시되면 표시 이름의 시작 부분에 "IID_"이 추가됩니다. 두 번째 메서드는 CLSID(클래스 식별자)를 선언하는 데 사용됩니다. LogViewer는 표시 이름의 시작 부분에 "CLSID_"을 추가합니다.
GUID 형식이 함수에 대한 매개 변수인 경우 LogViewer는 선언된 모든 IID 및 CLSID와 값을 비교합니다. 일치하는 항목이 발견되면 IID 식별 이름이 표시됩니다. 그렇지 않은 경우 표준 GUID 표기법으로 32-16진수 문자 값을 표시합니다.
COM_INTERFACE_PTR 형식
COM_INTERFACE_PTR 형식은 COM 인터페이스 포인터의 기본 형식입니다. COM 인터페이스를 선언할 때 실제로 COM_INTERFACE_PTR 파생된 새 형식을 정의합니다. 따라서 이러한 형식에 대한 포인터는 함수에 대한 매개 변수가 될 수 있습니다. COM_INTERFACE_PTR 기본 형식이 함수에 대한 OUT 매개 변수로 선언되고 [iid] 레이블이 있는 별도의 매개 변수가 있는 경우 로거는 IID에 전달된 를 선언된 모든 GUID와 비교합니다. 일치 항목이 있고 IID와 이름이 같은 COM 인터페이스가 선언된 경우 로거는 해당 인터페이스의 모든 함수를 후크하고 기록합니다.
예를 들면 다음과 같습니다.
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
);
이 예제에서 riid 에는 [iid] 한정자가 있습니다. 이는 ppv 에서 반환된 포인터가 riid로 식별된 인터페이스에 대한 COM 인터페이스 포인터임을 로거에 나타냅니다.
다음과 같이 함수를 선언할 수도 있습니다.
DDRESULT DirectDrawCreateClipper( DWORD dwFlags, [out] LPDIRECTDRAWCLIPPER *lplpDDClipper, IUnknown *pUnkOuter );
이 예제에서 LPDIRECTDRAWCLIPPER는 IDirectDrawClipper 인터페이스에 대한 포인터로 정의됩니다. 로거는 lplpDDClipper 매개 변수에서 반환되는 인터페이스 형식을 식별할 수 있으므로 다른 매개 변수에는 [iid] 한정자가 필요하지 않습니다.