技术标签: C++ Windows api 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仅充电情况下是检测不到存储设备的.这部分就不是很了解了,希望熟悉的大佬指点.
文章浏览阅读6.7k次。Linux 发行版中提供的 OpenLDAP 软件按照一个客户机/服务器模型实现了轻量级目录访问协议(LDAP)。LDAP 的设计目的是提供一种有效的方法来查找和管理信息。OpenLDAP 软件和包提供了创建目录信息树(一个主要进行读操作的数据库)的工具。本文向您展示如何存储用户的帐号信息,并修改身份验证服务来使用 LDAP 获取所需要的信息。内部细节并不重要,因为这些工具可以将数据库的内容以_posixaccount
文章浏览阅读1.1k次。什么是XPath?XPath (XML Path Language) 是一门在 XML 文档中查找信息的语言,可用来在 XML 文档中对元素和属性进行遍历。什么是XML?XML 指可扩展标记语言(EXtensible Markup Language)XML 是一种标记语言,很类似 HTMLXML 的设计宗旨是传输数据,而非显示数据XML 的标签需要我们自行定义XML 被设计为具..._xpath可以从
文章浏览阅读129次。每次 PAT 考试结束后,考试中心都会发布一个考生单位排行榜。本题就请你实现这个功能。输入格式:输入第一行给出一个正整数 N(≤105),即考生人数。随后 N 行,每行按下列格式给出一个考生的信息:准考证号 得分 学校其中准考证号是由 6 个字符组成的字符串,其首字母表示考试的级别:B代表乙级,A代表甲级,T代表顶级;得分是 [0, 100] 区间内的整数;学校是由..._085 pat单位排行 (25 分) 每次 pat 考试结束后,考试中心都会发布一个考生单位排行
文章浏览阅读451次。工业物联网有时也被称为工业4.0,随着人口红利的终结,未来也正在向自动化前进。在18世纪后期,工业1.0使用蒸汽动力进行机械生产。20世纪初,电力的出现帮助我们进入大规模生产的工业2.0。20世纪70年代,电子和IT基础设施开始实现生产自动化,我们进入了3.0。如今作为文明的下一步是IIoT和工业4.0,其中网络物理系统将监视,分析和改进我们的制造业务。美国国家科学基金会将网络物理系统描述为“..._工业互联网的发展,对机器视觉有哪些影响
文章浏览阅读3.3k次,点赞8次,收藏20次。几天内居然这么多人看了,那么就更新一下。之前的代码复用情况太多,改了一下,少了一两百行。不过可能还有bug,欢迎指正。首先把界面设计好,画好那么多按钮,并给每个按钮命名,然后再双击按钮,添加相应的事件,比如按数字就在textbox.text加上数字。using System;using System.Collections.Generic;using System.ComponentModel..._c#计算器
文章浏览阅读91次。文章目录791. 高精度加法793. 高精度乘法题目来源于Acwing算法学习社区791. 高精度加法#include <iostream>#include <vector>using namespace std;vector<int> A , B , C;string a , b;vector<int> add(vector<int>& A , vector<int>& B){ in
文章浏览阅读111次。给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。示例:给定 1->2->3->4, 你应该返回 2->1->4->3.来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/swap-nodes-in-pairs解题思..._交换链表中的相邻的节点
文章浏览阅读296次。C# CodinGame记录(4)--ASCII ART_coding games c语言ascii art
文章浏览阅读3.7k次。1.我注意到组态王软件是按软件点数来收费的,请问这个点数是如何计算的?组态王软件是按点数收费的,这里讲的点数不是用户在工程设计时设计的采样点的数目,而是组态王数据词典中定义的所有变量(不包括软件本身自带的21个变量),因为在组态王的数据词典中除了要定义采样点外,还需要定义一部分内存变量来实现软件的逻辑控制动画连接等,所以用户在软件选型时要留有一定的点数余量。2.我想删除一个指定的变..._组态王变量频率
文章浏览阅读2.2k次。如下图 当我们遇到一个复杂问题时可以用图像帮我们思考在上图中,我们设置二维数组宽度为rows,高度为columns,取左上角坐标为(startX,startY),取左下角的坐标为(endX,endY),可以发现每次循环的开始为(startX,startY),endX和endY可以根据rows和columns来推出,所以我们应该去发现(startX,startY)的规律我们来_顺时针螺旋式从外向内输出二维数组数据
文章浏览阅读348次。JS原型链与继承知识点匿名函数调用的this指向原型链与原型对象new对象的过程代码注释原型链的查找顺序查找自身私有属性知识点匿名函数调用的this指向 JavaScript的匿名函数中this指向window javascript中函数的this指向函数的最后调用者,而匿名函数是一个没有指针的全局变量,在非严格版中默认调用者是window,那么它的this指向的就是全局,就是window对象。原型链与原型对象原型对象是函数特有的(prototype),为了继承所有类型的数据都有原型链(__js 原型链依次往上查找null
文章浏览阅读1.3w次。关于 密码学Paillier同态加密的介绍以及c++实现_ssw 加密算法在 2009 年由 shen、shi 和 waters 提出