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中查詢。注意:
接著迴圈使用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 );
}