程序实现启用/禁用设备(驱动)enable/disable device with windows api

程序实现启用/禁用设备(驱动)enable/disable device with windows api

分类:  CPP2009-09-25 00:16  3914人阅读  评论(5)  收藏  举报
windows service database filter application function

目录(?)[+]

懒人也动笔了;2009-09-25

程序实现启用/禁用设备(驱动)

—— enable/disable device (driver) with windows api

需求:

    用程序实现类似设备管理器对设备驱动的控制(启用/禁用);


起初的尝试(失败):

     一开始使用的是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中讲到:

程序实现启用/禁用设备(驱动)enable/disable device with windows api_第1张图片 ��

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 ).

IIcThe 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都没有找到相关解释,无奈作罢;

 


进展——使用SetupDi×××API:

      使用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:

DICS_FLAG_GLOBAL
Make the change in all hardware profiles.
DICS_FLAG_CONFIGSPECIFIC
Make the change in the specified profile only.

结果:

成功实现功能;

遗憾:不明白为什么service不能控制wdm driver service;

你可能感兴趣的:(程序实现启用/禁用设备(驱动)enable/disable device with windows api)