特定桌面线程枚举与SetThreadDesktop调用线程识别及权限问题
桌面线程枚举与权限问题解答
问题解答
1. 枚举特定桌面中运行的线程
枚举特定桌面内线程的核心流程:
- 调用
CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)获取系统所有线程的快照 - 遍历每个线程,通过
GetThreadDesktop()获取线程所属桌面句柄 - 用
GetUserObjectInformation()读取桌面名称,与目标桌面名称比对,筛选出匹配的线程 - 注意:操作需具备足够权限,否则会触发访问被拒绝错误
2. 识别调用SetThreadDesktop()的线程与进程
完全可行。当进程调用SetThreadDesktop()将线程关联到目标桌面后,该线程的所属桌面会更新,可通过以下步骤追踪:
- 枚举系统所有线程,获取每个线程的桌面句柄及名称
- 定位到目标桌面对应的线程后,调用
GetProcessIdOfThread()获取所属进程ID - 通过进程ID进一步获取进程名称等信息
解决GetThreadDesktop() ERROR_ACCESS_DENIED问题
你的代码出现权限拒绝错误,核心原因:
- 调用
GetThreadDesktop()前未获取足够的线程访问权限 SetUserObjectAllAccess()调用时机错误,在GetThreadDesktop()失败后再设置权限无意义- 通过进程名称过滤线程效率低,且存在名称重复风险
以下是修正后的Pascal代码:
function GetProcessNameByID(ProcessID: DWORD): string; var hProcess: THandle; ProcessName: array[0..MAX_PATH] of Char; begin Result := ''; // 请求受限查询权限,足够获取进程名称 hProcess := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, ProcessID); if hProcess <> 0 then begin if GetModuleBaseName(hProcess, 0, ProcessName, MAX_PATH) > 0 then Result := ProcessName; CloseHandle(hProcess); end; end; function GetUserObjectName(hUserObject: THandle): string; var Count: DWORD; begin Result := ''; // 先获取所需缓冲区大小 if not GetUserObjectInformation(hUserObject, UOI_NAME, nil, 0, Count) then begin if GetLastError() = ERROR_INSUFFICIENT_BUFFER then begin SetLength(Result, Count); if GetUserObjectInformation(hUserObject, UOI_NAME, PChar(Result), Count, Count) then SetLength(Result, StrLen(PChar(Result))); end; end; end; function GetThreadsInTargetDesktop(const TargetDesktopName: string): Boolean; var SnapThreadHandle: THandle; ThreadEntry: TThreadEntry32; hThread: THandle; DeskObj: HDESK; DesktopName: string; ProcessID: DWORD; SessionID: DWORD; begin Result := False; // 创建线程快照 SnapThreadHandle := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if SnapThreadHandle = INVALID_HANDLE_VALUE then Exit; try ThreadEntry.dwSize := SizeOf(TThreadEntry32); if not Thread32First(SnapThreadHandle, ThreadEntry) then Exit; repeat // 打开线程获取必要权限 hThread := OpenThread(THREAD_QUERY_LIMITED_INFORMATION, False, ThreadEntry.th32ThreadID); if hThread <> 0 then begin try // 获取线程所属桌面 DeskObj := GetThreadDesktop(ThreadEntry.th32ThreadID); if DeskObj <> 0 then begin DesktopName := GetUserObjectName(DeskObj); // 比对目标桌面名称(不区分大小写) if CompareText(DesktopName, TargetDesktopName) = 0 then begin ProcessID := GetProcessIdOfThread(hThread); ProcessIdToSessionId(ProcessID, SessionID); Writeln('线程ID: ', IntToStr(ThreadEntry.th32ThreadID)); Writeln('进程ID: ', IntToStr(ProcessID)); Writeln('会话ID: ', IntToStr(SessionID)); Writeln('窗口站: ', GetUserObjectName(GetProcessWindowStation)); Writeln('进程名称: ', GetProcessNameByID(ProcessID)); Writeln('桌面名称: ', DesktopName); Writeln('基础优先级: ', IntToStr(ThreadEntry.tpBasePri)); Writeln(''); Result := True; end; CloseHandle(DeskObj); end; finally CloseHandle(hThread); end; end; until not Thread32Next(SnapThreadHandle, ThreadEntry); finally CloseHandle(SnapThreadHandle); end; end; // 调用示例:枚举"Default"桌面内的线程 // GetThreadsInTargetDesktop('Default');
关键修正说明
- 使用
OpenThread(THREAD_QUERY_LIMITED_INFORMATION, ...)获取线程访问权限,这是调用GetThreadDesktop()的必要前提 - 优化
GetUserObjectName()的缓冲区大小计算逻辑,避免内存错误 - 先验证
GetThreadDesktop()返回句柄的有效性,再执行后续操作 - 直接通过桌面名称筛选线程,精准匹配需求
- 采用
CompareText()进行不区分大小写的名称比对,提升兼容性
内容的提问来源于stack exchange,提问作者JOTA
相关产品推荐
相关产品推荐

