Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi ADS - Fullname (https://www.delphipraxis.net/84996-ads-fullname.html)

MarLe 24. Jan 2007 13:32


ADS - Fullname
 
Hallo,

Hab ein ADS Beispiel ADS-Info heruntergeladen (Ich glaub von dieser Anfrage)
http://www.delphipraxis.net/internal...ct.php?t=95077

Bräuchte jetzt noch eine Funktion um über ADS den Fullname zu bekommen.
Meine Funktion schmiert mir aber mit einer Exception ab!?!

Delphi-Quellcode:
function TForm2.GetUserFullname(Domain, Username: String): string;
var
  usr: IADsUser;
  s: string;
begin
  result := 'Unknown';
  ADsGetObject('WinNT://' + Domain + '/' + Username, IADsUser, usr);
  assert(usr <> nil);
  s := usr.FullName;
  result := s;  // hier ist die Welt noch in Ordnung
end;            // -> Exceptions "access violation"
Gruss
MarLe

Udontknow 26. Jan 2007 09:31

Re: ADS - Fullname
 
Hallo!

Ist ein wenig ins Blaue hineingeraten, aber sehr wahrscheinlich handelt es sich bei IADSUser um ein Interface. Interfaces werden ja automatisch freigegeben, sobald sie out-of-scope gehen, das heisst, sobald der Compiler in diesem Falle die lokale Variable usr freigeben will, geschieht implizit ein Aufruf der Methode _Release.

Delphi-Quellcode:
ADsGetObject('WinNT://' + Domain + '/' + Username, IADsUser, usr);
try
  assert(usr <> nil);
  s := usr.FullName;
finally
  USR:=NIL; // <- Hier sollte der Fehler direkt auftauchen
end;
Und innerhalb dieser Release-Routine oder im Destruktor des dahinterliegenden Objektes kracht es dann wohl. Vielleicht gibt es eine explizite DeInit-Routine, die unbedingt vor Freigabe des Interfaces aufgerufen werden muss? Schau doch mal ins MSDN, vielleicht hilft dir das weiter.

Cu,
Udontknow

MarLe 26. Jan 2007 12:08

Re: ADS - Fullname
 
Danke für den Tipp.

IADsUser ist ein Interface

Delphi-Quellcode:
// *********************************************************************//
// Interface: IADsUser
// Flags:    (4416) Dual OleAutomation Dispatchable
// GUID:     {3E37E320-17E2-11CF-ABC4-02608C9E7553}
// *********************************************************************//
  IADsUser = interface(IADs)
    ['{3E37E320-17E2-11CF-ABC4-02608C9E7553}']
    function Get_FullName: WideString; safecall;
    procedure Set_FullName(const retval: WideString); safecall;
    property FullName: WideString read Get_FullName write Set_FullName;
    ...
Ein usr := nil; hat nix gebracht.

Du hast schon recht wenn ich ein Objekt instantiere (geschieht hier anscheinend über die Hilfsmethode ADsGetObject)
muss ich auch dann den reservierten Speicher wieder freigegeben.
Hab da noch folgendes entdeckt
Delphi-Quellcode:
function FreeADsMem(aPtr: Pointer): BOOL; forward;
Diese Funktion wird in dem besagten Downloadbeispiel aber nirgends benutzt?!?

Udontknow 26. Jan 2007 16:33

Re: ADS - Fullname
 
Du hast mich ein wenig falsch verstanden. Da es ein Interface ist, musst du normalerweise eben gerade NICHT dafür sorgen, das Objekt eigenhändig freizugeben. Interfaces verfügen über eine Referenz-Zählung, sobald sie feststellen, daß von aussen keine Objekte mehr das Interface "offenhalten", wird automatisch der Destruktor des Objekts, das dieses Interface unterstützt, aufgerufen. Und da vermute ich auch den Fehler. Evtl. benutzt du eine falsche Version der ActiveX-DLL, in der das Objekt implementiert ist? Vielleicht gibt es neue Interfaces, die stattdessen zu nutzen sind, wobei es dann aber eigentlich unsinnig ist, das Objekt weiterhin das Interface unterstützen zu lassen, wenn es dann fehlerhaft arbeitet...

Mit dem Code wollte ich dir lediglich zeigen, daß es daran liegt, daß das Interface freigegeben wird, es sollte keine Lösung sein.
Ein hässlicher und unsauberer Workaround wäre natürlich ein try/except um das NIL-Setzen der Interface-Variable, aber wenn sonst nichts hilft...

Cu,
Udontknow

Udontknow 26. Jan 2007 17:16

Re: ADS - Fullname
 
Hallo nochmals,

habe mir mal die Mühe gemacht, dasselbe ohne diese Wrapper-Dateien zu machen. Du musst zuerst aber die TLB-Datei erzeugen:
Menü\Komponente Importieren\Typbibliothek importieren, anschliessend "Active DS Type Library" auswählen. Zweimal weiter klicken und Fertig stellen, anschliessen wird eine Unit mit Namen ActiveDS_TLB angelegt, diese solltest du dann in einem neuen Projekt in die uses-Klausel mitaufnehmen.

Die ADSGetObject-Routine habe ich einfach statisch ins Hauptformular eingebunden:
Delphi-Quellcode:
function ADsGetObject(pwcPathName: PWideChar; const xRIID: TGUID; var pVoid): HResult; stdcall; external 'activeds.dll';
Das ist der Code, den ich für eine Namensauflösung dann einsetze:
Delphi-Quellcode:
var
  usr: IADSUser;
  pc:array[0..255] of WideChar;
  s:String;
begin
  s:='WinNT://'+Edit_Domain.Text+'/'+Edit_Kennung.Text;
  StringToWideChar(S,PC,Length(S)+1);

  ADsGetObject(pc, IADsUser, usr);
  if not Assigned(usr) then
    Edit_Name.Text := 'Unknown'
  else
    Edit_Name.Text := usr.FullName;
end;
Bei mir klappts. Allerdings habe ich ganz schöne Schwierigkeiten, von normalen Strings in PWideChar umzuwandeln...

Cu,
Udontknow

MarLe 31. Jan 2007 14:47

Re: ADS - Fullname
 
Danke für deine Mühe.
Deine Methode funktioniert zwar, allerdings sind in der adshlp.pas noch einige andere Methoden die in dem Beispiel benutzt werden, die müsste ich auch alle umlegen.

Ehrlich gesagt blicke ich da nicht ganz durch warum es in der ursprünglichen Variante am Ende kracht.
Wenn ich die Funktion ohne Rückgabewert deklariere und den Wert innerhalb der Funktion ausgebe z.B. in einem Memo kracht's nicht.
Liegt's vielleicht an der Stringumwandlung ???

Alter Mann 31. Jan 2007 19:15

Re: ADS - Fullname
 
Hallo MarLe,

ich möchte es mal so erklären.

IADsUser ist ein Interface, dh. die darüber referenzierten Objekte sind tasächlich schon vorhanden.
In diesem Fall im Active Directory. Über ADsGetObject holst Du dir einen 'Zugriff' auf das Objekt
und möchtest eine bestimmtem Wert zwischenspeichern.
Delphi-Quellcode:
  ADsGetObject('WinNT://' + Domain + '/' + Username, IADsUser, usr);
  assert(usr <> nil);
  s := usr.FullName;
Da Referenzen auf Interface-Objecte nicht frei gegeben werden (müssen/können/dürfen); i.d. Fall dürfen,
da ansonsten das referenzierte Objekt in der ADS freigegeben wird, wird legendlich die Referenz auf
das Objekt gelöscht. Sprich "usr = nil", ohne Dein zutun, da usr nicht mehr benötigt wird.

Da jedoch S := usr.FullName; auch nur eine Referenz ist und damit auch
Delphi-Quellcode:
  Result := S;
werden auch diese Referenzen gelöscht, und es kommt zur Zugriffsverletzung.

Ich hoffe es bringt etwas für das Verständnis.

Vielleicht Morgen etwas mehr.

Gruss

MarLe 1. Feb 2007 12:31

Re: ADS - Fullname
 
Aha, das leuchtet mir ein.
Allerdings hatte ich gedacht dass ich mit der Zuweisung auf eine Variable den Wert bekomme.
Sonst hätte ich ja auch nur
Delphi-Quellcode:
result := usr.FullName;
verwenden können.
d.h. jetzt stellt sich die Frage wie ich aus dieser Referenz zu einer normalen Stringvariable komme?

Udontknow 1. Feb 2007 13:30

Re: ADS - Fullname
 
Zitat:

Zitat von Alter Mann


Da jedoch S := usr.FullName; auch nur eine Referenz ist und damit auch
Delphi-Quellcode:
  Result := S;
werden auch diese Referenzen gelöscht, und es kommt zur Zugriffsverletzung.

Hallo!

Ich habe immer gedacht, ich verstehe etwas von Interfaces, aber du bringst mich ziemlich in Verwirrung.

S := usr.FullName; ist doch letztendlich nichts anderes als der Aufruf der im Interface deklarierten Methode Get_FullName und eben Zuweisung des Ergebnisses dieser Methode an eine Variable. Was soll an diesem Befehl denn nun eine Referenz sein? Und was soll denn da an Referenzen gelöscht werden?
Solange die Variable USR weder auf NIL gesetzt wird noch "out of scope" geht (also z.B. bei lokalen Variablen beim Verlassen einer Prozedur), wird da keine Referenz freigegeben.

Was genau willst du also eigentlich sagen? Eine Erklärung, wieso es bei meiner Variante klappt, und bei der ursprünglichen nicht, sehe ich hier nicht...

Cu,
Udontknow

marabu 1. Feb 2007 13:31

Re: ADS - Fullname
 
Hallo,

ihr bringt mich alle ziemlich in Verwirrung. Ich würde es mal so versuchen:

Delphi-Quellcode:
function GetUserFullname(Domain, Username: string): string;
var
  usr: IADsUser;
  ws: WideString;
begin
  ws := 'WinNT://' + Domain + '/' + Username;
  if ADsGetObject(PWideChar(ws), IID_IADsUser, usr) = S_OK
    then Result := usr.FullName
    else Result := 'Unknown';
end;
Getippt und nicht getestet.

Grüße vom marabu

Udontknow 1. Feb 2007 13:42

Re: ADS - Fullname
 
Ah, ein direkter Cast von Widestring von PWideChar ist das Geheimnis, obskure Array of WideChar zu vermeiden! Schön zu wissen. :)

Aber was ist mit dieser Zugriffsschutzverletzung? Hast du eine Ahnung, warum sie in der ursprünglichen Variante auftritt?

Cu,
Udontknow

marabu 1. Feb 2007 18:28

Re: ADS - Fullname
 
Hallo,

nicht wirklich. Ich weiß ja nicht einmal, ob mein Code fehlerfrei läuft. Hast du das testen können?

Was mir beim Vergleich auffällt ist, dass im Beitrag #1 nicht die IID übergeben wird, sondern ein Interface.

Freundliche Grüße

Alter Mann 1. Feb 2007 18:29

Re: ADS - Fullname
 
HI,

für Verwirrung wollte ich nciht sorgen. Es ist ein 'Versuch' einer Erklärung mit 'einfachen' Worten.

@Udontknow

Dein Beispiel verwendet ein TEdit mit der Text-Eigenschaft, dh. wenn Text := usr.FullName; gesetzt wird,
wird eine Windowsbotschaft an das Steuerelement gesendet, in diesem WM_SETTEXT.
Damit wird der Inhalt von usr.FullName nach Edit_Name.Text kopiert.
Bei der Zuweisung 'S := usr.FullName' passiert das ebend nicht und bei der Zuweisung von 'Result := S;'
kracht es ebend, das liegt an der Bedeutung von Result für den Compiler und dem lösen der Bindung von usr.

Wie gesagt/geschrieben mit 'einfachen' Worten.

Bei WideString greift ein anderer Mechanismus(auch ein kopieren der Inhalte von Source nach Dest), deshalb
auch keine Exception. Aus diesem Grund kommt es auch zur keiner Exception beim Aufruf einer Callback-Funktion.

Gruss

Alter Mann

marabu 1. Feb 2007 18:48

Re: ADS - Fullname
 
Hallo,

Zitat:

Zitat von Alter Mann
... Damit wird der Inhalt von usr.FullName nach Edit_Name.Text kopiert.
Bei der Zuweisung 'S := usr.FullName' passiert das ebend nicht ...

bist du da sicher? Copy-On-Demand in Verbindung mit dem Result einer access method?

Nachdenkliche Grüße von einem noch älteren Mann

Alter Mann 1. Feb 2007 19:06

Re: ADS - Fullname
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ja, siehe Anhang.

marabu 1. Feb 2007 19:59

Re: ADS - Fullname
 
Hallo,

der markierte Assembler-Code sagt für meine Begriffe nichts zum Thema aus: Es wird geprüft, ob der auf dem Stack obenauf liegende Zeiger 0 ist. Copy-On-Demand bei String-Variablen findet nach meiner Auffassung de facto nur bei direkten Zuweisungen statt.

Zweifelnde Grüße

Alter Mann 1. Feb 2007 20:59

Re: ADS - Fullname
 
Hallo marabu,

wie Du meinst.

Ich bezog mich auf zwar auf TControl.SetText, aber ich möchte Dir da nicht (...) reinreden.

Bis zu nächsten Mal/Thema/Thread usw:

Gruss

Alter Mann

Udontknow 1. Feb 2007 21:10

Re: ADS - Fullname
 
Hallo!

Ich habe auf der Arbeit auch noch mal die Funktion ausgelagert, sodaß eben eine Result-Variable vom Typ String mit dem Inhalt Usr.FullName gefüllt wird. Keine Exception. Ausserdem findet doch dieser Referenz-Zähl-Mechanismus nur bei Strings statt, aber Usr.FullName ist ein Widestring...

Na wer weiss, vielleicht habe ich da auch wieder irgendwas anders gemacht, ich poste sie morgen mal.

Cu,
Udontknow

marabu 1. Feb 2007 21:23

Re: ADS - Fullname
 
Hallo Alter Mann,

hier gibt es kein "Reinreden" - wir diskutieren hier ein Problem und sollte ich mich irren, dann nicht zum ersten Mal. In Beitrag #14 habe ich doch deine Aussage zitiert, die ich für überdenkenswert hielt. Das von dir bereit gestellte CPU-Fenster zeigt genau zu diesem Sachverhalt nichts. Dass TControl.SetText für die Zuweisung per property setter aufgerufen wird war unstrittig.

@Udontknow: Danke für die Rückmeldung.

Freundliche Grüße

MarLe 2. Feb 2007 09:17

Re: ADS - Fullname
 
Erstmals Danke für Eure Mühe.
Hab mal die Variante von marabu probiert.
Leider kracht's genau an der gleiche Stelle?!?

Udontknow 2. Feb 2007 10:55

Re: ADS - Fullname
 
Hallo!

Also, ich habe sowohl meine eigene (umständliche) Routine als auch die von Marabu ausprobiert, wohlgemerkt ohne die ADSHelper-Units, und ohne Exceptions zu erhalten. Kann es sein, daß die Funktionen in der Helper-Unit falsch abgebildet werden oder so?

Delphi-Quellcode:
function GetUsername(Domain,Kennung:String):String;
var
  usr: IADSUser;
  pc:array[0..255] of WideChar;
  s:String;
begin
  s:='WinNT://'+Domain+'/'+Kennung;
  StringToWideChar(S,PC,Length(S)+1);

  ADsGetObject(pc, IADsUser, usr);
  if not Assigned(usr) then
    Result := 'Unknown'
  else
    Result := usr.FullName;
end;

function GetUserFullname(Domain, Username: string): string;
var
  usr: IADsUser;
  ws: WideString;
begin
  ws := 'WinNT://' + Domain + '/' + Username;
  if ADsGetObject(PWideChar(ws), IID_IADsUser, usr) = S_OK
    then Result := usr.FullName
    else Result := 'Unknown';
end;

procedure TForm54.BTN_SuchenClick(Sender: TObject);
var
  usr: IADSUser;
  pc:array[0..255] of WideChar;
  s:String;
begin
  Edit_Name.Text:=GetUserName(Edit_Domain.Text,Edit_Kennung.Text);
  Edit_Name2.Text:=GetUserFullName(Edit_Domain.Text,Edit_Kennung.Text);
end;
Cu,
Udontknow

MarLe 2. Feb 2007 11:17

Re: ADS - Fullname
 
Hmm, jetzt hab ich mir nochmals die Units angeschaut die bei dem Beispiel dabei waren.
Wobei dabei anscheinend adhelper zu adshelp modifiziert wurde (das Datum ist aktueller).
Welche hast du bei dem Bsp von marabu verwendet???
Kannst du mal deine Variante (gezippt) von marabu hier reinhängen.

Danke MarLe

Alter Mann 2. Feb 2007 18:38

Re: ADS - Fullname
 
Hallo Udontknow,

ich bin nicht der ASM-Insider, weit gefehlt und ich möchte auch nicht deine Erfahrungen mit
Interface's in Frage stellen, die wirst du schon haben. Soweit ich weiss hast Du was
die Gültigkeit der Interface-Refrenzen betrifft auch Recht. Trozdem weiterlesen;-)

Es ging darum, warum eine Exception aus gelöst wird bei der Zuweisung Result := S;.
Delphi-Quellcode:
function TForm2.GetUserFullname(Domain, Username: String): string;
var
  usr: IADsUser;
  s: string;
begin
  result := 'Unknown';
  ADsGetObject('WinNT://' + Domain + '/' + Username, IADsUser, usr);
  assert(usr <> nil);
  s := usr.FullName;
  result := s;  // hier ist die Welt noch in Ordnung
end;
Sicherlich, und das ist eine Annahme, soll der Rückgabewert zB. einem TEdit.Text, TLabel.Caption usw. übergeben werden.
Wie der CPU-Dump gezeigt hat wird TControl.SetText aufgerufen. Um die ganze Sache genauer zu prüfen habe
ich das Nachgestellt. Verwendet habe ich die adshlp.pas vom 20.09.2004.
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, adshlp, ActiveDs_TLB, StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private-Deklarationen }
    S  : String;
    function GetFullNameByName(Domain : String; Name : String) : String;
    procedure FullNameByName(Domain : String; Name : String);
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Edit3.Text := '';
  Edit3.Text := GetFullNameByName(Edit2.Text, Edit1.Text);
end;

function TForm1.GetFullNameByName(Domain : String; Name : String) : String;
var
  usr : IADsUser;
begin
  ADsGetObject('WinNT://' + Domain + '/' + Name, IADsUser, usr);
  Assert(usr <> nil);
  S := usr.FullName;
  Result := S;
end;

procedure TForm1.FullNameByName(Domain : String; Name : String);
var
  usr : IADsUser;
begin
  ADsGetObject('WinNT://' + Domain + '/' + Name, IADsUser, usr);
  Assert(usr <> nil);
  Edit3.Text := usr.FullName;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  FullNameByName(Edit2.Text, Edit1.Text);
end;
Bei der Zuweisung von Result := S; kamm es zur Exception, wie bei MarLe.
Daraufhin habe ich das ganze mit OllyDebug durchlaufen lassen und folgende Feststellung gemacht.

Wenn TControl.SetText aufgerufen wird und man dorthinein verzweigt, stellt man Fest das ein Aufruf
von Tcontrol.GetText erfolgt und die Exception auslöst.

Um das Problem zu ergründen habe ich in der Unit Controls nachgesehen, was dort passiert.
Es wird mittels Perfom die WM_GETTEXTLENGTH-Message an das, dass Ergebnis erhaltene Control gesenden, um
die Textlänge zu ermitteln. Anschließend wird SetString aufgerufen, welche den Inhalt und die Länge des String
setzt
Zitat:

...S auf einen eindeutigen String zeigt (d.h. dessen Referenzzähler den Wert Eins hat)
Und dabei kommt es zur Exception.

Alles klar.?


Gruss
Alter Mann

PS Den genauen Grund der Exception konnte ich nicht heraubekommen, wie geschrieben: Ich bin kein ASM-Insider.


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:30 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