程序實現啓用/禁用設備(驅動)enable/disable device with windows api

懶人也動筆了;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中講到:

��

The DriverUnload routine is pretty self explanatory.� It will be called when the loader invokes the ControlService 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 the DeviceIoControl .

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 using CloseServiceHandle .� 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;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章