假定共有20个串口1,每个串口的设备名是 Serial数字
定义2个有20个设备对象指针的数组,一个保存绑定设备,一个保存被绑定设备
循环对20个设备对象进行open,然后创建设备对象并将其绑定到open后的设备对象所在的设备栈栈顶,并将原栈顶
设备对象存放到被绑定设备对象数组中
注意:创建的设备的一些特征要与被绑定设备一致. 这里创建的设备对象没有名字,一个驱动对象可以设置多个设备对象
涉及到的内核API:
NTSTATUS IoCreateDevice( _In_ PDRIVER_OBJECT DriverObject, _In_ ULONG DeviceExtensionSize, _In_opt_ PUNICODE_STRING DeviceName, _In_ DEVICE_TYPE DeviceType, _In_ ULONG DeviceCharacteristics, _In_ BOOLEAN Exclusive, _Out_ PDEVICE_OBJECT *DeviceObject );
PDEVICE_OBJECT IoAttachDeviceToDeviceStack( _In_ PDEVICE_OBJECT SourceDevice, _In_ PDEVICE_OBJECT TargetDevice );
NTSTATUS IoGetDeviceObjectPointer( _In_ PUNICODE_STRING ObjectName, _In_ ACCESS_MASK DesiredAccess, _Out_ PFILE_OBJECT *FileObject, _Out_ PDEVICE_OBJECT *DeviceObject );
VOID ObDereferenceObject( _In_ PVOID Object );
VOID PoStartNextPowerIrp( _Inout_ PIRP Irp );
VOID IoSkipCurrentIrpStackLocation( [in, out] PIRP Irp );
NTSTATUS IoCallDriver( _In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp );
PVOID MmGetSystemAddressForMdlSafe( [in] PMDL Mdl, [in] MM_PAGE_PRIORITY Priority );
VOID IoCompleteRequest( _In_ PIRP Irp, _In_ CCHAR PriorityBoost );
测试代码如下:
//__stdcall #include<ntddk.h> #include<ntstrsafe.h> #pragma code_seg("INT") #define AllNums 10 #ifndef SetFlag #define SetFlag(_F,_SF) ((_F) |= (_SF)) #endif #ifndef ClearFlag #define ClearFlag(_F,_SF) ((_F) &= ~(_SF)) #endif #define CCP_MAX_COM_ID 32 //用于计时 #define DELAY_ONE_MICROSECOND (-10) #define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000) #define DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000) PDEVICE_OBJECT sourceDevice[AllNums] = { 0 };//保存过滤设备 PDEVICE_OBJECT targetDevice[AllNums] = { 0 };//保存实际被绑定的设备 NTSTATUS MyAttachDevice(PDRIVER_OBJECT pDriverObject, PDEVICE_OBJECT pWantDevice, PDEVICE_OBJECT* ppSourceDevice, PDEVICE_OBJECT* ppTargetDevice) { /* 参数说明: PDRIVER_OBJECT pDriverObject 本驱动对象,用于创建设备对象用 PDEVICE_OBJECT pWantDevice, 想绑定的目标设备,这个设备不一定是最终被绑定的设备, 因为最终被绑定的设备是该设备所在的设备栈的栈顶的那个设备对象 PDEVICE_OBJECT* ppSourceDevice, 创建的设备对象,创建成功将它绑定到栈顶 PDEVICE_OBJECT* ppTargetDevice 保存实际被绑定的对象 */ NTSTATUS status; PDEVICE_OBJECT pTopDev = NULL; status = IoCreateDevice(pDriverObject, 0, 0, pWantDevice->DeviceType,0, 0, ppSourceDevice); if (status!=STATUS_SUCCESS) { DbgPrint("创建设备失败! status is %d ", status); return status; } else { // 拷贝重要标志位。固定的代码,复制即可 if (pWantDevice->Flags & DO_BUFFERED_IO) (*ppSourceDevice)->Flags |= DO_BUFFERED_IO; if (pWantDevice->Flags & DO_DIRECT_IO) (*ppSourceDevice)->Flags |= DO_DIRECT_IO; if (pWantDevice->Flags & DO_BUFFERED_IO) (*ppSourceDevice)->Flags |= DO_BUFFERED_IO; if (pWantDevice->Characteristics & FILE_DEVICE_SECURE_OPEN) (*ppSourceDevice)->Characteristics |= FILE_DEVICE_SECURE_OPEN; (*ppSourceDevice)->Flags |= DO_POWER_PAGABLE; pTopDev = IoAttachDeviceToDeviceStack(*ppSourceDevice, pWantDevice); if (!pTopDev) { DbgPrint("附加设备失败! "); //失败就删除创建的设备 IoDeleteDevice(*ppSourceDevice); *ppSourceDevice = NULL; status = STATUS_NO_SUCH_DEVICE; return status; } else { *ppTargetDevice = pTopDev; // 设置这个设备已经启动。固定代码直接复制 (*ppSourceDevice)->Flags = (*ppSourceDevice)->Flags & ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; } } } NTSTATUS MyOpenDevice(PUNICODE_STRING pDeviceName,PDEVICE_OBJECT* ppOutDeviceObject) { NTSTATUS status; PFILE_OBJECT fileObj = NULL; //不需要考虑ppOutDeviceObject是否获取成功,因为失败的话到时候自然用不到 status = IoGetDeviceObjectPointer(pDeviceName, FILE_ALL_ACCESS, &fileObj, ppOutDeviceObject); //因为文件对象参数必须提供,但是有用不到,所以获取成功后就将其解除引用 if (status == STATUS_SUCCESS) { ObDereferenceObject(fileObj); } return status; } VOID MyAttachAllComs(PDRIVER_OBJECT pDriverObject) { ULONG index = 0; UNICODE_STRING comName; WCHAR nameBuf[20] = { 0 }; PDEVICE_OBJECT pWantDevice = NULL; for (; index < AllNums; index++) { //初始化设备名 memset(nameBuf, 0, sizeof(WCHAR) * 20); RtlStringCchPrintfW(nameBuf, 20, L"\Device\Serial%d", index); RtlInitUnicodeString(&comName, nameBuf); MyOpenDevice(&comName, &pWantDevice); if (pWantDevice==NULL) { continue; } else { MyAttachDevice(pDriverObject, pWantDevice, &(sourceDevice[index]), &(targetDevice[index])); } } } NTSTATUS MyDisPatcher(PDEVICE_OBJECT pDeviceObject, PIRP pIrp) { NTSTATUS status; ULONG i, j; PIO_STACK_LOCATION pIrpStack=IoGetCurrentIrpStackLocation(pIrp); //因为创建了多个设备对象,而I/O管理器对当前设备栈发送irp,自然就先将irp发送到设备栈栈顶 //此时不一定是因为栈顶也就是本驱动对象负责的设备对象发送的irp,因为栈中任何一个设备对象 //都可能让I/O管理器发送irp过来. 只是irp要先到达设备栈顶,所以先要判断哪个设备对象的irp for (i = 0; i < AllNums; i++) { if (pDeviceObject==sourceDevice[i]) { //可能有电源信号发过来,什么都不干 if (pIrpStack->MajorFunction==IRP_MJ_POWER) { PoStartNextPowerIrp(pIrp); IoSkipCurrentIrpStackLocation(pIrp); //因为实际被绑定的设备对象在本设备对象下面,直接将irp发给它 return IoCallDriver(targetDevice[i], pIrp); } //处理写操作,先获取写的内容长度.因为不知道要写的缓冲区在哪个参数里,可以一个一个试 if (pIrpStack->MajorFunction==IRP_MJ_WRITE) { ULONG len = pIrpStack->Parameters.Write.Length; PUCHAR buf; if (pIrp->MdlAddress!=NULL) { buf = (PUCHAR)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority); } else { buf = (PUCHAR)pIrp->UserBuffer; } if (!buf) { buf = (PUCHAR)pIrp->AssociatedIrp.SystemBuffer; } //输出内容 DbgPrint("要写的内容: "); for (j= 0; j < len; j++) { DbgPrint("%2x",buf[j]); } DbgPrint(" "); } //PoStartNextPowerIrp(pIrp); IoSkipCurrentIrpStackLocation(pIrp); //因为实际被绑定的设备对象在本设备对象下面,直接将irp发给它 return IoCallDriver(targetDevice[i], pIrp); } } pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return STATUS_UNSUCCESSFUL; } VOID unLoadDriver(PDRIVER_OBJECT pDriverObject) { ULONG index = 0; LARGE_INTEGER interval; //先解绑 for (; index < AllNums; index++) { if (targetDevice[index]) { IoDetachDevice(targetDevice[index]); } } //再等待5秒让其处理完irp interval.QuadPart = (5 * 1000 * DELAY_ONE_MILLISECOND); KeDelayExecutionThread(KernelMode, FALSE, &interval); //最后将生成的所有设备对象删除 for (index = 0; index < AllNums; index ++) { if (sourceDevice[index]) { IoDeleteDevice(sourceDevice[index]); } } } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING us) { ULONG i; for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { pDriverObject->MajorFunction[i] = MyDisPatcher; } pDriverObject->DriverUnload = unLoadDriver; MyAttachAllComs(pDriverObject); return STATUS_SUCCESS; }