Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Profilverzeichnisse aller Nutzer ermitteln (https://www.delphipraxis.net/179119-profilverzeichnisse-aller-nutzer-ermitteln.html)

Dalai 15. Feb 2014 14:40

Profilverzeichnisse aller Nutzer ermitteln
 
Hey Leute :),

nachdem ich nun vergeblich - vermutlich auch mit den falschen Suchbegriffen - mehr als einen halben Tag rumgesucht und rumprobiert habe, beanspruche ich mal wieder euer Wissen.

Mein Ziel ist es, eine Liste aller auf einem Windows-System existierenden Nutzer zu ermitteln (geht ja einfach mit NetUserEnum), allerdings brauche ich zwingend zu jedem Nutzer die Informationen über sein Profilverzeichnis (also wo das liegt); und das muss auf jedem Windows-System (ab Win2k) auch ohne Domäne funktionieren.

Warum mache ich den Zirkus? Ich gebe mal ein Beispiel: Gegeben sei ein Nutzer mit dem Loginnamen Hans. Bei dessen erster Anmeldung wird sein Profilverzeichnis angelegt, z.B. C:\Users\Hans. Später wird der Nutzer umbenannt in Willi. Das Profilverzeichnis bleibt natürlich stehen. Ich brauche aber beide Informationen, sowohl den Loginnamen Willi als auch den Ort des Profilverzeichnisses C:\Users\Hans.

Inzwischen habe ich rausgefunden, dass die Informationen, die MSDN-Library durchsuchenNetUserEnum und MSDN-Library durchsuchenNetUserGetInfo in die Datenstruktur MSDN-Library durchsuchenUSER_INFO_2 oder MSDN-Library durchsuchenUSER_INFO_3 liefern, wohl nur (wenn überhaupt) auf Servern oder in Domänen gefüllt sind, insbesondere die für mich relevanten Teile usri3_home_dir und usri3_profile.

Hat jemand für mich Suchbegriffe, Funktionsnamen oder ähnliches, die die benötigten Informationen liefern können?

Die Frage bezieht sich übrigens nur beschränkt auf Delphi. Letztlich brauche ich die Funktionalität in AutoIt, aber das nur nebenbei.

MfG Dalai

Popov 15. Feb 2014 15:26

AW: Profilverzeichnisse aller Nutzer ermitteln
 
Also, auch wenn du die Verzeichnisse ermitteln kannst, bedeutete es nicht, dass du auf sie zugreifen kannst. Denn bei der Erstellung wird man gefragt ob andere auf das Verzeichnis zugreifen können (zumindest bei XP).

Weiterhin weiß ich, auch wenn ich es selbst noch nie genutzt habe, dass in der Registry die Userkonten vermerkt sind (ich glaube in der HK_USERS). Ob man das irgendwie nutzen kann, weiß ich aber nicht.

Dalai 15. Feb 2014 15:54

AW: Profilverzeichnisse aller Nutzer ermitteln
 
Zitat:

Zitat von Popov (Beitrag 1247978)
Also, auch wenn du die Verzeichnisse ermitteln kannst, bedeutete es nicht, dass du auf sie zugreifen kannst.

Der Nutzer, der die Abfrage tätigt, hat zu dem Zeitpunkt in jedem Fall Adminrechte.

Zitat:

Denn bei der Erstellung wird man gefragt ob andere auf das Verzeichnis zugreifen können (zumindest bei XP).
Hab ich noch nie gesehen. Hast du das vielleicht mit der Freigabe eines Verzeichnisses verwechselt?

Zitat:

Weiterhin weiß ich, auch wenn ich es selbst noch nie genutzt habe, dass in der Registry die Userkonten vermerkt sind (ich glaube in der HK_USERS). Ob man das irgendwie nutzen kann, weiß ich aber nicht.
Natürlich sind sie das. Aber unter HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList stehen nur die SIDs der Nutzer sowie deren Nutzerprofile (ProfileImagePath). In diesem Fall bräuchte ich einen Weg, die Nutzernamen zur SID zu ermitteln. Das geht sicher irgendwie. Insgesamt bringt mir dieser Schlüssel aber nicht viel, denn dort sind auch die SIDs von Domänenkonten vermerkt, zu denen man ohne DC wohl nicht an den Nutzernamen kommt. Es geht mir aber nur um lokale Nutzerkonten.

MfG Dalai

mkinzler 15. Feb 2014 16:15

AW: Profilverzeichnisse aller Nutzer ermitteln
 
Zitat:

Hab ich noch nie gesehen. Hast du das vielleicht mit der Freigabe eines Verzeichnisses verwechselt?
Nein, auch als Admin wirst Du unter Umständen gefragt, wenn Du auf Dateien zugreifen willst die anderen Benutzern "gehören".

TiGü 16. Feb 2014 11:32

AW: Profilverzeichnisse aller Nutzer ermitteln
 
Zitat:

Zitat von Dalai (Beitrag 1247975)
Mein Ziel ist es, eine Liste aller auf einem Windows-System existierenden Nutzer zu ermitteln (geht ja einfach mit NetUserEnum), allerdings brauche ich zwingend zu jedem Nutzer die Informationen über sein Profilverzeichnis (also wo das liegt); und das muss auf jedem Windows-System (ab Win2k) auch ohne Domäne funktionieren.

Inzwischen habe ich rausgefunden, dass die Informationen, die MSDN-Library durchsuchenNetUserEnum und MSDN-Library durchsuchenNetUserGetInfo in die Datenstruktur MSDN-Library durchsuchenUSER_INFO_2 oder MSDN-Library durchsuchenUSER_INFO_3 liefern, wohl nur (wenn überhaupt) auf Servern oder in Domänen gefüllt sind, insbesondere die für mich relevanten Teile usri3_home_dir und usri3_profile.

Das ist so nicht ganz korrekt!
Beide Funktionen funktionieren auch auf lokaler Ebene ohne Domänencontroller!
Einfach ServerName in beiden Funktionen leer lassen bzw. leeren Pointer übergeben und schon füllt dir das lokale Betriebssystem die angeforderten User-Records.

Zitat:

Zitat von MSDN
ServerName [in]
Pointer to a constant string that specifies the DNS or NetBIOS name of the remote server on which the function is to execute. If this parameter is NULL, the local computer is used.


Dalai 16. Feb 2014 13:10

AW: Profilverzeichnisse aller Nutzer ermitteln
 
Zitat:

Zitat von TiGü (Beitrag 1248048)
Das ist so nicht ganz korrekt!
Beide Funktionen funktionieren auch auf lokaler Ebene ohne Domänencontroller!

Richtig. Aber eben nicht die von mir gewünschten Felder/Infos.

Zitat:

Einfach ServerName in beiden Funktionen leer lassen bzw. leeren Pointer übergeben und schon füllt dir das lokale Betriebssystem die angeforderten User-Records.
Hast du es mal selbst ausprobiert, und dabei in den Datenstrukturen Home- und Profilverzeichnisse gefunden? Ich würde ja nicht fragen, wenn ich die gewünschten Informationen dort gefunden hätte.

MfG Dalai

jaenicke 16. Feb 2014 13:19

AW: Profilverzeichnisse aller Nutzer ermitteln
 
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.

Dalai 16. Feb 2014 14:19

AW: Profilverzeichnisse aller Nutzer ermitteln
 
Zitat:

Zitat von jaenicke (Beitrag 1248066)
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.dll' name '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

TiGü 17. Feb 2014 11:05

AW: Profilverzeichnisse aller Nutzer ermitteln
 
Zitat:

Zitat von Dalai (Beitrag 1248064)
Zitat:

Einfach ServerName in beiden Funktionen leer lassen bzw. leeren Pointer übergeben und schon füllt dir das lokale Betriebssystem die angeforderten User-Records.
Hast du es mal selbst ausprobiert, und dabei in den Datenstrukturen Home- und Profilverzeichnisse gefunden? Ich würde ja nicht fragen, wenn ich die gewünschten Informationen dort gefunden hätte.

Tatsache! :shock:
Wenn man mal nicht auf Arbeit ist und den Quelltext vor sich hat...

Wenn unsere Applikation keinen DC findet, dann weichen wir auf Known Folders aus, die aber für alle User gleich sind.

Dalai 17. Feb 2014 20:36

AW: Profilverzeichnisse aller Nutzer ermitteln
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von TiGü (Beitrag 1248176)
Wenn unsere Applikation keinen DC findet, dann weichen wir auf Known Folders aus, die aber für alle User gleich sind.

Das nutzt aber auch nur etwas, wenn der Name des Profilverzeichnisses exakt mit dem Benutzernamen übereinstimmt. Mit dem im OP gegebenen Beispiel kommt man da nicht weit...

Ich hab mittlerweile die Vermutung, dass die Datenfelder bzgl. Home bzw. Profil in USER_INFO_x für die Angaben Stammverzeichnis stehen, die standardmäßig leer sind (siehe Bild). Ob das so genau stimmt, weiß ich nicht, und spielt letztlich für die Fragestellung keine Rolle.

MfG Dalai


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:58 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz