Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Alle User eines Rechners auflisten (https://www.delphipraxis.net/2336-alle-user-eines-rechners-auflisten.html)

CalganX 18. Jan 2003 18:22


Alle User eines Rechners auflisten
 
Hi,
welche Win32-API-Funktion erlaubt es mir alle User, die auf dem Rechner registriert sind aufzulisten?

Chris

Luckie 18. Jan 2003 18:39

NetUserEnum

CalganX 18. Jan 2003 18:48

Super!
Habe aber das Problem, dass das MSDN/PSDK mir nicht sagen will, in welcher Unit das ganze zu finden ist.
Das wüsste ich gerne und kannst du mir mal sagen, was da zurückgegeben wird???

Chris

Luckie 18. Jan 2003 18:53

PSDK sagt der header ist Lm.h. Ich weiß nicht, ob borland dafür eien Unit zur Verfügung stellt. Aber bei den Jedis müßtest du fündig werden. Näheres kann ich dir dazu auch nicht sagen, da ich damit noch nicht gearbeitet habe.

CalganX 18. Jan 2003 19:01

Nur ist das Problem, dass die Jedis immer noch offline sind (inzwischen vermute ich für immer... :-? )

Chris

Alexander 18. Jan 2003 19:05

Es gibt bestimmt Referenz-seiten, die (sicher nicht ganz legal) die Inhalte kopiert haben. Vielleicht hast du Glück und du findest bei Google was

Luckie 18. Jan 2003 19:08

Liste der Anhänge anzeigen (Anzahl: 1)
Im Anhang mal die Header-Überrsetzungen von den Jedis.

Alexander 18. Jan 2003 19:10

oder so :mrgreen:

Christian Seehase 19. Jan 2003 02:11

Moin Chris,

Zitat:

Habe aber das Problem, dass das MSDN/PSDK mir nicht sagen will, in welcher Unit das ganze zu finden ist.
Das ist so nicht ganz richtig.
Oft entspricht der Name der Headerdatei der dort angegeben wird, dem Namen der Unit in der Du die Funktion o.ä. findest.

Meist wirst Du keine direkte Delphi Implementierung von Funktionen finden, wenn diese für 9x und NT ff unterschiedlich sind.
(Wie z.B. die Netxxx Funktionen).

CalganX 19. Jan 2003 10:37

So, Jedi-Headers gefunden, heruntergeladen und installiert.
Nun stellt sich aber folgendes Problem:
Wenn ich folgendes Aufrufe (eigentlich nutze ich nonVCL, aber das mache ich später):
Delphi-Quellcode:
if NetUserEnum(nil, 0, FILTER_NORMAL_ACCOUNT, aBuffer, MAX_LENGTH, iUsers, iUsers{2}, nil) = NERR_SUCCESS then begin
  {... hier stellt sich das Problem: wie lese ich Buffer aus? }
end;
Wie bereits als Comment geschrieben: Welchen Typ soll aBuffer haben? Normalerweise ist das ja array[0..MAX_LENGTH+1] of Char, aber es müssen ja mehrere User rein!
Wie werden die User voneinander getrennt?

Chris

Luckie 19. Jan 2003 12:18

Ich vermute mal durch #0 und abgeschlossen wird die Liste mit #0#0.

CalganX 19. Jan 2003 12:23

Würde ich ausprobieren, nur gibt obiges folgendes aus (Label1.Caption := aBuffer);
Zitat:

|í|
Chris

CalganX 19. Jan 2003 13:23

So, nachdem ich mir mal die Ausgabestruktur angesehen habe (_USER_INFO_0). Kommt nun raus (Label1.Caption := Buffer.usri0_name):
Zitat:

?|?|?|?|?|???????????????????????????????????[...]???SUPPORT_388945a0
Chris

Christian Seehase 19. Jan 2003 13:50

Moin Chris,

kleine Gemeinheit der Netxxx Funktionen.
Da die Funktionen den erforderlichen Platz für die Daten selber reservieren, wird als Buffer kein Pointer, sondern die Adresse eines Pointers übergeben.

Der Aufruf muss also

Delphi-Quellcode:
if NetUserEnum(nil, 0, FILTER_NORMAL_ACCOUNT, [color=red]@[/color]aBuffer, MAX_LENGTH, iUsers, iUsers{2}, nil) = NERR_SUCCESS then
lauten.

Ganz wichtig:
Nachdem der Buffer abgearbeitet wurde muss er mit NetApiBufferFree wieder freigegeben werden.

Um den Buffer abzuarbeiten empfiehlt es sich eine zweite Variable des Typs anzulegen, und diesem dann den Wert in aBuffer nach Aufruf der Funktion zu übergeben.
Wenn man als Parameter prefmaxlen den Wert MAX_PREFERRED_LENGTH angibt erhält man ja alle Einträge, und kann diese dann in einer for Schleife abarbeiten, wobei ein inc(aBuffer) genügt, um auf den nächsten Eintrag zu kommen (wenn man aBuffer vom richtigen Typ her angelegt hat!)

Da ich den Zahlenwert der Konstanten MAX_LENGTH nicht kenne:
Tausch die lieber mal gegen MAX_PREFERRED_LENGTH aus.

CalganX 19. Jan 2003 14:00

Hi,
also, ich habe nun 2 Buffer vom Typ _USER_INFO_0 (so steht es auch im PSDK). Nun habe ich folgenden Source:
Delphi-Quellcode:
procedure TMainFrm.FormCreate(Sender: TObject);
var
  aBuffer1, aBuffer2: _USER_INFO_0;
  iUsers: Cardinal;
begin
  if NetUserEnum(nil, 0, FILTER_NORMAL_ACCOUNT, @aBuffer1,{<-} MAX_PATH+1,
    iUsers, iUsers{2}, nil) = NERR_SUCCESS then begin
     aBuffer2 := aBuffer1;
     NetApiBufferFree(@aBuffer1);
     Label1.Caption := aBuffer2.usri0_name;
  end;
end;
Allerdings gibt es an der Stelle, wo der Pfeil ist folgenden Fehler:
Zitat:

Zitat von Delphi-Debugger
[Error] Unit1.pas(31): Types of actual and formal var parameters must be identical

Hast du da eine Idee, Chris?

Chris

Luckie 19. Jan 2003 14:01

Zitat:

Zitat von Christian Seehase
Um den Buffer abzuarbeiten empfiehlt es sich eine zweite Variable des Typs anzulegen, und diesem dann den Wert in aBuffer nach Aufruf der Funktion zu übergeben.
Wenn man als Parameter prefmaxlen den Wert MAX_PREFERRED_LENGTH angibt erhält man ja alle Einträge, und kann diese dann in einer for Schleife abarbeiten, wobei ein inc(aBuffer) genügt, um auf den nächsten Eintrag zu kommen (wenn man aBuffer vom richtigen Typ her angelegt hat!)

Öhm, ja jetzt muß ich direkt mal nach Source fragen, weil ich nicht so ganz verstehe was du meinst.
Wenn aBuffer vom Typ Pointer ist kann ich ja mit Inc(aBuffer) an den nächsten Eintrag ran. Aber wie bekomme ich jetzt das ganze im Klartext?

CalganX 19. Jan 2003 14:06

Wenn du den Typ _USER_INFO_0 meinst, dann ist das ganze ein record. Du kannst darauf zugreifen. Wie, steht in irgendeinem der vorangegangenen Sources.

Chris

Luckie 19. Jan 2003 14:12

Dass das Ding ein Record ist, ist klar. Den kenne ich ja auch. Nur ist es ja nicht ein Record, sondern jeder User hat ja einen Record. Irgendwie so stelle ich mir das vor:
Delphi-Quellcode:
s := aBuffer[0].UserName; // 1. gefundene User
s := aBuffer[1].Username; // 2. gefundene User

Christian Seehase 19. Jan 2003 19:39

Moin Zusammen,

also ich gehe da von der PSDK Deklaration aus, so dass der Typ von aBuffer ein Pointer auf eine USER_INFO_x Struktur ist.
Wie in Delphi sonst üblich einen Record zu übergeben kann ja eigentlich deshalb schon nicht klappen, da man sich ja eine beliebige Anzahl an Einträgen zurückgeben lassen kann, ohne vorher zu Wissen, wieviele es sind.

Ein Witz an diesen Netxxx Funktionen ist ja, dass sie den erforderlichen Speicher selber reservieren.

aBuffer wäre dann also als

Delphi-Quellcode:
var
  aBuffer : PUSER_INFO_1;
deklariert, so dass man auf diesem Wege das Ergebnis verarbeiten kann

Delphi-Quellcode:
for i := 1 to dwEntriesRead do
begin
  //... was auch immer mit aBuffer geschehen soll
  inc(aBuffer); // auf den nächsten Eintrag adressieren
end;
Deshalb auch das Sichern von aBuffer in einer Variablen gleichen Typs, da man ja an NetApiBufferFree die Startadresse des Ergebnisbuffers zurückliefern muss, damit der Speicher wieder freigegeben werden kann.

Als Pointer ist aBuffer in der Funktion nur deshalb deklariert, da es sich um einen Pointer auf verschiedene Datenstrukturen (USER_INF0_x) handeln kann, so dass eine feste Typangabe nicht möglich ist.

CalganX 20. Jan 2003 12:33

Hi Christian,
ich hatte mir heute auch mal was ausgedacht, werde das nachher mal prüfen, aber nun zu deinem Source:
woher kommt dwReadEntries?

Chris

CalganX 20. Jan 2003 13:19

Hi Christian,
also, ich gehe davon aus, dass PUSER_INFO_0 (bzw. _1) ein Pointer auf _USER_INFO_0 ist (
Delphi-Quellcode:
type PUSER_INFO_0 = ^_USER_INFO_0
).(Der Typ selber existiert in der JwaLM-Unit nämlich nicht)
Dann habe ich den Source wie folgt geschrieben:
Delphi-Quellcode:
{33}  if NetUserEnum(nil, 0, FILTER_NORMAL_ACCOUNT, aBuffer, MAX_PREFERRED_LENGTH, iUser, iUser, nil) = NERR_SUCCESS then begin
{34}    while aBuffer <> nil do begin
{35}      ListBox1.Items.Add(aBuffer.usri0_name);
{36}      inc(aBuffer);
{37}    end;
{38}  end;
Da kommt folgender Fehler.
Zitat:

Zitat von Delphi-Debugger
[Error] Unit1.pas(33): Types of actual and formal var parameters must be identical

Der Fehler wird gemeldet an der Stelle, wo aBuffer in NetUserEnum steht.

Chris

CalganX 20. Jan 2003 16:06

Hi,
so, nun ist es soweit, dass das Programm läuft (obwohl ich mich ehrlich frage, warum er das so macht).
Allerdings wirft er mir beim Starten eine kräftige Access Violation gegen den Kopf. :freak:
Hat jemand eine Ahnung warum?

Chris

CalganX 20. Jan 2003 16:39

Schon nach einer halben Stunde habe ich das Problem einigermaßen behoben. Jetzt ist mir auch klar woher "dwEntriesRead" herkommt... ;)
Aber zurück zum Thema: Anstatt irgendeiner AV oder Exception wird einfach das CPU-Fenster geöffnet, sonst nix.
CPU-Fenster beenden, F9 und schon geht's weiter.
Aber warum geht das CPU-Fenster überhaupt auf?

Chris

Luckie 20. Jan 2003 22:29

Poste doch mal, wie dein bisheriger Code aussieht. Ich beschäftige mich auch gerade damit.

Luckie 20. Jan 2003 22:50

So, ich habe es jetzt so weit:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  dwEntriesRead, dwEntriesTotal: DWORD;
  ui1, ui2 : Pointer;
  i : Integer;
begin
  if NetUserEnum(nil, 0, FILTER_NORMAL_ACCOUNT, ui1, MAX_PREFERRED_LENGTH, dwEntriesRead, dwEntriesTotal, nil) = NERR_SUCCESS then
  begin
    ui2 := ui1;
    for i := 0 to dwEntriesRead-1 do
    begin
      Listbox1.Items.Add(PUserInfo1(ui1)^.usri1_name);
      Inc(Integer(ui1)); // <-- So nicht!
    end;
  end;
  NetAPIBufferFree(ui2);
end;
Beim Abarbeiten der Schleife bekomme ich eine AccessViolation. Könnte sich dessen mal bitte jemand annehmen?

Luckie 21. Jan 2003 00:07

Problem gelöst. War etwas blöd:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  dwEntriesRead, dwEntriesTotal: DWORD;
  ui1, ui2 : Pointer;
  i : Integer;
begin
  if NetUserEnum(nil, 0, FILTER_NORMAL_ACCOUNT, ui1, MAX_PREFERRED_LENGTH, dwEntriesRead, dwEntriesTotal, nil) = NERR_SUCCESS then
  begin
    ui2 := ui1;
    for i := 0 to dwEntriesRead-1 do
    begin
      Listbox1.Items.Add(PUserInfo1(ui1)^.usri1_name);
      Inc(Integer(ui1), SizeOf(Pointer));
    end;
  end;
  NetAPIBufferFree(ui2);
end;

CalganX 21. Jan 2003 12:38

Hi,
super! Werde mal sehen, was sich da machen lässt. Das Problem ist nur, dass unter nonVCL sich das CPU-Fenster nicht öffnet, aber wenn ich mit der VCL programmiere schon.

Chris

PS: Source ist auf dem Laptop; Laptop steht irgendwo anders; Source kommt später

Luckie 21. Jan 2003 12:41

Das ist VCL Code. Hier ist das nonVCL Pendant:
Delphi-Quellcode:
procedure GetUsers;
var
  dwEntriesRead, dwEntriesTotal: DWORD;
  ui1, ui2 : Pointer;
  i : Integer;
  s : String;
begin
  ui2 := nil;
  SendDlgItemMessage(hTabDlgs[0], IDC_CB, CB_RESETCONTENT, 0, 0);
  if NetUserEnum(nil, 0, FILTER_NORMAL_ACCOUNT, ui1, MAX_PREFERRED_LENGTH, dwEntriesRead, dwEntriesTotal, nil) = NERR_SUCCESS then
  begin
    ui2 := ui1;
    for i := 0 to dwEntriesRead-1 do
    begin
      s := PUserInfo1(ui1)^.usri1_name;
      SendDlgItemMessage(hTabDlgs[0], IDC_CB, CB_ADDSTRING, 0, Integer(@s[1]));
      Inc(Integer(ui1), SizeOf(Pointer));
    end;
  end;
  NetAPIBufferFree(ui2);
  SendDlgItemMessage(hTabDlgs[0], IDC_CB, CB_SETCURSEL, 0, 0);
  s := Format('Anzahl der Benutzer: %d', [dwEntriesRead]);
  SendDlgItemMessage(hApp, IDC_STATUSBAR, SB_SETTEXT, 1, Integer(@s[1]));
end;
Geht beides ohne Probleme.

Christian Seehase 22. Jan 2003 20:35

Moin Zusammen,

nur um mal zu verdeutlichen, wie's so bei mir aussieht (speziell wegen des @pBuffer)

Delphi-Quellcode:
procedure TfrmMAIN.Button1Click(Sender: TObject);

var
  pBuffer       : PUSER_INFO_0;
  pWork         : PUSER_INFO_0;
  dwEntriesRead : DWORD;
  dwTotalEntries : DWORD;
  i             : integer;

begin
  if NetUserEnum(nil,0,FILTER_NORMAL_ACCOUNT,@pBuffer,MAX_PREFERRED_LENGTH,@dwEntriesRead,@dwTotalEntries,nil) = NERR_Success then
  begin
    try
      ListBox1.Items.Clear;
      pWork := pBuffer;
      for i := 1 to dwEntriesRead do
      begin
        ListBox1.Items.Add(pWork.usri0_name);
        inc(pWork);
      end;
    finally
      NetApiBufferFree(pBuffer);
    end;
  end;
end;
Unter Verwendung dieser Deklarationen:

Delphi-Quellcode:
type
  // LMCons.h
  NET_API_STATUS = DWORD;

  // aus LMACCESS.H
  PUSER_INFO_0 = ^USER_INFO_0;
  USER_INFO_0 =
    packed record
      usri0_name : PWChar;
    end;

  PUSER_INFO_1 = ^USER_INFO_1;
  USER_INFO_1 =
    packed record
      usri1_name        : PWChar;
      usri1_password    : PWChar;
      usri1_password_age : DWord;
      usri1_priv        : DWord;
      usri1_home_dir    : PWChar;
      usri1_comment     : PWChar;
      usri1_flags       : DWord;
      usri1_script_path : PWChar;
    end;

const
  MAX_PREFERRED_LENGTH = DWORD(-1);
  FILTER_NORMAL_ACCOUNT = $0002;

  // LMERR.H
  NERR_Success = 0; // Success


// aus LMACCESS.H
function NetUserEnum(
    const servername   : PWChar;
    const level        : DWord;
    const filter       : DWord;
    const bufptr       : Pointer;
    const prefmaxlen   : DWord;
    const entriesread  : PDWord;
    const totalentries : PDWord;
    const resume_handle : PDWord
    ) : NET_API_STATUS; stdcall; external 'netapi32.dll';

// aus LMAPIbuf.h
function NetApiBufferFree(
    const Buffer : Pointer
    ) : NET_API_STATUS; stdcall; external 'netapi32.dll';

CalganX 23. Jan 2003 12:16

Hi Christian,
aufgrund der Tatsache, dass neuerdings beim Starten des Programmes wieder einfach nur das CPU-Fenster aufpoppt, werde ich mal deinen Source testen. Problem ist nur, dass der Typ "PUSER_INFO_0" bei mir nicht in der JwaLM (bzw. JwaLmAccess) steht. Habe aber bereits den Typ angelegt.

Problem ist bei mir, dass ich nicht einfach @aBuffer schreiben kann. Bei mir akzeptiert er nur Pointer(aBuffer). Daran liegt vermutlich auch der Fehler.

Werde in den nächsten Stunden mal rein schauen...

Chris

Christian Seehase 23. Jan 2003 12:56

Moin Chris,

deshalb hatte ich auch meine Deklarationen mit angegeben.
In die Jedi Sourcen habe ich noch nicht reingeschaut, deshalb weiss ich nicht, welche "Philosophie" dort vertreten wird, was die Deklaration der APIs angeht.

Ich halte mich nur möglichst an die Vorgaben aus dem PSDK, damit ich auch die Samples möglichst einfach nutzen kann.

Borland selber deklariert an den Stellen, an denen Werte von der Funktion geschrieben werden sollen die Parameter als var, was dann u.a. zur Folge hat, dass man sich gelegentlich Dummy Variablen anlegen muss, wenn man einen Parameter nicht nutzen will (also auf 0 oder nil setzen muss), was bei einem var Parameter ja nur geht, wenn eine entsprechende Variable angegeben wird.

Wichtig:
Wenn das Programm auch unter 9x/ME gestartet werden soll, müssen die Funktionen dynamisch importiert werden, und nicht, wie angegeben, statisch, damit sie ggf. weggelassen werden können.

CalganX 23. Jan 2003 12:58

Was meinst du mit "dynamisch" import? Meinst du Compilerschalter?

Chris

Chewie 23. Jan 2003 13:08

Zitat:

Zitat von Chakotay1308
Was meinst du mit "dynamisch" import? Meinst du Compilerschalter?

Chris

Es gibt zwei Möglichkeiten,, Funktionen aus DLLs zu importieren. Näheres bei delphi-source.de

Christian Seehase 23. Jan 2003 13:23

Moin Chris,

nein, ich meine Damit, dass Du einen Typ deklarierst, der der Funktionsdeklaration entspricht, dann eine Variable diese Typs, die entsprechende DLL aus der die Funktion stammt mittels LoadLibrary/LoadLibraryEx lädst, und dann die Adresse der Funktion mit GetProcAddress der Variablen zuweist.
NetUserEnum (und die Netapi32.dll) gibt's auf der 9x Schiene nicht, so dass bei statischem Import das Programm beim Starten sonst gleich mit einer Fehlermeldung abbricht, wenn die, statisch importierte, Funktion irgendwo angesprochen wird.

So könnte das Ganze dann mit dynamischem Import der Funktionen aussehen:

Delphi-Quellcode:
const
  MAX_PREFERRED_LENGTH = DWORD(-1);
  FILTER_NORMAL_ACCOUNT = $0002;
  NERR_Success         = 0;

type
  NET_API_STATUS = DWORD;

  TcsNetUserEnum = function(
    const servername   : PWChar;
    const level        : DWord;
    const filter       : DWord;
    const bufptr       : Pointer;
    const prefmaxlen   : DWord;
    const entriesread  : PDWord;
    const totalentries : PDWord;
    const resume_handle : PDWord
    ) : NET_API_STATUS; stdcall;

  TcsNetApiBufferFree = function(
    const Buffer : Pointer
    ) : NET_API_STATUS; stdcall;

  PUSER_INFO_0 = ^USER_INFO_0;
  USER_INFO_0 =
    packed record
      usri0_name : PWChar;
    end;

var
  NetUserEnum     : TcsNetUserEnum;
  NetApiBufferFree : TcsNetApiBufferFree;
  hDLL            : DWORD;

  pBuffer         : PUSER_INFO_0;
  pWork           : PUSER_INFO_0;
  dwEntriesRead   : DWORD;
  dwTotalEntries  : DWORD;
  i               : integer;

begin
  hDLL := LoadLibrary('netapi32.dll');
  if hDLL = 0 then
  begin
    exit;
  end;
  try
    NetUserEnum := GetProcAddress(hDLL,'NetUserEnum');
    if @NetUserEnum = nil then
    begin
      exit;
    end;
    NetApiBufferFree := GetProcAddress(hDLL,'NetApiBufferFree');
    if @NetApiBufferFree = nil then
    begin
      exit;
    end;
    // Nutzen der Funktion
    if NetUserEnum(nil,0,FILTER_NORMAL_ACCOUNT,@pBuffer,MAX_PREFERRED_LENGTH,@dwEntriesRead,@dwTotalEntries,nil) = NERR_Success then
    begin
      try
        ListBox1.Items.Clear;
        pWork := pBuffer;
        for i := 1 to dwEntriesRead do
        begin
          ListBox1.Items.Add(pWork.usri0_name);
          inc(pWork);
        end;
      finally
        NetApiBufferFree(pBuffer);
      end;
    end;
  finally
    FreeLibrary(hDLL);
  end;
end;
Wobei man natürlich den Import, bzw. Freigabe der DLL auch im initialization/finalization Abschnitt erledigen kann (sollte), und das dann in Abhängigkeit des Betriebssystemes, auf dem das Programm läuft.
Was, gerade bei den Netxxx Funktionen, oft eine Beschränkung bedeutet, da nicht alle dieser Funktionen unter 9x zur Verfügung stehen. (z.B. NetUserEnum)

Für 9x muss man sich dann ggf. noch eine Unterscheidung der Datenstrukturen einfallen lassen, da die MS Deklarationen sich nur in der Gross-/Kleinschreibung unterscheiden, was in Pascal ja nicht funktioniert.


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

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