命名风格
从整体来看,该项目的命名风格如下:
全局变量:大驼峰
函数名:大驼峰
函数参数:小驼峰
局部变量:小驼峰
宏:全大写,单词下划线隔开
所有的数据类型用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
但是要避免未定义错误
所以全局变量定义时,最好别赋初值。