windows USB存储检测

针对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仅充电情况下是检测不到存储设备的.这部分就不是很了解了,希望熟悉的大佬指点.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值