命名风格

从整体来看,该项目的命名风格如下:

全局变量:大驼峰

函数名:大驼峰

函数参数:小驼峰

局部变量:小驼峰

宏:全大写,单词下划线隔开

所有的数据类型用typedef全部替换一遍:

typedef short SHORT;
typedef long LONG;
typedef int INT;

命名做到【见名知义】,不用刻意缩写

1、

代码学习

#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name

DECLARE_HANDLE(HFONT);

##是连接符号,就是把前后连起来

这个时候【HFONT】就会替换【name】

宏展开就为:

struct HFONT__{int unused;}; typedef struct HFONT__ *HFONT

2、

#define PHAPPAPI __declspec(dllexport)

__declspec(dllexport)作用是【导出】dll库函数,使用时引入头文件

3、函数的输入

INT WINAPI wWinMain(
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ PWSTR lpCmdLine,
    _In_ INT nCmdShow
    )

首先是函数参数隔行写,这种写法清晰、容易注释。

_In_在Windows编程中属于空宏,没有什么实际作用,但是【标示】这个参数是函数的输入参数。

// Input parameters --------------------------

//   _In_ - Annotations for parameters where data is passed into the function, but not modified.
//          _In_ by itself can be used with non-pointer types (although it is redundant).

// e.g. void SetPoint( _In_ const POINT* pPT );
#define _In_                            _SAL2_Source_(_In_, (), _Pre1_impl_(__notnull_impl_notref) _Pre_valid_impl_ _Deref_pre1_impl_(__readaccess_impl_notref))
#define _In_opt_                        _SAL2_Source_(_In_opt_, (), _Pre1_impl_(__maybenull_impl_notref) _Pre_valid_impl_ _Deref_pre_readonly_)

4、单元测试

整个代码都考虑到了debug模式的调试问题,有相应的调试代码

#ifdef DEBUG
    PHP_BASE_THREAD_DBG dbg;
#endif

typedef struct _PHP_BASE_THREAD_DBG
{
    CLIENT_ID ClientId;
    LIST_ENTRY ListEntry;
    PVOID StartAddress;
    PVOID Parameter;

    PPH_AUTO_POOL CurrentAutoPool;
} PHP_BASE_THREAD_DBG, *PPHP_BASE_THREAD_DBG;

5、 __stdcall

__stdcall修饰的函数,表明函数参数【从右往左入栈】

#define CALLBACK __stdcall
#define WINAPI __stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
#define cdecl _cdecl
#ifndef CDECL
#define CDECL _cdecl
#endif

__stdcall 采用的是从右到左压占,自动清理栈空间,我们在调用windows api 特别是在开线程指定线程处理函数的时候,需要进行指定。

Windows API使用__stdcall是个历史遗留的产物,要知道早期,NT3.1那时候的内存是非常宝贵的,所以使用__stdcall可以显著节约内存。

在C++中,可以在函数声明或定义时用关键字__stdcall指定调用约定。C语言的缺省调用约定为__cdecl

现在想c++11 的那些都不要加上__stdcall因为都已经封装好了,如何是使用windows系统api需要注意这一点。

WINDOWS的函数调用时需要用到栈(STACK,一种先入后出的存储结构)。当函数调用完成后,栈需要清理,这里就是问题的关键,如何清理???

如果我们的函数使用了__cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清理工作呢?答案是不能。

如果使用__stdcall,上面的问题就解决了,函数自己解决清理工作。所以,在跨(开发)平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现)。

综上所述:

__stdcall被调用者清理堆栈,生成执行代码小,节省内存。 缺点: 不支持可变参数。 windows api使用__stdcall是历史问题。

C调用约定(即用cdecl关键字说明)(The C default calling convention)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数vararg的函数(如printf)只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。 `cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用__stdcall`函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。

6、typedef _Success_(return >= 0) LONG NTSTATUS;

一、NTSTATUS

直译就是NT状态,也就是内核状态。主要是内核开发/驱动开发用到的API返回的状态。许多内核模式的标准驱动程序例程和驱动程序支持例程使用ntstatus类型作为返回值。此外,当完成IRP时,驱动程序在IRP的IO状态块结构中提供一个ntstatus类型的值。 ntstatus值分为四种类型:成功值、信息值、警告和错误值.

二、NTSTATUS Code

同样是一个32bit的值,大多数的值也是定义了默认的错误消息。它的构成如下:

https://www.cnblogs.com/yilang/p/11163521.html

7、extern全局变量

extern声明告诉编译器这个变量的定义在其他文件中,所以并不会为它分配内存。

内存里只会有一个地方存储这个变量。

https://www.cnblogs.com/zchen1995/p/12173648.html

但是要避免未定义错误

所以全局变量定义时,最好别赋初值。