Einzelnen Beitrag anzeigen

Benutzerbild von Dalai
Dalai

Registriert seit: 9. Apr 2006
1.682 Beiträge
 
Delphi 5 Professional
 
#8

AW: Profilverzeichnisse aller Nutzer ermitteln

  Alt 16. Feb 2014, 15:19
Wenn die API wirklich diese Informationen nicht zur Verfügung stellt, sollte es doch über die Registryzweige unter HKEY_USERS gehen, oder? Dort liegen ja die einzelnen Userzweige und der des aktuellen Users wird beim Login unter HKEY_CURRENT_USER eingeblendet.
Nein, Irrtum. Unter HKEY_USERS sind nur die Zweige der Nutzer eingeblendet, die derzeit angemeldet sind (siehe Wikipedia). Davon abgesehen komme ich dort weder an den Nutzernamen (Loginname) noch ans Profilverzeichnis.

-----

Ich hab gestern noch eine Lösung erarbeitet, die ganz gut funktioniert, aber wie immer wäre es schön, wenn das irgendwie schöner ginge. Im Grunde wird hier folgendes gemacht:
  • Ermittlung aller auf dem System existierenden Nutzernamen mit NetUserEnum (Level 0)
  • Ermittlung der SID zu jedem Nutzernamen mit GetAccountSid
  • Öffnen des Registry-Schlüssels HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList mit der SID und Auslesen (und Expandieren von Umgebungsvariablen) von ProfileImagePath
Delphi-Quellcode:
function GetAccountSid(const Server, User: WideString; var Sid: PSID): DWORD;
function ConvertSidToStringSid(SID: PSID; var StringSid: LPSTR): Boolean; stdcall;
    external 'advapi32.dllname 'ConvertSidToStringSidA';

implementation


function ExpandEnvStr(const sInput: string): string;
const
  MAXSIZE = 32768; // laut PSDK sind 32k das Maximum
begin
  SetLength(Result, MAXSIZE);
  SetLength(Result,
            ExpandEnvironmentStrings(PChar(sInput), @Result[1], Length(Result))-1);
            //-1 um abschließendes #0 zu verwerfen
end;


function GetAccountSid(const Server, User: WideString; var Sid: PSID): DWORD;
var
  dwDomainSize, dwSidSize: DWord;
  R : LongBool;
  wDomain : WideString;
  Use : DWord;
begin
  Result := 0;
  SetLastError(0);
  dwSidSize := 0;
  dwDomainSize := 0;
  R := LookupAccountNameW(PWideChar(Server), PWideChar(User), nil, dwSidSize,
    nil, dwDomainSize, Use);
  if (not R) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
  begin
    SetLength(wDomain, dwDomainSize);
    Sid := GetMemory(dwSidSize);
    R := LookupAccountNameW(PWideChar(Server), PWideChar(User), Sid,
      dwSidSize, PWideChar(wDomain), dwDomainSize, Use);
    if not R then
    begin
      FreeMemory(Sid);
      Sid := nil;
    end;
  end
  else
    Result := GetLastError;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  EntriesRead, TotalEntries, ResumeHandle : DWORD;
  UserInfo : lpUSER_INFO_0;
  lpBuffer : Pointer;
  i : Integer;
  Res : LongWord;
  sl : TStringList;
  sSID : PChar;
  ptrSID : PSID;
  reg : TRegistry;
begin
    ListBox1.Clear;
    sl:= TStringList.Create;
    ResumeHandle := 0;
    repeat
        Res:= NetUserEnum(nil, 0, 0, lpBuffer, 0, EntriesRead, TotalEntries, ResumeHandle);

        if (Res = NERR_SUCCESS) or (Res = ERROR_MORE_DATA) then
        begin
            UserInfo := lpBuffer;
            for i := 0 to EntriesRead-1 do
            begin
                sl.Add(WideCharToString(UserInfo^.usri0_name));
                ListBox1.Items.Add('------------------------------------------');
                ListBox1.Items.Add(WideCharToString(UserInfo^.usri0_name));
                Inc(UserInfo);
            end;
            NetApiBufferFree(lpBuffer);
        end;
   until (Res <> ERROR_MORE_DATA);

   for i:=0 to sl.Count-1 do
   begin
       GetAccountSid('', sl[i], ptrSID);
       if ConvertSidToStringSid(ptrSID, sSID) then
       begin
           ListBox1.Items.Add(sSID);
           reg:= TRegistry.Create;
           reg.RootKey:= HKEY_LOCAL_MACHINE;
           if reg.OpenKeyReadOnly('\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\' + sSID) then
               ListBox1.Items.Add(ExpandEnvStr(reg.ReadString('ProfileImagePath')));
           reg.CloseKey;
           reg.Free;
       end;
   end;
   sl.Free;
end;
Hinweis: Das ist nur ein Testprojekt, daher an einigen Stellen keine Auswertung der Funktionsrückgaben und alles in der Methode Button1Click. Die Funktion GetAccountSid hab ich von Luckie gemopst. Die Implementation in Delphi diente sowieso nur dem Zweck, das einfacher testen und dann nach AutoIt übertragen zu können. Falls jemand Interesse am AutoIt-Code hat, dann einfach fragen . Noch ein Tip für alle Suchmaschinenbenutzer: Die Funktionen ConvertSidToStringSid und GetAccountSid existieren in AutoIt bereits als UDF _Security__SidToStringSid bzw. _Security__GetAccountSid).

MfG Dalai
  Mit Zitat antworten Zitat