收藏 分销(赏)

通过设备接口打开设备的步骤.docx

上传人:pc****0 文档编号:8766431 上传时间:2025-03-01 格式:DOCX 页数:14 大小:176.76KB
下载 相关 举报
通过设备接口打开设备的步骤.docx_第1页
第1页 / 共14页
通过设备接口打开设备的步骤.docx_第2页
第2页 / 共14页
点击查看更多>>
资源描述
通过设备接口打开设备 详细步骤 一、驱动程序 1.驱动程序框架的创建 (1)用VC建立一个新工程。在VC IDE环境中选择File|New,弹出New对话框。在对话框中,选择Project选项卡。在Project选项卡中,选择Win32 Application 。设置工程名为OpenGuid.如图1所示,单击OK,进入下一个对话框,在对话框中选择一个空的工程。如图2。 图1 图2 (2).新建两个文件GuidOpen.h和GuidOpen.cpp.这两个文件的具体写法,详见程序编写。也可以直接添加现成的已经写好的文件,张帆这本书中,一般都是用的HelloWDM.h和HelloWDM.cpp. (3).增加新的编译版本,去掉Debug和Release版本。在Build | Configuration如图3和图4。 图3 图4 (4).修改工程属性。选择Project|Setting,在弹出的对话框中,选择General选项卡,将Intermediate files和Output files改为MyDriver_Check,这个名字英语C/C++中,所设置的Fo和Fd后面的文件名相一致。如图5。 图5 将C/C++选项卡中,原有的Project Options内容全部删掉,换成一下内容。 /nologo /Gz /MLd /W3 /WX /Z7 /Od /D WIN32=100 /D_X86_=1 /D WINVER=0x500 /D DBG=1 /Fo"MyDriver_Check/" /Fd"MyDriver_Check/" /FD /c 其中: /nologo:表示不显示编译的版本信息 /Gz:默认函数调用采用标准调用(_stdcall) /MLd /W3:采用第三级警告模式 /WX:将警告信息转换为错误信息,最大程度保证代码可靠 /Z7:用Z7模式产生调试信息? /Od:关闭调试模式,VC的调试命令不能调试内核下的程序 /D WIN32=100 /D_X86_=1 /D WINVER=0x500 /D DBG=1:定义4个宏(不知道为什么) /Fo"MyDriver_Check/:MyDriver_Check/为Output Directories中“创建”的文件夹,存放中间生成的目标代码路径 /Fd"MyDriver_Check/": MyDriver_Check/为存放.PDB文件的文件夹 /FD:生成文件依奈 /c:只进行编译,不连接 图6 选择Link选项卡,将原有的Project Options 内容全部删除,替换成如下内容: wdm.lib /nologo /base:"0x10000" /stack:0x400000,0x1000 /entry:"DriverEntry" /subsystem:console /incremental:no /pdb:"MyDriver_Check/GuidOpen.pdb" /debug /machine:I386 /nodefaultlib /out:"MyDriver_Check/GuiOpen.sys" /pdbtype:sept /subsystem:native /driver /SECTION:INIT,D /IGNORE:4078 其中: wdm.lib:链接WDM库 /nologo:链接时不显示版本信息 /base:"0x10000":加载驱动时,设定加载到虚拟内存的地址 /stack:0x400000,0x1000:设定函数使用堆栈的地址与大小 /entry:"DriverEntry":入口函数的地址(为符合标准函数调用的) /subsystem:console:设置子系统 /incremental:no:非递曾式链接 /pdb:"MyDriver_Check/GuidOpen.pdb":设置pdb文件的文件名为GuidOpen,保存于MyDriver_Check文件夹下面C/C++属性页中的设置一样。 /debug:以Debug方式链接 /machine:I386:产生代码为386兼容的平台下的 /nodefaultlib:不使用默认的库 /out:"MyDriver_Check/GuidOpen.sys":输出2进制的代码的文件名,保存于MyDriver_Check文件夹下与C/C++属性页中的设置一样。 /pdbtype:sept:设置pdb文件的类型 /subsystem:native:子系统为内核系统 /driver:编译驱动 /SECTION:INIT,D:将INIT的段设置为可抛弃的 /IGNORE:4078:忽略4078号警告错误 图7 (5).修改VC的lib目录和include 目录。 Tools->Options->Directories属性页下的 Show directories for 切换到Includefie 添加DDK的头文件 (安装的ddk的目录文件夹)\Inc\w2k (ddk的目录文件夹)\Inc\ddk\wdm\w2k 置于最上面 添加库文件 (安装的ddk的目录文件夹)\lib\w2k\i386 置于最上面 2.驱动程序说明 (1)重要驱动程序中重要的数据结构 驱动对象(DRIVER_OBJECT)在驱动加载时被内核中的对象管理程序所创建,由内核中的I/O管理器负责加载。 typedef struct _DRIVER_OBJECT{ CSHORT Type; CSHORT Size; PDEVICE_OBJECT DeviceObject; ULONG Flags; PVOID DriverStart; ULONG DriverSize; PVOID DriverSection; PDRIVER_EXTENSION DriverExtension; UNICODE_STRING DriverName; PUNICODE_STRING HardwareDatabase; PFAST_IO_DISPATCH FastIoDispatch; PDRIVER_INITIALIZE DriverInit; PDRIVER_STARTIO DriverStartIo; PDRIVER_UNLOAD DriverUnload; PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION+1]; } DRIVER_OBJECT; typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT; DeviceObject:每个驱动程序会有一个或多个设备对象。设备对象是由程序员自己创建的,而非操作系统完成,在驱动被卸载时,遍历每个设备对象,并将其删除。 设备对象(DEVICE_OBJECT) typedef struct _DEVICE_OBJECT{ struct _DRIVER_OBJECT *DriverObject; struct _DEVICE_OBJECT *NextDevice; struct _DEVICE_OBJECT *AttachedDevice; struct _IRP *CurrentIrp; ULONG Flags; struct _DEVOBJ_EXTENSION *DeviceObjectExtension; ...................... } DEVICE_OBJECT; typedef struct _DEVICE_OBJECT *PDEVICE_OBJECT;//ntndis 设备扩展 是由程序员制定内容和大小,由I/O管理器创建的,并且保存在非分页内存中。 (2)WDM驱动程序基本结构,在WDM驱动程序中,完成一个设备操作,至少需要两个设备对象共同完成,一个是物理设备对象(Physical Device Object)PDO和功能设备对象(Function Device Object)FDO。 当PC插入一个设备时,PDO会自动创建。确切的说是由总线驱动创建的,PDO不能单独操作设备,需要配合FDO一起使用。系统会检测到新设备,要求安装驱动程序,需要安装的驱动程序指的就是WDM程序,此驱动程序负责创建FDO,并且附加到PDO上。 (3)驱动程序分析 头文件中,除了生命函数之外,还要定义一个设备扩展。 typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT fdo; PDEVICE_OBJECT NextStackDevice; UNICODE_STRING interfaceName; //设备接口 } DEVICE_EXTENSION, *PDEVICE_EXTENSION; 当驱动程序被加载时,首先进入DriverEntry函数。DriverEntry主要是对驱动程序进行初始化,它是由系统进程所调用的,在Windows中有个特殊的进程叫做系统进程,打开进程管理器,里面有一个名为System的进程就是系统进程。系统进程在系统启动的时候就被创建了。驱动加载时,系统进程启动新的线程,调用执行体组建中的对象管理器,创建一个驱动对象。这个驱动对象是一个DRIVER_OBJECT的结构体。另外,系统进程调用执行体组建中的配置管理器程序,查询词驱动程序对应的注册表项。 DriverEntry函数由两个参数PDRIVER_OBJECT pDeriverObject 是刚才被创建的驱动对象的指针,和PUNICODE_STRING pRegistryPath,设备服务键的的键名字符串的指针。在这个函数中,主要是对系统进程创建的驱动对象进行初始化。 DriverEntry函数中,它对驱动对象的初始化一般是对例程的设置,卸载例程,AddDevice和IRP派遣函数。具体代码如下: pDriverObject->DriverExtension->AddDevice = GuidOpenAddDevice; pDriverObject->MajorFunction[IRP_MJ_PNP] = GuidOpenPnp; pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = pDriverObject->MajorFunction[IRP_MJ_CREATE] = pDriverObject->MajorFunction[IRP_MJ_CLOSE] = pDriverObject->MajorFunction[IRP_MJ_READ] = pDriverObject->MajorFunction[IRP_MJ_WRITE] = GuidOpenDispatchRoutine; pDriverObject->DriverUnload = GuidOpenUnload; 另外,设备服务键的键名有时候需要保存下来,因为这个字符串不是长期存在的,如果以后想使用这个UNICODE 字符串,就必须先把它复制到安全的地方,这个字符串的内容一般是\REGISTRY\MACHINE\SYSTEM\ControlSet\Service\[服务名].DriverEntry的返回值是NTSTATUS,是被定义的为32位的无符号长整形。0~0X7FFFFFFF被认为是正确的。而0X80000000~0XFFFFFFFF被认为是错误的。 接着驱动程序进入GuidOpenAddDevice例程。它的主要任务是创建设备对象(功能设备对象)并将其附加到PDO之上。它有两个参数 PDRIVER_OBJECT DriverObject,驱动对象和PDEVICE_OBJECT PhysicalDeviceObject 设备对象,就是底层总线驱动创建的PDO对象。 创建设备对象,用IoCreatDevice( IN PDRIVER_OBJECT DriverObject,//系统创建的驱动对象 IN ULONG DeviceExtensionSize,//程序员自己定义的(在头文件中)设备扩展 的大小 IN PUNICODE_STRING DeviceName OPTIONAL,//设备名,可以为空,此时,I/O管理器会自动以一个数字作为该设备对象的名称 IN DEVICE_TYPE DeviceType,//设备类型 IN ULONG DeviceCharacteristics,//对设备的进一步描述 IN BOOLEAN Exclusize,//是否一次只能进行一次IRP处理 OUT PDEVICE_OBJECT *DeviceObject)//新创建的设备对象 具体代码如下 PDEVICE_OBJECT fdo; status = IoCreateDevice( DriverObject, sizeof(DEVICE_EXTENSION), NULL,//没有指定设备名 FILE_DEVICE_UNKNOWN, 0, FALSE, &fdo); 此时,功能设备对象,创建完毕,接着是将,功能设备对象FDO附加到物理设备对象上PDO。利用函数PDEVICE_OBJECT IoAttachDeviceToDeviceStack( IN PDEVICE_OBJECT SourceDevice,//新创建的功能设备对象FDO IN PDEVICE_OBJECT TargetDevice//物理设备对象PDO ); 该函数调用成功的话返回一个设备对象,附加设备对象的设备对象,例如在这里,返回的是PDO,如果,The returned device object pointer can differ from TargetDevice if TargetDevice had additional drivers layered on top of it.如果该函数运行失败,则返回NULL。 具体程序,我们将该函数返回的设备对象,保存在,设备扩展中,这样一来,我们就需要先得到新创建的功能设备对象的设备扩展。 //得到设备扩展 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension; pdx->fdo = fdo; //将FDO附加到PDO上 pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject); WDM驱动程序,设备名无法被用户模式下的应用程序查询到,应用程序可以通过符号链接,设备名或者是设备接口来访问设备。本文只介绍设备接口方式。设备接口就是一组全局标志,他是一个128位组成的数字,并能保证在全世界范围内不会冲突。VC中有一个创建GUID的工具,叫guidgen.exe,它在D:\Program Files\Microsoft Visual Studio\Common\Tools中,运行它,它为用户提供了四种方式产生guid,其实它们都是128位的,只是输出的形式不同而已,一般选择第二种,单击New GUID会产生新的的guid,单击Copy将这个guid复制到新建的guid.h头文件中。 DEFINE_GUID(<<name>>, 0x5dada759, 0xde9a, 0x45e2, 0x8f, 0xb4, 0x1a, 0xa8, 0x8b, 0x1d, 0xe7, 0x8); 需要将《name》换成自己为这个接口而起的名字,例如MY_WDM_DEVICE.另外需要注意,在创建guid时,程序中应该包含头文件#include <initguid.h>,其中initguid.h只能在一个.cpp中包含, DEFINE_GUID()宏定义了,如果包含了一个initguid.h,那么定义一个guid, 如果没有包含一个initguid.h ,则定义一个extern guid指向定义的那个guid, 所以项目中必须有一个文件.cpp包含initguid.h,否则会出错。但如果包含了多个的initguid.h,也会出错否则会出现错误。 unresolved external symbol _MY_WDM_DEVICE 创建设备接口用函数NTSTATUS IoRegisterDeviceInterface( IN PDEVICE_OBJECT PhysicalDeviceObject, IN CONST GUID *InterfaceClassGuid, IN PUNICODE_STRING ReferenceString OPTIONAL, OUT PUNICODE_STRING SymbolicLinkName//将GUID输出一串UNICODE字符串 ); 具体代码创建设备接口 status = IoRegisterDeviceInterface(PhysicalDeviceObject, &MY_WDM_DEVICE, NULL, &pdx->interfaceName); 其中pdx->interfaceName就是暴露给应用程序的符号链接。包括四部分如图8 图8 (1)何种总线设备,例如ROOT (2)类设备的名称,LIUYOUJINDEVICE (3)这种设备的第几个设备#0000 (4)制定的设备接口GUID。 设置接口 IoSetDeviceInterfaceState(&pdx->interfaceName, TRUE); //设置操作模式 fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; fdo->Flags &= ~DO_DEVICE_INITIALIZING; 实现即插即用 即插即用IRP即IRP_MJ_PNP,它一般是由即插即用管理器发送给WDM驱动程序的。不同情况下,即插即用管理器会发送不同子类型的IRP_MJ_PNP IRP。在IRP_MJ_PNP派遣函数中要处理不同子功能代码的IRP,本程序采用函数指针的方法。首先初始化一个函数指针组成的数组,然后在派遣函数中判断是那种子功能代码。根据这个子功能代码区寻找行的函数指针,再通过指针找到针对具体子功能代码所作的操作函数。加载驱动时,所用到的各个IRP_MJ_PNP子功能代码。 1.953 Default Enter DefaultPnpHandler 11.953 Default Leave DefaultPnpHandler 11.953 Default Enter GuidOpenPnp 11.953 Default PNP Request (IRP_MN_FILTER_RESOURCE_REQUIREMENTS)修改I/O资源需求列表 11.953 Default Enter DefaultPnpHandler 11.953 Default Leave DefaultPnpHandler 11.953 Default Leave GuidOpenPnp 11.953 Default Enter GuidOpenPnp 11.953 Default PNP Request (IRP_MN_START_DEVICE)配置并初始化设备 11.953 Default Enter HandleStartDevice 11.953 Default Enter DefaultPnpHandler 11.953 Default Leave DefaultPnpHandler 11.953 Default Leave HandleStartDevice 11.953 Default Leave GuidOpenPnp 11.953 Default Enter GuidOpenPnp 11.953 Default PNP Request (IRP_MN_QUERY_CAPABILITIES)取设备能力 11.953 Default Enter DefaultPnpHandler 11.953 Default Leave DefaultPnpHandler 11.953 Default Leave GuidOpenPnp 11.953 Default Enter GuidOpenPnp 11.953 Default PNP Request (IRP_MN_QUERY_PNP_DEVICE_STATE)取设备状态 11.953 Default Enter DefaultPnpHandler 11.953 Default Leave DefaultPnpHandler 11.953 Default Leave GuidOpenPnp 11.953 Default Enter GuidOpenPnp 11.953 Default PNP Request (IRP_MN_QUERY_DEVICE_RELATIONS)给出与制定特征相关的设备列表 11.953 Default Enter DefaultPnpHandler 11.953 Default Leave DefaultPnpHandler 11.953 Default Leave GuidOpenPnp 对IRP_MN_DEVICE的处理 3.应用程序说明 创建一个对话框类型的驱动程序框架。 (1)首先需要将驱动程序中的guid.h文件copy到应用程序中,并且添加到应用程序的工程里。 (2)应用程序的Porject|Setting|Link的Object/library modules里要添加setupapi.lib库,否则会有类似这样的错误:GuidOpen_AppDlg.obj :unresolved external symbol __imp__SetupDiGetDeviceInterfaceDetailA@24 GuidOpen_AppDlg.obj : error LNK2001: unresolved external symbol __imp__SetupDiDestroyDeviceInfoList@4 GuidOpen_AppDlg.obj : error LNK2001: unresolved external symbol __imp__SetupDiEnumDeviceInterfaces@20 GuidOpen_AppDlg.obj : error LNK2001: unresolved external symbol __imp__SetupDiGetClassDevsA@16 程序中应该包含setupapi.h头文件,否则,会出现这样的错误: error C2065: 'HDEVINFO' : undeclared identifier error C2146: syntax error : missing ';' before identifier 'info' error C2065: 'info' : undeclared identifier error C2065: 'SetupDiGetClassDevs' : undeclared identifier error C2065: 'DIGCF_PRESENT' : undeclared identifier error C2065: 'DIGCF_INTERFACEDEVICE' : undeclared identifier ............................................................................................................................... 同时还必须包含的一个头文件是#include <initguid.h> 打开设备的代码如下: void CGuidOpen_AppDlg::OnOPENDEVICE() { // TODO: Add your control notification handler code here HANDLE hDevice = GetDeviceViaInterface((LPGUID)&MY_WDM_DEVICE,0); if (hDevice == INVALID_HANDLE_VALUE) { MessageBox("打开设备出错!"); // return 1; } MessageBox("设备打开成功"); CloseHandle(hDevice); // return 0; } HANDLE CGuidOpen_AppDlg::GetDeviceViaInterface(GUID *pGuid, DWORD instance) { //获得类信息 HDEVINFO info = SetupDiGetClassDevs(pGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE); if(info==INVALID_HANDLE_VALUE) { MessageBox("No HDEVINFO available for this GUID"); return NULL; } // Get interface data for the requested instance SP_INTERFACE_DEVICE_DATA ifdata; ifdata.cbSize = sizeof(ifdata); if(!SetupDiEnumDeviceInterfaces(info, NULL, pGuid, instance, &ifdata)) { MessageBox("No SP_INTERFACE_DEVICE_DATA available for this GUID instance"); SetupDiDestroyDeviceInfoList(info); return NULL; } // Get size of symbolic link name DWORD ReqLen; SetupDiGetDeviceInterfaceDetail(info, &ifdata, NULL, 0, &ReqLen, NULL); PSP_INTERFACE_DEVICE_DETAIL_DATA ifDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA)(new char[ReqLen]); if( ifDetail==NULL) { SetupDiDestroyDeviceInfoList(info); return NULL; } // Get symbolic link name ifDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA); if( !SetupDiGetDeviceInterfaceDetail(info, &ifdata, ifDetail, ReqLen, NULL, NULL)) { SetupDiDestroyDeviceInfoList(info); delete ifDetail; return NULL; } // printf("Symbolic link is %s\n",ifDetail->DevicePath); // Open file HANDLE rv = CreateFile( ifDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if( rv==INVALID_HANDLE_VALUE) rv = NULL; delete ifDetail; SetupDiDestroyDeviceInfoList(info); return rv; } 函数解析: WINSETUPAPI BOOL WINAPI SetupDiGetDeviceInterfaceDetail( IN HDEVINFO  DeviceInfoSet, IN PSP_DEVICE_INTERFACE_DATA  DeviceInterfaceData, OUT PSP_DEVICE_INTERFACE_DETAIL_DATA  DeviceInterfaceDetailData..OPTIONAL, IN DWORD  DeviceInterfaceDetailDataSize, OUT PDWORD  RequiredSize..OPTIONAL, OUT PSP_DEVINFO_DATA  DeviceInfoData  OPTIONAL ); 参数 DeviceInfoSet 指向设备信息集的指针,它包含了所要接收信息的接口。该句柄通常由SetupDiGetClassDevs函数返回。 DeviceInterfaceData 一个指向 SP_DEVICE_INTERFACE_DATA结构的指针,该结构指定了 DeviceInfoSet 参数中设备的接口。这个类型的指针通常由 SetupDiEnumDeviceInterfaces 函数返回。 DeviceInterfaceDetailData 一个指向SP_DEVICE_INTERFACE_DETAIL_DATA结构的指针,该结构用于接收指定接口的信息。该参数是可选的且可以为NULL。如果DeviceInterfaceDetailSize 参数为0,该参数必须为NULL。如果该参数被指定,主调者必须在调用该函数之前,设置 SP_DEVICE_INTERFACE_DETAIL_DATA 结构的 cbSize 成员为 sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA)。cbSize 成员总是包含数据结构的固定部分的长度。 DeviceInterfaceDetailDataSize DeviceInterfaceDetailData 参数指定的缓冲的大小。该缓冲的大小不能小于 (offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA, DevicePath) + sizeof(TCHAR)) 字节。 如果 DeviceInterfaceDetailData 参数为NULL,该参数必须为0. RequiredSize 一个指向变量的指针,该变量接收请求的 DeviceInterfaceDetailData 缓冲的大小。这个大小包含了结构的固定部分的大小再加上设备路径字符串的长度。该参数是可选的,也可以是NULL。 DeviceInfoData 一个指向缓冲的指针,该缓冲接收关于支持请求的接口的设备的信息。主调者必须设置 DeviceInfoData.cbSize 成员为 sizeof(SP_DEVINFO_DATA)。该参数是可选的,也可以为NULL。 返回值 如果函数顺利完成,则返回TRUE,如果有错误,则返回FALSE。 SetupDiGetDeviceInterfaceDetail函数用来传回另外一个与前一个函数所识别的接口有关的结构InterfaceClassGuid。包括设备的路径InterfaceClassGuid->DevicePath成员是一个设备路径。它可以作为应用程序打开设备时CreateFile的第一个参数。值得注意的是:第一次调用该函数时,其中的DeviceInterfaceDetailDataSize无法预知。故可以两次调用。第一次获取长度,第二次获取正确值。 WINSETUPAPI BOOL WINAPI SetupDiEnumDeviceInterfaces( IN HDEVINFO DeviceInfoSet,//指向设备信息集的指针,它包含了所要接收信息的接口。该句柄通常由SetupDiGetClassDevs函数返回。 IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL IN LPGUID InterfaceClassGuid, //指向GUID的指针 IN DWORD MemberIndex, OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData//The caller mus
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传
相似文档                                   自信AI助手自信AI助手

当前位置:首页 > 管理财经 > 管理学资料

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服