1. 程式人生 > >VC++實現啟用與停用裝置

VC++實現啟用與停用裝置

前些日子在研究USB裝置,就順便研究了一些如何在應用程式實現USB裝置的啟用與停用,然後稍微深入,於是有了本文。

想要實現類似裝置管理器的功能,其實也不是很難,無非就是呼叫一些API函式,就像本文描述的,採用的API函式就是SetupDi系列的函式。不過這類函式有很多,具體的請參見MSDN,而實現裝置啟用、停用僅需要用到的就只有5個函式:
       SetupDiGetClassDevs                   // 獲取裝置資訊集
       SetupDiEnumDeviceInfo                 // 從裝置資訊集中列舉每個裝置的具體資訊
       SetupDiGetDeviceRegistryProperty      // 從登錄檔中讀取PnP裝置的屬性
       SetupDiSetClassInstallParams          // 設定(包括取消)裝置類的安裝引數
       SetupDiCallClassInstaller             // 安裝指定裝置
以上函式均在setupapi.h標頭檔案中宣告,該標頭檔案包含在setupapi.lib函式庫中(使用以上函式前需要宣告這個標頭檔案)。

接下來就是如何實現裝置的啟用與停用。
從原理上講,裝置的啟用與停用其實就是對該裝置進行重安裝。

首先,我們需要宣告兩個變數用來儲存指定裝置類的屬性資訊:
    HDEVINFO        m_hDevInfo;              // 類似裝置控制代碼,以下暫且稱為裝置控制代碼
    SP_DEVINFO_DATA m_DeviceInfoData;        // 裝置詳細屬性資訊

然後呼叫SetupDiGetClassDevs函式獲取裝置控制代碼的值。(在這個函式中,需要指定裝置類的GUID,如果不清楚這個GUID,可以在相應的安裝檔案.inf中查詢。注意:

裝置類的GUID,不是裝置的GUID!)

接著迴圈使用SetupDiEnumDeviceInfo函式列舉對應裝置類中的裝置,並使用SetupDiGetDeviceRegistryProperty函式獲取得到的裝置的詳細資訊,進行判斷是否為所需的裝置(判斷的方式有多種,具體參考MSDN,本文采用裝置描述進行判斷)。
一旦列舉結束(即列舉不成功,而且用GetLastError()可以得到錯誤碼259)即可退出迴圈。當然如果找到裝置,即可break退出。

如果找到對應的裝置,就呼叫SetupDiSetClassInstallParams函式設定安裝的屬性。這裡有個注意的地方需要詳細說明一下:
SetupDiSetClassInstallParams的函式原型如下:
WINSETUPAPI BOOL WINAPI
  SetupDiSetClassInstallParams(
    IN HDEVINFO  DeviceInfoSet,
    IN PSP_DEVINFO_DATA  DeviceInfoData,  OPTIONAL
    IN PSP_CLASSINSTALL_HEADER  ClassInstallParams,  OPTIONAL
    IN DWORD  ClassInstallParamsSize
    );
注意第三個引數PSP_CLASSINSTALL_HEADER  ClassInstallParams,
這裡我們不採用這個結構,而是採用另外一個結構:SP_PROPCHANGE_PARAMS
並在這個結構中,
設定ClassInstallHeader欄位中(我們發現這個欄位也是一個結構,就是PSP_CLASSINSTALL_HEADER結構)的InstallFunction欄位值為DIF_PROPERTYCHANGE,
設定StateChange值為DICS_ENABLE(該值為啟用,若是停用則為DICS_DISABLE)
然後採用強行轉換將其轉為PSP_CLASSINSTALL_HEADER結構。

最後,呼叫SetupDiCallClassInstaller函式執行裝置的安裝(即:啟用或者停用),注意該函式第一個引數值應為DIF_PROPERTYCHANGE。
從裝置管理器中,可以驗證我們的做法。

以下貼出我程式中的主要原始碼。

  BOOL               rlt            = FALSE;
  CString            csFriendlyName = myDevcieName;                           // 請修改該值
  GUID               devGUID        = {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}; // 這個值不用我再說了吧?!
  HDEVINFO           m_hDevInfo     = INVALID_HANDLE_VALUE;
  SP_DEVINFO_DATA    m_DeviceInfoData;

  RtlZeroMemory(&m_DeviceInfoData, sizeof(SP_DEVINFO_DATA));                  // 初始化m_DeviceInfoData,當然這只是範例,
  m_DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);                          // 有其他初始化的方法,大家見仁見智

  m_hDevInfo = SetupDiGetClassDevs(&devGUID,0,0,DIGCF_PRESENT );
 
  for (DWORD i = 0; SetupDiEnumDeviceInfo ( m_hDevInfo, i, &m_DeviceInfoData ); i++ )
  {
         DWORD DataT;
         LPTSTR buffer  = NULL;
         DWORD buffersize = 0;
        
         while ( !SetupDiGetDeviceRegistryProperty
              (
                   m_hDevInfo,
                   &m_DeviceInfoData,
                   SPDRP_DEVICEDESC,
                   &DataT,
                   (PBYTE)buffer,
                   buffersize,
                   &buffersize
         ) )
        {
            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
            {
                 // Change the buffer size.
                 if (buffer) 
                 {
                       LocalFree(buffer);
                 }
                 buffer = (char*)LocalAlloc(LPTR,buffersize);
            }
            else
            {
                 break;
            }
        }

        if ( ( buffer != NULL ) && ( csFriendlyName.Find ( buffer, 0 ) != ( -1 ) ) )
        {
            if (buffer) 
            {
                 LocalFree(buffer);
            }
            break;
        }
                
        if (buffer) 
        {
            LocalFree(buffer);
        }
  }

  SP_PROPCHANGE_PARAMS propChange = { sizeof ( SP_CLASSINSTALL_HEADER ) };
  propChange.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
  propChange.Scope = DICS_FLAG_GLOBAL;
  propChange.StateChange = DICS_ENABLE;   // 啟用,或是停用,請使用DICS_DISABLE

  if (m_DeviceInfoData.DevInst != NULL)
  {
        rlt = SetupDiSetClassInstallParams
        (      
                m_hDevInfo, 
                &m_DeviceInfoData, 
                ( SP_CLASSINSTALL_HEADER * ) &propChange, 
                sizeof ( propChange ) 
        );
  }

  if ( rlt )
  {
        rlt = SetupDiCallClassInstaller ( DIF_PROPERTYCHANGE, m_hDevInfo, &m_DeviceInfoData );
  }