Windows服务中调用GetRawInputDeviceList返回0的问题咨询
我来帮你拆解这个问题——其实Windows服务里调用GetRawInputDevices()不是完全不可行,但确实有Windows安全模型带来的限制,咱们一步步说清楚:
为什么服务里调用会返回0?
Windows服务默认运行在会话0隔离环境中,而Raw Input输入设备是和用户登录会话(通常是会话1及以上)绑定的。会话0是系统级会话,没有关联任何用户的物理输入设备上下文,所以默认调用GetRawInputDevices()自然会返回0;而你的非服务程序运行在当前登录用户的会话里,能正常访问设备,所以可以枚举到5-6个设备。
可行吗?怎么调整?
是可行的,但需要让你的设备枚举代码运行在用户会话中,而不是服务所在的会话0。有两种方案:
方案1:允许服务与桌面交互(测试可用,生产不推荐)
从Windows Vista开始,微软就不建议使用这个选项,因为会带来安全风险,但测试环境可以快速验证:
- 打开服务管理器,找到你的服务,右键→属性→登录选项卡,勾选“允许服务与桌面交互”
- 重启服务后,调用
GetRawInputDevices()应该就能获取到设备列表了
方案2:启动用户会话子进程(生产推荐)
更好的做法是让服务在检测到用户登录时,启动一个运行在用户会话的子进程,在子进程里执行设备枚举。核心是用WTSGetActiveConsoleSessionId()获取活跃用户会话ID,再用CreateProcessAsUser()在该会话中启动程序。
服务端核心代码(启动子进程)
#include <windows.h> #include <Wtsapi32.h> #pragma comment(lib, "Wtsapi32.lib") BOOL LaunchDeviceEnumeratorInUserSession() { // 获取当前活跃控制台会话ID(用户登录的会话) DWORD sessionId = WTSGetActiveConsoleSessionId(); if (sessionId == 0xFFFFFFFF) { return FALSE; // 无活跃用户会话 } // 获取用户会话的令牌 HANDLE hUserToken = NULL; if (!WTSQueryUserToken(sessionId, &hUserToken)) { return FALSE; } STARTUPINFO si = {0}; PROCESS_INFORMATION pi = {0}; si.cb = sizeof(STARTUPINFO); // 指定用户会话的默认桌面 si.lpDesktop = L"winsta0\\default"; // 启动设备枚举子进程,替换成你的子程序路径 BOOL success = CreateProcessAsUser( hUserToken, L"C:\\YourPath\\DeviceEnumerator.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ); // 清理资源 CloseHandle(hUserToken); if (pi.hProcess) CloseHandle(pi.hProcess); if (pi.hThread) CloseHandle(pi.hThread); return success; }
子进程代码(设备枚举,和非服务程序逻辑一致)
#include <windows.h> #include <iostream> #include <vector> void EnumerateRawInputDevices() { UINT deviceCount = 0; // 先获取设备总数 if (!GetRawInputDevices(NULL, &deviceCount, sizeof(RAWINPUTDEVICELIST))) { std::cout << "GetRawInputDevices failed, error code: " << GetLastError() << std::endl; return; } // 分配内存存储设备列表 std::vector<RAWINPUTDEVICELIST> devices(deviceCount); if (!GetRawInputDevices(devices.data(), &deviceCount, sizeof(RAWINPUTDEVICELIST))) { std::cout << "GetRawInputDevices failed, error code: " << GetLastError() << std::endl; return; } std::cout << "Found " << deviceCount << " raw input devices:" << std::endl; for (UINT i = 0; i < deviceCount; ++i) { char deviceName[256] = {0}; UINT nameSize = sizeof(deviceName); // 获取设备名称 if (GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, deviceName, &nameSize)) { std::cout << "Device " << i + 1 << ": " << deviceName << std::endl; } } // 这里添加把设备信息发送到服务端的逻辑 } int main() { EnumerateRawInputDevices(); return 0; }
什么时候完全不可行?
如果你的服务必须严格运行在会话0,不能启动子进程,也不允许开启“与桌面交互”,那确实没办法获取用户的输入设备——这是Windows会话隔离机制的设计目的,防止服务被恶意利用来窃取用户输入或访问用户会话资源。
内容的提问来源于stack exchange,提问作者TOESTER
相关产品推荐
相关产品推荐

