目录(?)[+]
懒人也动笔了;2009-09-25
用程序实现类似设备管理器对设备驱动的控制(启用/禁用);
一开始使用的是service api 重启驱动对应的,即使用”ControlService“向服务发送SERVICE_CONTROL_STOP消息停止服务,
再用“StartService"重新启动服务即可;
服务的启动、停止源代码可搜索 ”service control program“或"svccontrol.cpp" 找到微软的msdn sample;
如果链接地址没变的话是:http://msdn.microsoft.com/en-us/library/bb540474(VS.85).aspx
对于一些普通的驱动,该方法可以奏效,但是对于我们的设备驱动却失败了;
于是先使用命令行 sc 手动停止驱动服务,
不想同样失败,得到的提示信息是:
ControlService Failed 1052:
The request is not valid for this service
上网搜索后找到解释为:
You cannot stop the WDM drivers using ControlService , even on 32bit [http://www.codecomments.com/archive421-2006-5-938507.html]
我们的设备是用wdf写的;
另外还发现了为什么ControlServcie可以控制驱动:http://www.reverse-engineering.info/SystemCoding/SkeletonKMD_Tutorial.htm Kernel Mode Driver Tutorial中讲到:
The DriverUnload routine is pretty self explanatory.� It will be called when the loader invokes theControlService API with a SERVICE_CONTROL_STOP message. DriverUnload basically unwinds the actions taken by the DriverEntry function.� It will perform any driver specific cleanup and is at a minimum required to delete the device object (IoDeleteDevice ) and the symbolic link (IoDeleteSymbolicLink ).� Failure to perform either of these actions will corrupt the internal service database entry for the driver preventing it from being loaded again (until you reboot L ).
IIc. The Loader
If you have Driver Studio, you can use the handy little utitlity �Driver Monitor� to load, start, stop, and unload drivers for testing purposes. However, if your driver must provide services to a ring 3 application will, the ring 3 application will will manually have to install the driver as a service in order to be able to communicate with it.� The procedure for loading a driver is as follows:
1. Obtain a handle to the service manager using OpenSCManager .
2. Define the driver using CreateService . This creates an entry in the service database for the driver.� Note, that this information is kept in the registry under...HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/"My Driver's Name".� Realize, that CreateService does not *load* the driver.� It simply creates an entry in the service database so that it can be recognized.
3. Open the driver using OpenService .� This call loads the driver, but realize that this call does not *start* the driver.
4. Finally start the driver using the StartService function.� Note that this function is what finally calls the DriverEntry routine.� If there are errors in your DriverEntry routine, expect StartService to fail.
5. Now you can open a handle to the driver in a ring 3 application using CreateFile and send requests via theDeviceIoControl .
6. Stop the driver by sending a SERVICE_CONTROL_STOP via ControlService .� Note that this function calls the DriverUnload routine.� If there are errors in your DriverUnload routine, expect this function to fail and expect to be unable to load the driver again unless you reboot.� This is because if this call fails, or the driver crashes before executing this call, the service database entry for the driver will be corrupted until you reboot.
7. Finally delete the service using DeleteService and release the handle to the service control database usingCloseServiceHandle .� DeleteService is the function responsible for deleting the actual entry in the service database and the corresponding key in the registry.
上诉讲到了整个驱动的加载过程;但是为什么service不能卸载wdm驱动? 尽管翻找了半天 msdn上的WDK和service都没有找到相关解释,无奈作罢;
使用ControlService失败,遂寻找其他办法;
后发现使用devcon 可以 enable/disable驱动程序,其效果和 Device Manager的一样,
而devcon在ddk中有源码,遂参照devcon中代码实现了程序 enable/disable驱动;
关键函数代码如下:
// EnumAndControlDevice:
// enum the device whose device id matched the path_filter and control the device with control_code;
// path_filter: the filter string to enum the device
// control_code: can be DICS_ENABLE/DICS_DISABLE/DICS_PROPCHANGE/DICS_START/DICS_STOP which indicating a change in a device's state
// here use DICS_ENABLE to enable, DICS_DISABLE to disable and DICS_PROPCHANGE to restart the device;
int EnumAndControlDevice(const TCHAR * path_filter, int control_code)
{
int ret = 0;
GUID Guid;// = GUID_DEVCLASS_HIDCLASS;
DWORD size = 0;
::SetupDiClassGuidsFromNameEx (_T("hidclass"), &Guid, 1, &size, NULL, NULL); //这里我们的设备是”hidclass"设备
HDEVINFO info;
info=SetupDiGetClassDevs (&Guid, NULL, NULL, DIGCF_PRESENT); // 枚举已存在(DIGCF_PRESENT)的hidclass类型设备
if (info!=INVALID_HANDLE_VALUE)
{
DWORD devIndex = 0;
SP_DEVINFO_DATA did;
did.cbSize = sizeof(SP_DEVINFO_DATA);
for (devIndex=0;SetupDiEnumDeviceInfo (info,devIndex, &did);++devIndex) //枚举设备信息
{
TCHAR deviceId[1024] = {0};
DWORD requiredSize = 0;
if(SetupDiGetDeviceInstanceId (info, &did,deviceId, sizeof(deviceId), &requiredSize))
{
LOG_INFO((debuginfo, "device:%s/n", TString::to_string(deviceId).c_str()));
// Add the link to the list of all DFU devices
if( (_tcsstr(deviceId, path_filter) != NULL) )
{
LOG_INFO((debuginfo, "find the device with path:%s/n", TString::to_string(path_filter).c_str()));
int tmp_ret = ControlDevice (info,&did, control_code);
if(tmp_ret != 0){
ret = tmp_ret;
}
}
}
}
SetupDiDestroyDeviceInfoList (info);
}
return ret;
}
ControlDevice:改自devcon-》cmds.cpp [ControlCallback]
int ControlDevice(HDEVINFO Devs, PSP_DEVINFO_DATA DevInfo,int controlCode)
/*++
Routine Description:
Callback for use by Enable/Disable/Restart
Invokes DIF_PROPERTYCHANGE with correct parameters
uses SetupDiCallClassInstaller so cannot be done for remote devices
Don't use CM_xxx API's, they bypass class/co-installers and this is bad.
In Enable case, we try global first, and if still disabled, enable local
Arguments:
Devs )_ uniquely identify the device
DevInfo )
controlcode:
Return Value:
EXIT_xxxx
--*/
{
int ret = 0;
SP_PROPCHANGE_PARAMS pcp;
SP_DEVINSTALL_PARAMS devParams;
switch(controlCode) {
case DICS_ENABLE:
//
// enable both on global and config-specific profile
// do global first and see if that succeeded in enabling the device
// (global enable doesn't mark reboot required if device is still
// disabled on current config whereas vice-versa isn't true)
//
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
pcp.StateChange = controlCode;
pcp.Scope = DICS_FLAG_GLOBAL;
pcp.HwProfile = 0;
//
// don't worry if this fails, we'll get an error when we try config-
// specific.
if(SetupDiSetClassInstallParams (Devs,DevInfo,&pcp.ClassInstallHeader,sizeof(pcp))) {
SetupDiCallClassInstaller (DIF_PROPERTYCHANGE,Devs,DevInfo);
}
//
// now enable on config-specific
//
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
pcp.StateChange = controlCode;
pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
pcp.HwProfile = 0;
break;
default:
//
// operate on config-specific profile
//
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
pcp.StateChange = controlCode;
pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
pcp.HwProfile = 0;
break;
}
if(!SetupDiSetClassInstallParams (Devs,DevInfo,&pcp.ClassInstallHeader,sizeof(pcp)) ||
!SetupDiCallClassInstaller (DIF_PROPERTYCHANGE ,Devs,DevInfo)) {
//
// failed to invoke DIF_PROPERTYCHANGE
//
ret = ::GetLastError();
LOG_INFO((debuginfo, " fail to invoke DIF_PROPERTYCHANGE:%d/n", ret));
} else {
//
// see if device needs reboot
//
devParams.cbSize = sizeof(devParams);
if(SetupDiGetDeviceInstallParams (Devs,DevInfo,&devParams) && (devParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT))) {
// need to reboot
LOG_INFO((debuginfo, " need to reboot/n"));
} else {
//
// appears to have succeeded
//
LOG_INFO((debuginfo, " restart driver succeed/n"));
}
}
return ret;
}
补充:
关于hardware profiles: 参考 http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/hardware_profiles_overview.mspx?mfr=true
另外参考WDK——》Device Installation: SP_PROPCHANGE_PARAMS
Flags that specify the scope of a device property change. Can be one of the following:
成功实现功能;
遗憾:不明白为什么service不能控制wdm driver service;