无法理解这个 c++ typedef
Cannot understand this c++ typedef
我不太确定如何阅读这段代码:
typedef NTSTATUS(NTAPI* QUERYINFORMATIONPROCESS)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
NTSTATUS
或QUERYINFORMATIONPROCESS
是typedef
的名字吗?如果是这样,实际类型是什么?这是函数指针类型吗?用法是这样的:
QUERYINFORMATIONPROCESS QueryInformationProcess =
(QUERYINFORMATIONPROCESS)GetProcAddress(
hDll, "NtQueryInformationProcess");
if (QueryInformationProcess)
{
NTSTATUS ntStatus = QueryInformationProcess(
processInformation.hProcess,
PROCESSINFOCLASS::ProcessBasicInformation,
&pbi, sizeof(pbi), &uLength);
[…]
这个出自"C++ Multithreading Cookbook",里面没有解释这段代码。感谢您的帮助!
它是一个指向函数指针的类型定义,称为 QUERYINFORMATIONPROCESS。这个函数有 5 个参数,return 一个 NTSTATUS。
typedef NTSTATUS(NTAPI* QUERYINFORMATIONPROCESS)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
这里最难的部分是这是一个函数指针:
当声明一个函数指针时,为了区别于函数returning 一个指针,我们需要在它的名称两边加上括号:
(*QUERYINFORMATIONPROCESS)
指针之前的 NTAPI 告诉编译器调用约定(如何传递参数 [寄存器、堆栈、参数顺序]、如何清理参数等)。
剩下的就是 return 类型 (NTSTATUS
) 和函数的参数。
一个更简单的例子是:
typedef int (*funcptr)(int x);
现在,相对容易看出它是一个 returning int
函数,它是一个指向该函数的指针,它接受一个参数 int x
。
不使用 Microsoft 宏编写它是
typedef NTSTATUS (*QUERYINFORMATIONPROCESS)(
HANDLE ProcessHandle,
PROCESSINFOCLASS ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
这是一个名为"QUERYINFORMATIONPROCESS"的函数指针类型。
具体是指向NtQueryInformationProcess的指针,需要从DLL中加载。
(您的书的作者似乎有拼写问题。)
那将是一个函数指针 typedef
,它将类型 QUERYINFORMATIONPROCESS
定义为该类型的别名:
NTSTATUS(NTAPI*)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
在示例中使用时,
QUERYINFORMATIONPROCESS QueryInformationProcess =
(QUERYINFORMATIONPROCESS)GetProcAddress(
hDll, "NtQueryInformationProcess");
它扩展成这样:
NTSTATUS(NTAPI* QueryInformationProcess)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
) =
(NTSTATUS(NTAPI*)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
))GetProcAddress(hDll, "NtQueryInformationProcess");
(不能 100% 确定这是否是正确的扩展,我发现函数指针通常很尴尬。)
使用函数指针类型定义是因为函数指针语法本身确实笨拙,而像这样的函数指针别名使用标准语法。
有关函数指针的详细信息,请参阅 this answer。
此外,它是指向 Windows 函数的指针,该函数使用标准 Windows 标识符(NTSTATUS、NTAPI、IN、OUT、HANDLE、PROCESSINFOCLASS、PVOID、ULONG 和 PULONG)。
根据 Windows 文件,这些标识符是:
// ntdef.h
typedef __success(return >= 0) LONG NTSTATUS;
// See
// WinNT.h
#define NTAPI __stdcall
// __stdcall is a Windows calling convention, used by Visual Studio when compiling Windows code.
typedef void *PVOID;
#ifdef STRICT
typedef void *HANDLE;
#if 0 && (_MSC_VER > 1000)
#define DECLARE_HANDLE(name) struct name##__; typedef struct name##__ *name
#else
#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name
#endif
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(name) typedef HANDLE name
#endif
// WinDef.h
typedef unsigned long ULONG;
typedef ULONG *PULONG;
#ifndef IN
#define IN
#endif
#ifndef OUT
#define OUT
#endif
IN和OUT是作为程序员的辅助工具,而类型则是给程序员一个一致的接口。不完全确定 PROCESSINFOCLASS
;它似乎与 NtQueryInformationProcess
本身一起在 ntdll.dll
中定义。根据this MSDN thread,定义如下:
private enum PROCESSINFOCLASS : int
{
ProcessBasicInformation = 0,
ProcessQuotaLimits,
ProcessIoCounters,
ProcessVmCounters,
ProcessTimes,
ProcessBasePriority,
ProcessRaisePriority,
ProcessDebugPort,
ProcessExceptionPort,
ProcessAccessToken,
ProcessLdtInformation,
ProcessLdtSize,
ProcessDefaultHardErrorMode,
ProcessIoPortHandlers, // Note: this is kernel mode only
ProcessPooledUsageAndLimits,
ProcessWorkingSetWatch,
ProcessUserModeIOPL,
ProcessEnableAlignmentFaultFixup,
ProcessPriorityClass,
ProcessWx86Information,
ProcessHandleCount,
ProcessAffinityMask,
ProcessPriorityBoost,
MaxProcessInfoClass
} ;
这似乎已经过时了,但是 this site seems to have a more recent version, which appears to be consistent with official MS NtQueryInformationProcess() documentation。
希望这对您有所帮助。
有一条规则可以帮助阅读如此详细的类型定义,称为 Clockwise/Spiral 规则。该规则是一个三步算法,引用原始文章的步骤是:
- 从未知元素开始,向spiral/clockwise方向移动;当遇到以下元素时,将它们替换为相应的英文语句:
- [X] 或 [] => 数组 X 大小...或数组未定义大小...
- (type1, type2) => 函数传递 type1 和 type2 返回...
- * => 指向...
的指针
- 在 spiral/clockwise 方向继续这样做,直到所有标记都被覆盖。
- 始终先解决括号中的任何问题!
知道哪个标识符对应于一个类型通常很有用,以便识别未知元素(可能有多个这样的元素)。在你的情况下 - 知道所有 (NTSTATUS
, NTAPI
, HANDLE
, PROCESSINFOCLASS
, PVOID
, ULONG
, PULONG
) 是类型。
文章提供了有关应用规则的有用示例。
此外,我在 Google 中找到了一张说明规则的图片:
识别未知元素:
void
是一个类型
signal
,嗯,好像是未知元素。此外,它是
位于星号右侧,就我而言只能是 variable/typedef 名称
int
, void
- 类型
fp
,第二个未知元素(记住,我之前提到过可以有多个未知元素)
int
, int
- 类型
现在从最左边的未知元素开始应用规则,即 signal
:
- 从
signal
向右移动并遇到第一个左括号:signal
是一个带有 int
和一些复杂的东西的函数
- 我们来分析第二个未知元素
fp
,它也是signal
的第二个参数。从 fp
开始顺时针螺旋移动,遇到右括号,然后是星号:fp
是指向某物的指针。
- 继续从
*fp
移动并遇到左括号:fp
是一个指向接受 int
并返回某些内容的函数的指针。
- 继续移动遇到
void
:fp
是一个指向接受int
并返回void
的函数的指针。
- 现在我们已经将第二个参数解码为
signal
:signal
是一个接受 int, and a pointer to a function taking an
intand returning
void` 的函数
- 继续从
signal(
移动并遇到星号:signal
是一个返回指针的函数...
- 继续从
(*
移动,遇到最右边的左括号:signal
是一个函数(我在这里省略了参数)返回一个指向接受int
的函数的指针
- 最后,遇到
void
:signal
是一个函数(我在这里省略了参数)返回一个指向接受int
并返回[=18=的函数的指针].
P.S。您问题中的 typedef 可以用 using
声明用现代 C++ 编写:
using QUERYINFORMATIONPROCESS = NTSTATUS(NTAPI*)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
它没有太大变化,但是 typedef-name
与实际定义有更多的分离,使其更具可读性。编写新代码时,请考虑使用 using
而不是 typedef
:-)
我不太确定如何阅读这段代码:
typedef NTSTATUS(NTAPI* QUERYINFORMATIONPROCESS)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
NTSTATUS
或QUERYINFORMATIONPROCESS
是typedef
的名字吗?如果是这样,实际类型是什么?这是函数指针类型吗?用法是这样的:
QUERYINFORMATIONPROCESS QueryInformationProcess =
(QUERYINFORMATIONPROCESS)GetProcAddress(
hDll, "NtQueryInformationProcess");
if (QueryInformationProcess)
{
NTSTATUS ntStatus = QueryInformationProcess(
processInformation.hProcess,
PROCESSINFOCLASS::ProcessBasicInformation,
&pbi, sizeof(pbi), &uLength);
[…]
这个出自"C++ Multithreading Cookbook",里面没有解释这段代码。感谢您的帮助!
它是一个指向函数指针的类型定义,称为 QUERYINFORMATIONPROCESS。这个函数有 5 个参数,return 一个 NTSTATUS。
typedef NTSTATUS(NTAPI* QUERYINFORMATIONPROCESS)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
这里最难的部分是这是一个函数指针:
当声明一个函数指针时,为了区别于函数returning 一个指针,我们需要在它的名称两边加上括号:
(*QUERYINFORMATIONPROCESS)
指针之前的 NTAPI 告诉编译器调用约定(如何传递参数 [寄存器、堆栈、参数顺序]、如何清理参数等)。
剩下的就是 return 类型 (NTSTATUS
) 和函数的参数。
一个更简单的例子是:
typedef int (*funcptr)(int x);
现在,相对容易看出它是一个 returning int
函数,它是一个指向该函数的指针,它接受一个参数 int x
。
不使用 Microsoft 宏编写它是
typedef NTSTATUS (*QUERYINFORMATIONPROCESS)(
HANDLE ProcessHandle,
PROCESSINFOCLASS ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
这是一个名为"QUERYINFORMATIONPROCESS"的函数指针类型。
具体是指向NtQueryInformationProcess的指针,需要从DLL中加载。
(您的书的作者似乎有拼写问题。)
那将是一个函数指针 typedef
,它将类型 QUERYINFORMATIONPROCESS
定义为该类型的别名:
NTSTATUS(NTAPI*)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
在示例中使用时,
QUERYINFORMATIONPROCESS QueryInformationProcess =
(QUERYINFORMATIONPROCESS)GetProcAddress(
hDll, "NtQueryInformationProcess");
它扩展成这样:
NTSTATUS(NTAPI* QueryInformationProcess)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
) =
(NTSTATUS(NTAPI*)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
))GetProcAddress(hDll, "NtQueryInformationProcess");
(不能 100% 确定这是否是正确的扩展,我发现函数指针通常很尴尬。)
使用函数指针类型定义是因为函数指针语法本身确实笨拙,而像这样的函数指针别名使用标准语法。
有关函数指针的详细信息,请参阅 this answer。
此外,它是指向 Windows 函数的指针,该函数使用标准 Windows 标识符(NTSTATUS、NTAPI、IN、OUT、HANDLE、PROCESSINFOCLASS、PVOID、ULONG 和 PULONG)。
根据 Windows 文件,这些标识符是:
// ntdef.h
typedef __success(return >= 0) LONG NTSTATUS;
// See
// WinNT.h
#define NTAPI __stdcall
// __stdcall is a Windows calling convention, used by Visual Studio when compiling Windows code.
typedef void *PVOID;
#ifdef STRICT
typedef void *HANDLE;
#if 0 && (_MSC_VER > 1000)
#define DECLARE_HANDLE(name) struct name##__; typedef struct name##__ *name
#else
#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name
#endif
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(name) typedef HANDLE name
#endif
// WinDef.h
typedef unsigned long ULONG;
typedef ULONG *PULONG;
#ifndef IN
#define IN
#endif
#ifndef OUT
#define OUT
#endif
IN和OUT是作为程序员的辅助工具,而类型则是给程序员一个一致的接口。不完全确定 PROCESSINFOCLASS
;它似乎与 NtQueryInformationProcess
本身一起在 ntdll.dll
中定义。根据this MSDN thread,定义如下:
private enum PROCESSINFOCLASS : int
{
ProcessBasicInformation = 0,
ProcessQuotaLimits,
ProcessIoCounters,
ProcessVmCounters,
ProcessTimes,
ProcessBasePriority,
ProcessRaisePriority,
ProcessDebugPort,
ProcessExceptionPort,
ProcessAccessToken,
ProcessLdtInformation,
ProcessLdtSize,
ProcessDefaultHardErrorMode,
ProcessIoPortHandlers, // Note: this is kernel mode only
ProcessPooledUsageAndLimits,
ProcessWorkingSetWatch,
ProcessUserModeIOPL,
ProcessEnableAlignmentFaultFixup,
ProcessPriorityClass,
ProcessWx86Information,
ProcessHandleCount,
ProcessAffinityMask,
ProcessPriorityBoost,
MaxProcessInfoClass
} ;
这似乎已经过时了,但是 this site seems to have a more recent version, which appears to be consistent with official MS NtQueryInformationProcess() documentation。
希望这对您有所帮助。
有一条规则可以帮助阅读如此详细的类型定义,称为 Clockwise/Spiral 规则。该规则是一个三步算法,引用原始文章的步骤是:
- 从未知元素开始,向spiral/clockwise方向移动;当遇到以下元素时,将它们替换为相应的英文语句:
- [X] 或 [] => 数组 X 大小...或数组未定义大小...
- (type1, type2) => 函数传递 type1 和 type2 返回...
- * => 指向... 的指针
- 在 spiral/clockwise 方向继续这样做,直到所有标记都被覆盖。
- 始终先解决括号中的任何问题!
知道哪个标识符对应于一个类型通常很有用,以便识别未知元素(可能有多个这样的元素)。在你的情况下 - 知道所有 (NTSTATUS
, NTAPI
, HANDLE
, PROCESSINFOCLASS
, PVOID
, ULONG
, PULONG
) 是类型。
文章提供了有关应用规则的有用示例。
此外,我在 Google 中找到了一张说明规则的图片:
识别未知元素:
void
是一个类型signal
,嗯,好像是未知元素。此外,它是 位于星号右侧,就我而言只能是 variable/typedef 名称int
,void
- 类型fp
,第二个未知元素(记住,我之前提到过可以有多个未知元素)int
,int
- 类型
现在从最左边的未知元素开始应用规则,即 signal
:
- 从
signal
向右移动并遇到第一个左括号:signal
是一个带有int
和一些复杂的东西的函数 - 我们来分析第二个未知元素
fp
,它也是signal
的第二个参数。从fp
开始顺时针螺旋移动,遇到右括号,然后是星号:fp
是指向某物的指针。 - 继续从
*fp
移动并遇到左括号:fp
是一个指向接受int
并返回某些内容的函数的指针。 - 继续移动遇到
void
:fp
是一个指向接受int
并返回void
的函数的指针。 - 现在我们已经将第二个参数解码为
signal
:signal
是一个接受int, and a pointer to a function taking an
intand returning
void` 的函数
- 继续从
signal(
移动并遇到星号:signal
是一个返回指针的函数... - 继续从
(*
移动,遇到最右边的左括号:signal
是一个函数(我在这里省略了参数)返回一个指向接受int
的函数的指针 - 最后,遇到
void
:signal
是一个函数(我在这里省略了参数)返回一个指向接受int
并返回[=18=的函数的指针].
P.S。您问题中的 typedef 可以用 using
声明用现代 C++ 编写:
using QUERYINFORMATIONPROCESS = NTSTATUS(NTAPI*)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
它没有太大变化,但是 typedef-name
与实际定义有更多的分离,使其更具可读性。编写新代码时,请考虑使用 using
而不是 typedef
:-)