3.1用c语言还是用C++语言
从理论上讲,只要能编译出PE二进制文件格式的语言都可以编写驱动,C、C++、汇编语言甚至Delphi语言都可以,但提供DDK只含有C\C++的链接库,所以最好用C\C++来开发驱动。
C++面向对象,灵活性和模块性高。但C语言实例多。个人还是倾向于用C语言。
3.1.1 调用约定
分为四种__cdecl, __stdcall, __fastcall 和 thiscall,其中最常见到的就是前两种。
1)__cdecl
c语言调用约定, c语言调用会在obj文件中产生一个符号来代表函数,符号的形式是下划线+函数名,并且函数体以ret形式返回。参数的压栈为从右到左。由于函数以ret返回,所以调用后的堆栈与调用前是不同的,需要将栈指针+参数长度。由调用者恢复堆栈。
例void __cdecl Foo(int a, int b);
Foo(0x12345678, 0x11223344)
展开汇编为:
push 11223344h
push 12345678h
call _Foo
add esp,8
2)__stdcall
void __stdcall Foo(int a, int b)
c语言调用在obj文件中产生一个符号来代替函数。形式为 下划线+函数名@X, X代表清理堆栈时需要的数字,函数以 ret X返回。从右向左压栈。函数负责恢复堆栈。
Foo(0x12345678, 0x11223344)
展开汇编为
push 11223344h
push 12345678h
call _Foo@8
驱动程序的编写需要标准调用约定。而一般vc编译器默认为C调用约定,所以用vc编译时注意修改调用约定。DDK编译环境编译驱动时默认为标准调用约定。
3.1.2函数的导出名
cl.exe既可以编译C语言又可以编译C++,编译器会默认根据文件的扩展名来用不同编译方式。同一种调用约定按不同德编译方式会产生不同德编译结果。
例 void __stdcall Foo(int a, int b)
在C++编译方式中会编译成obj文件后的符号为?Foo@@YGXHH@Z, 而在c编译方式中为_Foo@8.
windows驱动的默认入口函数为_DriverEntry@8。因此用c++编译时会导致连接错误。所以需要用extern “C”来解决。
例extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegisterPath)
同样在c++程序中包含ntddk.h和wdm.h时,也会有编译错误。也需要用extern "C"来修饰。
#ifdef __cplusplus
extern "C"
{
#endif
#include <NTDDK.h>
#ifdef __cplusplus
}#endif