无法理解这个 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
  );

NTSTATUSQUERYINFORMATIONPROCESStypedef的名字吗?如果是这样,实际类型是什么?这是函数指针类型吗?用法是这样的:

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 规则。该规则是一个三步算法,引用原始文章的步骤是:

  1. 从未知元素开始,向spiral/clockwise方向移动;当遇到以下元素时,将它们替换为相应的英文语句:
    • [X] 或 [] => 数组 X 大小...或数组未定义大小...
    • (type1, type2) => 函数传递 type1 和 type2 返回...
    • * => 指向...
    • 的指针
  2. 在 spiral/clockwise 方向继续这样做,直到所有标记都被覆盖。
  3. 始终先解决括号中的任何问题!

知道哪个标识符对应于一个类型通常很有用,以便识别未知元素(可能有多个这样的元素)。在你的情况下 - 知道所有 (NTSTATUS, NTAPI, HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG ) 是类型。

文章提供了有关应用规则的有用示例。

此外,我在 Google 中找到了一张说明规则的图片:

识别未知元素:

  • void是一个类型
  • signal,嗯,好像是未知元素。此外,它是 位于星号右侧,就我而言只能是 variable/typedef 名称
  • int, void - 类型
  • fp,第二个未知元素(记住,我之前提到过可以有多个未知元素)
  • int, int - 类型

现在从最左边的未知元素开始应用规则,即 signal:

  1. signal 向右移动并遇到第一个左括号:signal 是一个带有 int 和一些复杂的东西的函数
  2. 我们来分析第二个未知元素fp,它也是signal的第二个参数。从 fp 开始顺时针螺旋移动,遇到右括号,然后是星号:fp 是指向某物的指针。
  3. 继续从 *fp 移动并遇到左括号:fp 是一个指向接受 int 并返回某些内容的函数的指针。
  4. 继续移动遇到voidfp是一个指向接受int并返回void的函数的指针。
  5. 现在我们已经将第二个参数解码为 signalsignal 是一个接受 int, and a pointer to a function taking anintand returningvoid`
  6. 的函数
  7. 继续从 signal( 移动并遇到星号:signal 是一个返回指针的函数...
  8. 继续从(*移动,遇到最右边的左括号:signal是一个函数(我在这里省略了参数)返回一个指向接受int的函数的指针
  9. 最后,遇到voidsignal是一个函数(我在这里省略了参数)返回一个指向接受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 :-)