Windows下数据传输时禁用USB存储设备的技术方案求助
解决USB传输数据时无法禁用的替代方案
原因分析
原代码依赖SetupDiCallClassInstaller执行DIF_PROPERTYCHANGE来禁用设备,但当USB驱动器处于数据传输中时,系统会因为设备被进程占用而拒绝操作,返回错误码13(资源占用/权限冲突)。
替代方案
1. 强制卸载设备(使用CM_Request_Device_Eject)
此API可尝试强制弹出设备,即使有数据传输正在进行,但会直接中断传输,可能导致数据丢失,需谨慎使用。
代码示例:
#include <Cfgmgr32.h> #pragma comment(lib, "Cfgmgr32.lib") BOOL ForceEjectUsbDevice(LPCWSTR deviceInstanceId) { DEVINST devInst; CONFIGRET cr = CM_Locate_DevNode(&devInst, deviceInstanceId, CM_LOCATE_DEVNODE_NORMAL); if (cr != CR_SUCCESS) { return FALSE; } // 执行强制弹出操作 cr = CM_Request_Device_Eject(devInst, NULL, NULL, 0, 0); return cr == CR_SUCCESS; }
注意:操作前需提示用户数据会中断,避免损失。
2. 终止占用进程后再禁用
先找到并终止正在访问USB驱动器的进程,消除资源占用后再执行原禁用逻辑。
步骤:
- 获取目标USB的盘符(如D:)
- 枚举系统所有进程
- 检查每个进程是否打开了该盘符下的文件句柄
- 终止关联进程后,调用原代码禁用设备
代码片段(简化版进程检查):
#include <Windows.h> #include <Psapi.h> #pragma comment(lib, "Psapi.lib") BOOL TerminateProcessesUsingDrive(char driveLetter) { DWORD processIds[1024]; DWORD bytesReturned; if (!EnumProcesses(processIds, sizeof(processIds), &bytesReturned)) { return FALSE; } int numProcesses = bytesReturned / sizeof(DWORD); char drivePath[4] = {driveLetter, ':', '\\', 0}; for (int i = 0; i < numProcesses; i++) { HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE, FALSE, processIds[i]); if (hProcess) { // 尝试打开驱动器,判断是否被占用 HANDLE hDrive = CreateFileA(drivePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDrive == INVALID_HANDLE_VALUE && GetLastError() == ERROR_SHARING_VIOLATION) { TerminateProcess(hProcess, 0); } if (hDrive != INVALID_HANDLE_VALUE) CloseHandle(hDrive); CloseHandle(hProcess); } } return TRUE; }
注意:终止进程可能导致系统不稳定或数据丢失,需确认操作必要性。
3. 注册表/组策略全局禁用USB存储
通过修改系统注册表,从服务层面禁用所有USB存储设备,不受当前设备状态影响。
注册表操作代码:
#include <Windows.h> BOOL DisableUsbStorageGlobally() { HKEY hKey; LONG result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\USBSTOR", 0, KEY_SET_VALUE, &hKey); if (result != ERROR_SUCCESS) { return FALSE; } DWORD startValue = 4; // 4=禁用USB存储服务,3=恢复启用 result = RegSetValueExA(hKey, "Start", 0, REG_DWORD, (BYTE*)&startValue, sizeof(DWORD)); RegCloseKey(hKey); return result == ERROR_SUCCESS; }
注意:需要管理员权限,且会禁用所有USB存储设备(包括未连接的)。
4. WMI禁用设备
通过WMI的Win32_PnPEntity类执行禁用操作,部分场景下可绕过临时资源占用问题。
代码示例:
#include <comdef.h> #include <Wbemidl.h> #pragma comment(lib, "wbemuuid.lib") BOOL DisableUsbDeviceViaWMI(LPCWSTR deviceInstanceId) { HRESULT hres = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hres)) return FALSE; hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); if (FAILED(hres)) { CoUninitialize(); return FALSE; } IWbemLocator* pLoc = NULL; hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc); if (FAILED(hres)) { CoUninitialize(); return FALSE; } IWbemServices* pSvc = NULL; hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc); if (FAILED(hres)) { pLoc->Release(); CoUninitialize(); return FALSE; } hres = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); if (FAILED(hres)) { pSvc->Release(); pLoc->Release(); CoUninitialize(); return FALSE; } // 查询目标设备并执行禁用 IEnumWbemClassObject* pEnumerator = NULL; wchar_t query[256]; swprintf_s(query, L"SELECT * FROM Win32_PnPEntity WHERE DeviceID='%s'", deviceInstanceId); hres = pSvc->ExecQuery(_bstr_t(L"WQL"), _bstr_t(query), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (FAILED(hres)) { pSvc->Release(); pLoc->Release(); CoUninitialize(); return FALSE; } IWbemClassObject* pclsObj = NULL; ULONG uReturn = 0; BOOL success = FALSE; while (pEnumerator) { HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if (uReturn == 0) break; VARIANT vtResult; hr = pclsObj->ExecMethod(_bstr_t(L"Disable"), 0, NULL, NULL, &vtResult); if (SUCCEEDED(hr)) success = TRUE; VariantClear(&vtResult); pclsObj->Release(); } pEnumerator->Release(); pSvc->Release(); pLoc->Release(); CoUninitialize(); return success; }
注意:需要管理员权限,部分设备可能不支持WMI禁用操作。
内容的提问来源于stack exchange,提问作者Ashwin arjunan
相关产品推荐
相关产品推荐

