delphiwinapi

How enumerate threads running in a specific desktop?


Suppose that a process calls the SetThreadDesktop() API to capture the screen of a specific desktop.

Is it possible to identify this thread inside of the desktop and know the caller process?

If yes, could you provide a code example for it?


Edition:

With reference to Remy's answer i have this following code, but GetLastError() to GetThreadDesktop() is ERROR_ACCESS_DENIED.

function GetProcessNameByID(ProcessID: DWORD): string;
var
  hProcess: THandle;
  ProcessName: array[0..MAX_PATH] of Char;
begin
  Result := '';
  hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, 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
  GetUserObjectInformation(hUserObject, UOI_NAME, PChar(Result), 0, Count);
  SetLength(Result, Count + 1);
 
  if GetUserObjectInformation(hUserObject, UOI_NAME, PChar(Result), Count, Count) then
    StrResetLength(Result)
  else
    Result := '';
end;
 
function SetUserObjectAllAccess(hUserObject: THandle): Boolean;
var
  Sd: PSecurity_Descriptor;
  Si: Security_Information;
begin
  Sd := PSecurity_Descriptor(LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH));
  InitializeSecurityDescriptor(Sd, SECURITY_DESCRIPTOR_REVISION);
  SetSecurityDescriptorDacl(Sd, True, nil, False);
 
  Si := DACL_SECURITY_INFORMATION;
  Result := SetUserObjectSecurity(hUserObject, Si, Sd);
 
  LocalFree(HLOCAL(Sd));
end;
 
function GetThreadsList(PID: Cardinal): Boolean;
var
  SnapProcHandle: THandle;
  NextProc: Boolean;
  TThreadEntry: TThreadEntry32;
  SessionID: Cardinal;
  DeskObj: HDESK;
begin
  SnapProcHandle := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  Result := (SnapProcHandle <> INVALID_HANDLE_VALUE);
  if Result then
  try
    TThreadEntry.dwSize := SizeOf(TThreadEntry);
    NextProc := Thread32First(SnapProcHandle, TThreadEntry);
    while NextProc do
    begin
      //if TThreadEntry.th32OwnerProcessID = PID then
      if GetProcessNameByID(TThreadEntry.th32OwnerProcessID) = 'Project2.exe' then
        begin
        Writeln('Thread ID:        ' + {IntToHex(TThreadEntry.th32ThreadID, 8))}IntToStr(TThreadEntry.th32ThreadID));
        Writeln('Process ID:       ' + Inttostr(GetProcessIdOfThread(TThreadEntry.th32ThreadID)));
        ProcessIdToSessionId(TThreadEntry.th32OwnerProcessID, SessionID);
        Writeln('Session ID:       ' + IntToStr(SessionID));
        Writeln('Window Station:   ' + GetUserObjectName(GetProcessWindowStation));
        Writeln('Process Name:     ' + GetProcessNameByID(TThreadEntry.th32OwnerProcessID));
        DeskObj := GetThreadDesktop(TThreadEntry.th32ThreadID);
        SetUserObjectAllAccess(DeskObj);
        Writeln('Desktop Name:     ' + GetUserObjectName(DeskObj));
        Writeln('Base Priority     ' + inttostr(TThreadEntry.tpBasePri));
        Writeln('');
      end;
      NextProc := Thread32Next(SnapProcHandle, TThreadEntry);
    end;
  finally
    CloseHandle(SnapProcHandle);
  end;
end;
 

Solution

  • There is not a specific API to enumerate threads in a desktop. You will have to enumerate all available threads using CreateToolhelp32Snapshot() and Thread32(First|Next)() (see Enumerating threads in Windows), and then filter them using GetThreadDesktop().