DriverEntry返回值是NTSTATUS的数据,NTSTATUS被定义为32位的无符号长整型。在驱动程序开发中,人们习惯用NTSTATUS返回状态。DriverEntry的返回值如果表示成功,则意味着加载驱动成功,否则意味着加载驱动失败,调用对象管理程序销毁驱动对象。如果该程序成功,它必须返回STATUS_SUCCESS,否则,它必须返回一个在ntstatus.h定义的错误状态码。下面列出STATUS_SUCCESS与STATUS_UNSUCCESSFUL的定义。 st1\\:*{behavior:url(#ieooui) }
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
1.2.1 协议驱动程序注册
驱动程序在DriverEntry环境中通过调用NdisRegisterProtocol向NDIS注册ProtocolXxx函数。NdisRegisterProtocol定义如下: VOID NdisRegisterProtocol( OUT PNDIS_STATUS Status,
OUT PNDIS_HANDLE NdisProtocolHandler,
IN NDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics, IN UINT CharacteristicsLength );
参数Status是指向调用者提供的一个变量的指针,该函数可以返回下列的值: NDIS_STATUS_SUCCESS
NDIS库把调用者注册为一个协议驱动程序 NDIS_STATUS_BAD_CHARACTERISTICS
对参数ProtocolCharacteristics 中MajorNdisVersion所描述的版本而言CharacteristicsLength太短 NDIS_STATUS_BAD_VERSION
参数ProtocolCharacteristics 中MajorNdisVersion描述的版本不可用 NDIS_STATUS_RESOURCES
资源不够,可能是内存,阻止NDIS库注册调用者
参数NdisProtocolHandle指向调用者提供的一个变量,函数将通过它返回一个描述已注册驱动的一个句柄。参数ProtocolCharacteristics指向一个
NDIS_PROTOCOL_CHARACTERISTICS结构体,由调用者设置。参数CharacteristicsLength描述ProtocolCharacteristics所指结构体的大小。
该调用的返回句柄NdisProtocolHandler对协议驱动程序是透明的,协议驱动程序必须保存该句柄,并在将来对NDIS的调用中作为输入参数传递,例如,打开低层适配器(调用NdisOpenAdapter函数时)。
在调用NdisRegisterProtocol之前,DriverEntry必须完成以下操作:
??????????对NDIS_PROTOCOL_CHARACTERISTICS结构
体进行零初始化,例如调用NdisZeroMemory函数。这将确保可选入口点的尚未使用的成员设置为NULL。如果该结构没被置零,那么在调用NdisRegisterProtocol之前,任何不使用的成员必须置为NULL;
??????????在NDIS_PROTOCOL_CHARACTERISTICS结构
中指定协议兼容的NDIS版本;
??????????在NDIS_PROTOCOL_CHARACTERISTICS结构
中设置驱动程序导出的必需的和可选的ProtocolXxx函数的地址。 在WinPcap的NPF中的具体实现为:首先分配一个NDIS_PROTOCOL_CHARACTERISTICS的结构体,然后执行零初始化,接着用协议数据(版本、名称等)与驱动程序所需回调函数的地址设置该结构体。最后调用NdisRegisterProtocol函数把NPF注册为一个NDIS协议驱动程序。下面为NPF中对应的代码:
//声明一个NDIS_PROTOCOL_CHARACTERISTICS结构实例ProtocolChar; NDIS_PROTOCOL_CHARACTERISTICS ProtocolChar;
//协议名称PacketDriver
NDIS_STRING ProtoName = NDIS_STRING_CONST(\
//对ProtocolChar的内存空间清零
RtlZeroMemory(&ProtocolChar,sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
//用协议数据(版本、名称等)与回调函数地址设置ProtocolChar #ifdef #ifdef NDIS50
ProtocolChar.MajorNdisVersion = 5; #else
ProtocolChar.MajorNdisVersion = 3; #endif
ProtocolChar.MinorNdisVersion = 0; ProtocolChar.Reserved = 0;
ProtocolChar.OpenAdapterCompleteHandler = NPF_OpenAdapterComplete; ProtocolChar.CloseAdapterCompleteHandler = NPF_CloseAdapterComplete; ProtocolChar.SendCompleteHandler = NPF_SendComplete;
ProtocolChar.TransferDataCompleteHandler = NPF_TransferDataComplete; ProtocolChar.ResetCompleteHandler = NPF_ResetComplete; ProtocolChar.RequestCompleteHandler = NPF_RequestComplete; ProtocolChar.ReceiveHandler = NPF_tap;
ProtocolChar.ReceiveCompleteHandler = NPF_ReceiveComplete; ProtocolChar.StatusHandler = NPF_Status;
ProtocolChar.StatusCompleteHandler = NPF_StatusComplete; #ifdef NDIS50
ProtocolChar.BindAdapterHandler = NPF_BindAdapter; ProtocolChar.UnbindAdapterHandler = NPF_UnbindAdapter; ProtocolChar.PnPEventHandler = NPF_PowerChange; ProtocolChar.ReceivePacketHandler = NULL; #endif
ProtocolChar.Name = ProtoName;
//把NPF注册为一个NDIS协议驱动程序 NdisRegisterProtocol( &Status,
&g_NdisProtocolHandle, &ProtocolChar,
sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
if (Status != NDIS_STATUS_SUCCESS) { //注册协议驱动程序到NDIF失败,程序结束 return Status; }
1.2.2 设置卸载例程和IRP的派遣函数
在DriverEntry函数中,一般设置卸载例程和IRP的派遣函数。设置卸载例程和设置派遣函数都是对驱动对象的设置。设备对象中的MajorFunction是一个函数指针数组,IRP_MJ_CREATE、IRP_MJ_ CLOSE、IRP_MJ_WR'lTE等代表数组的第几个元素。下列为NPF的DriverEntry函数中的设置:
DriverObject->MajorFunction[IRP_MJ_CREATE] = NPF_Open; DriverObject->MajorFunction[IRP_MJ_CLOSE] = NPF_Close; DriverObject->MajorFunction[IRP_MJ_CLEANUP]= NPF_Cleanup; DriverObject->MajorFunction[IRP_MJ_READ] = NPF_Read; DriverObject->MajorFunction[IRP_MJ_WRITE] = NPF_Write;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = NPF_IoControl; DriverObject->DriverUnload = NPF_Unload;// 卸载例程
1.2.3 获取系统中可用网络适配器的信息
函数getAdaptersList获取系统中可用的网络适配器信息,返回一个包含系统中可用网络适配器列表的字符串。如果getAdaptersList失败了,NPF试图通过调用getTcpBindings函数获得绑定了TCP/IP的网络适配器信息。函数getTcpBindings返回指向包含绑定了TCP/IP适配器的注册表键值的指针。
下面为DriverEntry函数中相关的代码。
bindP = getAdaptersList(); if (bindP == NULL) {
//在注册表中没找到适配器,试图复制TCP-IP绑定的适配器 tcpBindingsP = getTcpBindings();
if (tcpBindingsP == NULL) {
//没有找到绑定了TCP/IP的适配器,程序执行错误处理 goto RegistryError; }
bindP = (WCHAR*)tcpBindingsP;
bindT = (WCHAR*)(tcpBindingsP->Data);
} else {
bindT = bindP;
}
for (; *bindT != UNICODE_NULL;
bindT += (macName.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR)) {
RtlInitUnicodeString(&macName, bindT);
NPF_CreateDevice(DriverObject, &macName);//给网络适配器创建一个设备 }
1.2.4 对可用的网络适配器创建一个设备
函数NPF_CreateDevice对一个给定的网络适配器创建一个设备。NPF驱动程序也调用NPF_CreateDevice函数,通过IoCreateDevice系统接口把Open/close,read/write与IOCTL请求的句柄地址传递给操作系统。
1.3 DriverEntry函数的具体实现
NPF的DriverEntry注册所有驱动程序的I/O回调函数、创建设备、在NDIS内把NPF定注册为一个协议驱动程序。NPF的DriverEntry函数的主要代码如下: packetNtx\\driver\\packet.c 91~282 NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) {
?
/*根据操作系统的版本,定义跳过回环数据包的正确标识*/
//获得操作系统的版本
PsGetVersion(&OsMajorVersion, &OsMinorVersion, NULL, NULL); //根据不同的操作系统,定义跳过回环数据包的正确标识 if((OsMajorVersion == 5) && (OsMinorVersion == 0))
{ // Windows 2000 需要NDIS_FLAGS_DONT_LOOPBACK与
// NDIS_FLAGS_SKIP_LOOPBACK两个标识
g_SendPacketFlags = NDIS_FLAGS_DONT_LOOPBACK | NDIS_FLAGS_SKIP_LOOPBACK_W2K; } else
{ // Windows XP、Windows 2003与后续的操作系统
//只需要NDIS_FLAGS_DONT_LOOPBACK标识
g_SendPacketFlags = NDIS_FLAGS_DONT_LOOPBACK; }
/*初始化设备名称的前缀*/
NdisInitUnicodeString(&g_NPF_Prefix, g_NPF_PrefixBuffer);
/*获得CPU的个数,并保存该值*/ g_NCpu = NdisSystemProcessorCount();
/*零初化ProtocolChar结构体*/
RtlZeroMemory(&ProtocolChar,sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
/*向NDIS注册协议驱动程序*/
//用协议数据(版本、名称等)与回调函数地址设置ProtocolChar #ifdef NDIS50
ProtocolChar.MajorNdisVersion = 5; #else
ProtocolChar.MajorNdisVersion = 3; #endif
ProtocolChar.MinorNdisVersion = 0; ProtocolChar.Reserved = 0;
ProtocolChar.OpenAdapterCompleteHandler = NPF_OpenAdapterComplete; ProtocolChar.CloseAdapterCompleteHandler = NPF_CloseAdapterComplete; ProtocolChar.SendCompleteHandler = NPF_SendComplete;
ProtocolChar.TransferDataCompleteHandler = NPF_TransferDataComplete; ProtocolChar.ResetCompleteHandler = NPF_ResetComplete; ProtocolChar.RequestCompleteHandler = NPF_RequestComplete; ProtocolChar.ReceiveHandler = NPF_tap;
ProtocolChar.ReceiveCompleteHandler = NPF_ReceiveComplete; ProtocolChar.StatusHandler = NPF_Status;
ProtocolChar.StatusCompleteHandler = NPF_StatusComplete; #ifdef NDIS50
ProtocolChar.BindAdapterHandler = NPF_BindAdapter; ProtocolChar.UnbindAdapterHandler = NPF_UnbindAdapter; ProtocolChar.PnPEventHandler = NPF_PowerChange; ProtocolChar.ReceivePacketHandler = NULL; #endif
ProtocolChar.Name = ProtoName;
//把NPF注册为一个NDIS协议驱动程序 NdisRegisterProtocol( &Status,
&g_NdisProtocolHandle,