针对windows下USB存储设备,主要分为检测是否已插入存储设备和实时插入检测.
实时插入检测: 主要是注册windows事件,或重写Qt的nativeEvent事件.这个比较简单,因为策略是定期检测,不需要实时插入检测.
检查存储设备是否已插入: 主要分为三个步骤:
1: 通过GetLogicDrives()获取所有盘符,然后逐一使用GetDriveType如果是盘符类型,如果是DRIVE_REMOVABLE,可移动设备.
代码:
bool ExistUsbStorageDevice() { #ifdef _WIN32 DWORD all_disk = GetLogicalDrives(); std::string disk_path; int disk_num = 0; while (all_disk) { if (1 == (all_disk & 0X1)) { disk_path.clear(); disk_path.push_back(static_cast<char>('A' disk_num)); disk_path.push_back(」; if (DRIVE_REMOVABLE == GetDriveType(disk_path.c_str())) { return true; } } all_disk = all_disk >> 1; disk_num; } return false; #else return false; #endif // _WIN32 }
然而,这一步只能检测到U盘,移动硬盘GetDriveType返回是DRIVE_FIXED,与本机盘符类型一致
2: 通过CreateFile如果是,打开设备,然后获取设备的总线类型BusTypeUsb,即YUSB总线.可以判断它是USB存储设备.
代码:
bool ExistUsbStorageDevice() { #ifdef _WIN32 DWORD all_disk = GetLogicalDrives(); std::string disk_path; int disk_num = 0; while (all_disk) { if (1 == (all_disk & 0X1)) { disk_path.clear(); disk_path = "\\\\.\\"; disk_path.push_back(static_cast<char>('A' disk_num)); disk_path.push_back(」; HANDLE hDevice = CreateFile(disk_path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL); PSTORAGE_DEVICE_DESCRIPTOR pDevDesc = (PSTORAGE_DEVICE_DESCRIPTOR)new BYTE[sizeof(STORAGE_DEVICE_DESCRIPTOR) 512 - 1]; pDevDesc->Size = sizeof(STORAGE_DEVICE_DESCRIPTOR) 512 - 1; if (GetDisksProperty(hDevice, pDevDesc)) { if (pDevDesc->BusType == BusTypeUsb) { return true; } } } all_disk = all_disk >> 1; disk_num; } return false; #else return false; #endif // _WIN32 }
这一步基本成功,但手机存储设备还有一个例外。.手机连接电脑后,便携式设备在设备管理器中的分类.得不到盘符.
3.没有实现在线信息的搜索C 检测手机设备.但查到了windows portable devices这个名词.于是在msdn搜了一下.看链接:查找设备
经过一番调试,简化了代码:
bool ExistPortableDevice() { #ifdef _WIN32 HRESULT init_result = CoInitialize(NULL); if (FAILED(init_result)) { return false; } IPortableDeviceManager* portable_device_manager; HRESULT hr = CoCreateInstance(CLSID_PortableDeviceManager, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&portable_device_manager)); if (FAILED(hr)) { return false; } bool exist = false; do { DWORD device_id_size; hr = portable_device_manager->GetDevices(NULL, &device_id_size); if (FAILED(hr)|| static_cast<int>(device_id_size) <= 0) { break; } exist = true; } while (0); if (SUCCEEDED(init_result)) { CoUninitialize(); } return exist; #else return false; #endif // _WIN32 }
以下是获得的设备信息描述,可用于代码调试,检查检测是否正确:
std::vector<std::string> GetPortableDevicesDescription(IPortableDeviceManager* portable_device_manager) { std::vector<std::string> devices_description; DWORD device_id_size; HRESULT hr = portable_device_manager->GetDevices(NULL, &device_id_size); if (FAILED(hr) || static_cast<int>(device_id_size) <= 0) { return devices_description; } PWSTR* device_ids = new (std::nothrow) PWSTR[device_id_size]; if (device_ids == NULL) { return devices_description; } do { hr = portable_device_manager->GetDevices(device_ids, &device_id_size); if (FAILED(hr)) { break; } DWORD device_index = 0; for (device_index = 0; device_index < device_id_size; device_index ) { DWORD describe_size = 0; hr = portable_device_manager->GetDeviceDescription(device_ids[device_index], NULL, &describe_size); if (FAILED(hr) || static_cast<int>(describe_size) <= 0) { break; } WCHAR* describe = new (std::nothrow) WCHAR[describe_size]; hr = portable_device_manager->GetDeviceDescription(device_ids[device_index], describe, &describe_size); if (SUCCEEDED(hr)) { devices_description.push_back(StringUtil::wstring2string(describe)); } } for (device_index = 0; device_index < device_id_size; device_index ) { CoTaskMemFree(device_ids[device_index]); device_ids[device_index] = NULL; } } while (0); delete[] device_ids; device_ids NULL;
return devices_description;
}
到这一步基本上已经能完整解决需求了,但想着是不是还能精益求精. 我的手机连接USB时,可选择仅充电,或者文件传输.这两种情况下都是会检测到USB存储,是否可以再进一步检测到这点.查看到检索设备支持的功能类别,存在存储类别.结合链接:便携式设备 COM API 示例3
实现代码:
#include <iostream>
#include <windows.h>
#include <portabledeviceapi.h>
#include <atlcomcli.h>
#include <PortableDevice.h>
#pragma comment(lib,"PortableDeviceGUIDs.lib")
#define SELECTION_BUFFER_SIZE 81
#define CLIENT_NAME L"WPD Sample Application"
#define CLIENT_MAJOR_VER 1
#define CLIENT_MINOR_VER 0
#define CLIENT_REVISION 2
bool SupportStorage(IPortableDevice* pDevice) {
HRESULT hr = S_OK;
CComPtr<IPortableDeviceCapabilities> pCapabilities;
CComPtr<IPortableDevicePropVariantCollection> pCategories;
DWORD dwNumCategories = 0;
// Get an IPortableDeviceCapabilities interface from the IPortableDevice interface to
// access the device capabilities-specific methods.
hr = pDevice->Capabilities(&pCapabilities);
if (FAILED(hr))
{
printf("! Failed to get IPortableDeviceCapabilities from IPortableDevice, hr = 0x%lx\n", hr);
}
// Get all functional categories supported by the device.
if (SUCCEEDED(hr))
{
hr = pCapabilities->GetFunctionalCategories(&pCategories);
if (FAILED(hr))
{
printf("! Failed to get functional categories from the device, hr = 0x%lx\n", hr);
}
}
if (SUCCEEDED(hr))
{
hr = pCategories->GetCount(&dwNumCategories);
if (FAILED(hr))
{
printf("! Failed to get number of functional categories, hr = 0x%lx\n", hr);
}
}
printf("\n%d Functional Categories Found on the device\n\n", dwNumCategories);
// Loop through each functional category and display its name
if (SUCCEEDED(hr))
{
for (DWORD dwIndex = 0; dwIndex < dwNumCategories; dwIndex++)
{
PROPVARIANT pv = { 0 };
PropVariantInit(&pv);
hr = pCategories->GetAt(dwIndex, &pv);
if (SUCCEEDED(hr))
{
// We have a functional category. It is assumed that
// functional categories are returned as VT_CLSID
// VarTypes.
if (pv.puuid != NULL)
{
// Display the functional category name
//DisplayFunctionalCategory(*pv.puuid);
printf("\n");
}
}
PropVariantClear(&pv);
}
}
return true;
}
bool ExistPortableDevices() {
CoInitialize(NULL);
IPortableDeviceManager* pPortableDeviceManager;
HRESULT hr = CoCreateInstance(CLSID_PortableDeviceManager,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pPortableDeviceManager));
if (FAILED(hr))
{
printf("! Failed to CoCreateInstance CLSID_PortableDeviceManager, hr = 0x%lx\n", hr);
}
DWORD cPnPDeviceIDs;
if (SUCCEEDED(hr))
{
hr = pPortableDeviceManager->GetDevices(NULL, &cPnPDeviceIDs);
if (FAILED(hr))
{
printf("! Failed to get number of devices on the system, hr = 0x%lx\n", hr);
}
}
// Report the number of devices found. NOTE: we will report 0, if an error
// occured.
printf("\n%d Windows Portable Device(s) found on the system\n\n", cPnPDeviceIDs);
if (SUCCEEDED(hr) && (cPnPDeviceIDs > 0))
{
PWSTR* pPnpDeviceIDs;
pPnpDeviceIDs = new (std::nothrow) PWSTR[cPnPDeviceIDs];
if (pPnpDeviceIDs != NULL)
{
DWORD dwIndex = 0;
hr = pPortableDeviceManager->GetDevices(pPnpDeviceIDs, &cPnPDeviceIDs);
if (SUCCEEDED(hr))
{
// For each device found, display the devices friendly name,
// manufacturer, and description strings.
for (dwIndex = 0; dwIndex < cPnPDeviceIDs; dwIndex++)
{
IPortableDeviceValues* clientInformation;
hr = CoCreateInstance(CLSID_PortableDeviceValues,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&clientInformation));
if (SUCCEEDED(hr))
{
clientInformation->SetStringValue(WPD_CLIENT_NAME, CLIENT_NAME);
clientInformation->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, CLIENT_MAJOR_VER);
clientInformation->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, CLIENT_MINOR_VER);
clientInformation->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, CLIENT_REVISION);
}
IPortableDevice* device;
hr = CoCreateInstance(CLSID_PortableDeviceFTM,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&device));
if (SUCCEEDED(hr))
{
hr = (device)->Open(pPnpDeviceIDs[dwIndex], clientInformation);
if (hr == E_ACCESSDENIED)
{
wprintf(L"Failed to Open the device for Read Write access, will open it for Read-only access instead\n");
clientInformation->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS, GENERIC_READ);
hr = (device)->Open(pPnpDeviceIDs[dwIndex], clientInformation);
}
SupportStorage(device);
if (FAILED(hr))
{
wprintf(L"! Failed to Open the device, hr = 0x%lx\n", hr);
// Release the IPortableDevice interface, because we cannot proceed
// with an unopen device.
(device)->Release();
device = nullptr;
}
}
printf("[%d] ", dwIndex);
WCHAR pDeviceFriendlyName[16] = { 0 };
DWORD pcchDeviceFriendlyName = 0x0d;
// HRESULT re = pPortableDeviceManager->GetDeviceFriendlyName(pPnpDeviceIDs[dwIndex], pDeviceFriendlyName, &pcchDeviceFriendlyName);
// printf(" ");
HRESULT re = pPortableDeviceManager->GetDeviceDescription(pPnpDeviceIDs[dwIndex], pDeviceFriendlyName, &pcchDeviceFriendlyName);
// DisplayManufacturer(pPortableDeviceManager, pPnpDeviceIDs[dwIndex]);
// printf(" ");
// DisplayDescription(pPortableDeviceManager, pPnpDeviceIDs[dwIndex]);
printf("%s\n", pDeviceFriendlyName);
}
}
else
{
printf("! Failed to get the device list from the system, hr = 0x%lx\n", hr);
}
for (dwIndex = 0; dwIndex < cPnPDeviceIDs; dwIndex++)
{
CoTaskMemFree(pPnpDeviceIDs[dwIndex]);
pPnpDeviceIDs[dwIndex] = NULL;
}
// Delete the array of PWSTR pointers
delete[] pPnpDeviceIDs;
pPnpDeviceIDs = NULL;
return true;
}
}
return false;
}
int main() {
bool res = ExistPortableDevices();
return 0;
}
但是在USB仅充电的情况下pv.puuid输出后是{23F05BBC-15DE-4C2A-A55B-A9AF5CE412EF} 也就是WPD_FUNCTIONAL_CATEGORY_STORAGE,应该是无法分别是仅充电还是文件传输.
而且借用一加手机测试,第三步的代码,在USB仅充电情况下是检测不到存储设备的.这部分就不是很了解了,希望熟悉的大佬指点.