Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Daten aus der Registry lesen mit TService (https://www.delphipraxis.net/89242-daten-aus-der-registry-lesen-mit-tservice.html)

fips0705 28. Mär 2007 09:46


Daten aus der Registry lesen mit TService
 
Hallo,

ich versuche einen NT-Dienst zu schreiben, in dem ich prüfe ob ein bestimmter Rechner noch über einen Ping im Netzwerk zu erreichen ist. Dies funktioniert auch sehr gut. Der Dienst lässt sich starten und stoppen, nur wenn ich die gespeicherten Daten aus der Registry lesen will tut sich gar nichts. Er liest die Daten nicht.
Wenn ich den Abschitt für das lesen der Registry deaktiviere, funktioniert die Routine, so wie jetzt wird der Timer nicht vollständig durchlaufen.
Oder kann man im TService nicht in der Registry lesen.
Wo habe ich da einen Denkfehler.
Delphi-Quellcode:
 

//------------------------------------------------------------------------------
// Arbeitsroutine

procedure TService.Timer1Timer(Sender: TObject);
var
  Regi: TRegistry;

const
FileName = 'c:\temp\pinglog.txt';

begin
If RegRead=True then
begin

  Regi := TRegistry.Create;
 try
  Regi.RootKey := HKEY_LOCAL_MACHINE;
  if Regi.OpenKey('SOFTWARE\JF\AutoDown', True) then
   begin
    MIP:=regist.ReadString('IP');
    MZeit:=regist.ReadString('Zeit');
   end
  finally
   regist.Free;
 end;



if MIP = '' then
 begin
     MIP := '127.0.0.1';
     MZeit := '30';
 end;

// ReceiveTimeout einstellen
 ICMP_Service.ReceiveTimeout := 1000;
// Zähler für Shutdown auf null setzen;
 MZaehler:=0;
 RegRead:=False;
end;

AssignFile(f,FileName);
if FileExists(FileName) then Append(f)
else Rewrite(f);

// Ping starten

ICMP_Service.Host := MIP;
// Ping senden
ICMP_Service.Ping;

if ICMP_Service.ReplyStatus.FromIpAddress = '0.0.0.0' then
  begin
  if MZaehler <= strtoint(MZeit)  then
    begin
     MZaehler:=MZaehler+1;
     IF MZaehler < strtoint(MZeit) then
       writeln(f,inttostr(Mzaehler)+' Ping an IP ' +MIP + ' nicht i.o. ' + datetimetostr(now()))
      else
       begin
       writeln(f,'Shutdown wird eingeleitet '+ inttostr(Mzaehler)+' Ping an IP ' +MIP + ' nicht i.o. ' + datetimetostr(now()));
       CloseFile(f);
//       ExWindows(EWX_Poweroff); //Herunterfahren und Ausschalten
       end;
      end;
  end
else
  begin
// Zähler für Shutdown auf 0 setzen;
    MZaehler:=0;
    writeln(f,'Ping an IP ' +ICMP_Service.ReplyStatus.FromIpAddress + ' ok.');

  end;

CloseFile(f);
end;

Luckie 28. Mär 2007 09:57

Re: Daten aus der Registry lesen mit TService
 
Ich frage mich, ob man in einem Service überhaupt einen Timer benutzen kann, denn an welches Fenster soll er denn seine WM_TIMER Nachricht schicken? Versuch doch mal, nur zum Testen ausserhalb der Timer-Prozedur auf die Registry zu zugreifen und schreib parallel eine Log-Datei, damit du siehst, wo der Service gerade ist.

fips0705 28. Mär 2007 10:04

Re: Daten aus der Registry lesen mit TService
 
ja ausserhalb des Timers funktioniert das lesen aus der Registry einwandfrei,
auch so funktioniert es.
Delphi-Quellcode:
if FileExists(FileName) then Append(f)
else Rewrite(f);
writeln(f,'timerstart');
If RegRead=True then
begin
//
  Regi := TRegistry.Create;
  writeln(f,'create');
 try
   writeln(f,'try');
//  Regi.RootKey := HKEY_LOCAL_MACHINE;
//  if Regi.OpenKey('SOFTWARE\JF\AutoDown', True) then
//   begin
//      writeln(f,'lese');
//    MIP:=regist.ReadString('IP');
//    MZeit:=regist.ReadString('Zeit');
//   end
  finally
    writeln(f,'finally');
//   regist.Free;
 end;

fips0705 28. Mär 2007 10:21

Re: Daten aus der Registry lesen mit TService
 
Hallo Luckie,

ich habe hier im Forum gelesen, das man einen Timer im tService benutzen kann, aber nicht muss, aber wo soll ich denn dann aus der Registry lesen?

ich habe den Fehler jetzt so weit eingegenzt, dass ich weiss das es an

Delphi-Quellcode:
    MIP := regist.ReadString('IP');
    MZeit:= regist.ReadString('Zeit');
nur verstehen tue ich das nicht da die Werte ja in der Registry stehen :gruebel:

Gruss Jörg

Igotcha 28. Mär 2007 11:29

Re: Daten aus der Registry lesen mit TService
 
Delphi-Quellcode:
var
  Regi: TRegistry;
...
  Regi := TRegistry.Create;
try
  Regi.RootKey := HKEY_LOCAL_MACHINE;
  if Regi.OpenKey('SOFTWARE\JF\AutoDown', True) then
   begin
    MIP:=regist.ReadString('IP');
    MZeit:=regist.ReadString('Zeit');
   end
  finally
   regist.Free;
Du initialisiert "Regi", benutzt später aber "regist".

Gruß Igotcha

fips0705 28. Mär 2007 11:53

Re: Daten aus der Registry lesen mit TService
 
Vielen Dank Igotcha,

manchmal sieht man den Wald vor Bäumen nicht.

jetzt funktioniert es auch, warum sollte es auch nicht :lol:

Schwedenbitter 8. Apr 2007 22:30

Re: Daten aus der Registry lesen mit TService
 
Hallo,

ich habe es mit den o.g. Tips ausprobiert, aber bislang keinen Erfolg gehabt.

Zur Vereinfachung und auch für mein Verständnis habe ich mir jetzt einen Dienst geschrieben, der nur einen Registry-Wert (ist vorhanden) ausliest und in einer Log-Datei das Ergebnis vermerkt und sonst nichts weiter macht. Die einzige Procedure die ich hinzugefügt habe, ist die für das Ereignis OnStart.
Ich habe denselben Code in einer Anwendung gehabt. Dort hat das alles prima geklappt. Nach dem Portieren in den Dienst geht das mit dem Auslesen der Registry nicht mehr.

Delphi-Quellcode:
Procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
Var
   Reg      : TRegistry;
   Quelle   : String;
   F        : Text;
Begin
   Reg:=TRegistry.Create;
   Try
      Reg.RootKey:=HKEY_CURRENT_USER;
      If Reg.OpenKeyReadOnly('Software\Olympus\DSSPlayer2002\Dictation
                              Module\DownLoad\InstallSetting') Then
         Quelle:=Reg.ReadString('DictationPath')+'\'
      Else
         Quelle:='Error';
   Finally
      Reg.Free;
   End;
   AssignFile(F,'Test.log');
   {$I-}
   ReWrite(F);
   WriteLn(F,Quelle);
   CloseFile(F);
   {$I+}
End;
Wo liegt mein Fehler? Bin völlig ratlos!

TBx 8. Apr 2007 22:35

Re: Daten aus der Registry lesen mit TService
 
@Schwedenbitter: Ich gehe davon aus, dass Du Deinem Service keinen User zugewiesen hast. Dadurch wird als User System verwendet. Dieser User ist nicht in der Lage, Deinen User-Bereich auszulesen.
Deine Abfrage sollte funktionieren, wenn Du dem Service Deinen Useraccount zuweist.

Gruß

Thomas

Olli 9. Apr 2007 02:39

Re: Daten aus der Registry lesen mit TService
 
Zitat:

Zitat von onlinekater
Dieser User ist nicht in der Lage, Deinen User-Bereich auszulesen.

Falsch. Aber er ist in disem Kontext nicht als HKCU verlinkt!

Es ist sogar noch besser. Als Teil der TCB hat man sogar Backup/Restore-Rechte und kann damit Registry-Hives mounten. Damit kann man also die NTUSER.DAT eines beliebigen Benutzers einbinden. Aber gehen wir mal davon aus, daß der Benutzer dessen Registrywerte den Service interessieren im Moment eingeloggt ist und damit sein Profil (NTUSER.DAT) geladen ist. In diesem Fall hole man sich die SID des Benutzers (notfalls auch den vollen Namen) und lese den entsprechenden Wert unter HKEY_USERS aus.

Beispiel. Insofern "Administrator" gerade eingeloggt ist und es sich um das eingebauten Adminkonto handelt, wird der Schlüssel
HKEY_USERS\S-1-5-21-??????????-??????????-??????????-500
das gleiche enthalten wie
HKEY_CURRENT_USER
im Kontext dieses Benutzers. Probiert es einfach aus.

Zitat:

Zitat von Luckie
Ich frage mich, ob man in einem Service überhaupt einen Timer benutzen kann, denn an welches Fenster soll er denn seine WM_TIMER Nachricht schicken?

Bekanntermaßen kann ein Timer auch eine Callback-Funktion benutzen und damit vollkommen unabhängig von einem Fenster agieren.
Zitat:

[in] Handle to the window to be associated with the timer. This window must be owned by the calling thread. If this parameter is NULL, no window is associated with the timer and the nIDEvent parameter is ignored.
Übrigens gibt es für die Registry genau wie für Verzeichnisse/Dateien Benachrichtigungsfunktionen, womit man sich den Timer spart und es elegant und vor allem zeitgemäß löst. Timer sind nicht gerade ressourcenschonend :roll:

Schwedenbitter 9. Apr 2007 08:42

Re: Daten aus der Registry lesen mit TService
 
Zitat:

Zitat von Olli
...
Insofern "Administrator" gerade eingeloggt ist und es sich um das eingebauten Adminkonto handelt, wird der Schlüssel
HKEY_USERS\S-1-5-21-??????????-??????????-??????????-500
das gleiche enthalten wie
HKEY_CURRENT_USER
im Kontext dieses Benutzers. Probiert es einfach aus.

Würde ich gern! Ich habe nur nicht die geringste Ahnung, wie ich das machen soll. Ich weiß noch nicht einmal, wo in der Hilfe ich nachschauen bzw. nach welchen Stichworten ich suchen sollt.

P.S.
Ich verstehe auch nicht ganz, warum ich das mit meiner Anwendung prima auslesen kann. Sofern das SYSTEM quasi als User benutzt wird, wäre doch davon auszugehen, dass dieses ziemlich viel Rechte hat und die Werte deshalb lesen kann ...

Olli 9. Apr 2007 15:07

Re: Daten aus der Registry lesen mit TService
 
Zitat:

Zitat von Schwedenbitter
Würde ich gern! Ich habe nur nicht die geringste Ahnung, wie ich das machen soll. Ich weiß noch nicht einmal, wo in der Hilfe ich nachschauen bzw. nach welchen Stichworten ich suchen sollt.

???

Zitat:

Zitat von Schwedenbitter
Ich verstehe auch nicht ganz, warum ich das mit meiner Anwendung prima auslesen kann. Sofern das SYSTEM quasi als User benutzt wird, wäre doch davon auszugehen, dass dieses ziemlich viel Rechte hat und die Werte deshalb lesen kann ...

Hat ja niemand gesagt, daß du nicht die Rechte hast wenn du im SYSTEM-Kontext unterwegs bist, nur hast du definitiv ein anderes Mapping der Win32-Rootschlüssel. Aber ich wiederhole mich bereits.

Schwedenbitter 9. Apr 2007 17:39

Re: Daten aus der Registry lesen mit TService
 
Zitat:

Zitat von Olli
HKEY_USERS\S-1-5-21-??????????-??????????-??????????-500

Wie bekomme ich diesen Nummern-Schlüssel raus? Dass dieser unterschiedlich ist/sein kann, entnehme ich jedenfalls den Fragezeichen. Und ich wollte mein Programm so schreiben, dass es auf verschiedenen Rechnern mit verschiedenen Windows-Installationen laufen kann.

Olli 9. Apr 2007 20:32

Re: Daten aus der Registry lesen mit TService
 
Ich zitiere mich mal selber:

Zitat:

Zitat von Olli
In diesem Fall hole man sich die SID des Benutzers (notfalls auch den vollen Namen) und lese den entsprechenden Wert unter HKEY_USERS aus.

Das letzte Element (die 500) ist der Standardwert für das eingebaute Adminkonto. Dieser Wert ist also auch variabel, je nachdem für welchen Benutzer du die SID ermittelst.

Schwedenbitter 10. Apr 2007 07:37

Re: Daten aus der Registry lesen mit TService
 
So, ich habe mir im Netz jetzt folgenden Quellcode zusammengesucht bzw. gebastelt:

Delphi-Quellcode:
Function GetSID:String;
   Function TokenToUserSidString(Token:THandle):String;
   Const
      SECURITY_NT_AUTHORITY = 5;
   Type
      PSID        = ^TSID;
      TSID        = Packed Record
                     Revision             : Byte;
                     SubAuthorityCount    : Byte;
                     IdentifierAuthority  : TSIDIdentifierAuthority;
                     //  [0..SubAuthorityCount - 1]
                     SubAuthority         : Array [Byte] Of DWORD;
                   End;
      PTokenUser  = ^TTokenUser;
      TTokenUser  = Record
                      User  : TSIDAndAttributes;
                     End;
   Var
      User        : PTokenUser;
      Size        : DWORD;
      Loop        : Byte;
   Begin
      Result:='';
      Size:=4096;
      GetMem(User,Size);
      Try
         FillChar(User^,Size,0);
         If GetTokenInformation(Token,TokenUser,User,Size,Size) Then
            With PSID(User^.User.Sid)^ Do
            Begin
               Result:='S-'+IntToStr(Revision)+'-'+
                        IntToStr(IdentifierAuthority.Value[SECURITY_NT_AUTHORITY]);
               For Loop:=0 To SubAuthorityCount-1 Do
                  Result:=Result+'-'+IntToStr(SubAuthority[Loop]);
            End;
      Finally
         FreeMem(User);
      End;
   End;
Var
   Token           : THandle;
Begin
   If OpenProcessToken(GetCurrentProcess,TOKEN_QUERY,Token) Then
   Begin
      Try
         Result:=TokenToUserSidString(Token);
      Finally
         CloseHandle(Token);
      End;
   End      
   Else
      Result:='Error';
End;
In meiner Anwendung funktioniert das und ich bekomme die lange Nummer. Wenn ich das in meinen Dienst einbaue, erhalte ich als Ergebnis immer S-1-5-18. Liegt vermutlich daran, dass der Dients als Benutzer System hat.

Wie kann ich das ändern?

Olli 10. Apr 2007 09:02

Re: Daten aus der Registry lesen mit TService
 
Bequemer geht's mit MSDN-Library durchsuchenConvertSidToStringSid (ab Windows 2000).

Zitat:

Zitat von Schwedenbitter
In meiner Anwendung funktioniert das und ich bekomme die lange Nummer. Wenn ich das in meinen Dienst einbaue, erhalte ich als Ergebnis immer S-1-5-18. Liegt vermutlich daran, dass der Dients als Benutzer System hat.

Wie kann ich das ändern?

Ganz einfach, du benutzt MSDN-Library durchsuchenLookupAccountName um die SID eines beliebigen Benutzers zu ermitteln - welcher Benutzer das ist, dafür wirst du ja wohl ein Kriterium haben.

Schwedenbitter 10. Apr 2007 11:07

Re: Daten aus der Registry lesen mit TService
 
Danke!

Jetzt habe ich nur noch ein Problem: Welche der vielen Units, die sich in Remkos Beispiel finden, muss ich benutzen, damit die Variable vom Typ TSidNameUse definiert ist? Diese brauche ich für LookupAccountName.

Luckie 10. Apr 2007 12:10

Re: Daten aus der Registry lesen mit TService
 
Guck doch einfach nach. :roll:

Olli 10. Apr 2007 12:15

Re: Daten aus der Registry lesen mit TService
 
Schonmal was von GREP gehoert? Ich persoenlich kann PowerGREP3 empfehlen - benutze es fast jeden Tag. Die perfekte Loesung fuer grosse Quelltextverzeichnisse usw.

Vermutlich ist es JwaNtSecApi - aber wie gesagt, am einfachsten ist wenn du dich auf unsere Projektseite begibst und dort mal eben das ganze Paket runterlaedst und dort nach der Unit suchst in welcher die Funktion deklariert ist. Verdammt, das erinnert mich, dass ich mal wieder ein neues aktualisiertes Paket releasen muss :-|

Schwedenbitter 10. Apr 2007 21:57

Re: Daten aus der Registry lesen mit TService
 
@Olli: Danke für die Hilfe.

Ich habe die Pakete jetzt geladen und das Verzeichnis meines Projektes kopiert. Außerdem habe ich den Teil des Quellcodes von Remko unter meinem o.g. Link kopiert, der die Uses-Anweisungen enthält.
Während des Kompilierens bekomme ich die Fehlermeldung, dass JwaWinSta.dcu nicht gefunden wurde. Eine entsprechende Datei (JwaWinSta.pas) habe ich in den Paketen auch nicht finden können. Scheinbar kommt es aber genau auf diese an. Denn wenn ich JwaWinSta aus den zu verwendenden Units entferne, bekomme ich die Fehlermeldung Undefinierter Bezeichner 'SidToStr'.

Ich habe auch Remkos Beispielprogramm runtergeladen und getestet. Abgesehen davon, dass bei mir die letzte Zahl nicht stimmt, statt der angezeigten 500 steht wie im Registrierungseditor 1003, lässt sich das Programm auch wegen der fehlenden JwaWinSta nicht kompilieren.

Jetzt bin ich völlig verwirrt.

Schwedenbitter 12. Apr 2007 14:00

Re: Daten aus der Registry lesen mit TService
 
Ich habe mir jetzt die Function SidToStr selbst zusammengebastelt, nachdem ich sie nirgends finden so oder in einer Unit konnte. Für den Fall, dass jemand Interesse hat:
Delphi-Quellcode:
Function SIDToStr(SID:Windows.PSID):WideString;
Var
   SIA    : PSIDIdentifierAuthority;
   dwCount : Cardinal;
   I      : Integer;
Begin
   If Not isValidSID(SID) Then
      Result:='Error'
   Else
   Begin
      Result:='S-'+IntToStr(Byte(SID^))+'-';
      SIA:=GetSIDIdentifierAuthority(SID);
      Result:=Result+IntToStr(SIA.Value[5]);
      dwCount:=GetSIDSubAuthorityCount(SID)^;
      For I:=0 To (dwCount-1) Do
         Result:=Result+'-'+IntToStr(GetSIDSubAuthority(SID,I)^);
   End;
End;
Allerdings stehe ich vor demselben Problem wie das Programm LSALogon von Remko. Die letzten drei bzw. vier Ziffern des SID stimmen nicht. Damit komme ich nicht in diesen Unterschlüssel rein.
Ich habe jetzt auf mehreren Rechnern festgestellt, dass es neben der 1003 auch noch mehrere Möglichkeiten gibt. Meine Idee, die 500 einfach durch eine 1003 zu ersetzen, geht damit leider auch nicht mehr. Schade eigentlich.

Luckie 12. Apr 2007 14:05

Re: Daten aus der Registry lesen mit TService
 
Zitat:

Zitat von Schwedenbitter
Ich habe mir jetzt die Function SidToStr selbst zusammengebastelt, nachdem ich sie nirgends finden so oder in einer Unit konnte.

MSDN-Library durchsuchenConvertSidToStringSid
Einfach selber deklarieren, sollte nicht so schwer sein.

Olli 13. Apr 2007 15:37

Re: Daten aus der Registry lesen mit TService
 
Nach wie vor ist ConvertSidToStringSid eine Option, denke ich.

Zitat:

Zitat von Schwedenbitter
Allerdings stehe ich vor demselben Problem wie das Programm LSALogon von Remko. Die letzten drei bzw. vier Ziffern des SID stimmen nicht. Damit komme ich nicht in diesen Unterschlüssel rein.
Ich habe jetzt auf mehreren Rechnern festgestellt, dass es neben der 1003 auch noch mehrere Möglichkeiten gibt. Meine Idee, die 500 einfach durch eine 1003 zu ersetzen, geht damit leider auch nicht mehr. Schade eigentlich.

Und wieso stimmen die nicht? Das ist die UID. Die ist nur fuer das eingebaute Adminkonto konstant (500 eben). Fuer alle anderen sind es Werte ueber 1000. Und die haengen davon ab wieviele Benutzer das System hat und welcher Benutzer das ist. Die Zahl wird immer erhoeht und das Loeschen von Benutzern aendert daran nichts - will heissen dass sie nicht wiederverwendet werden, selbst wenn der Benutzer geloescht wird. Der erste Teil der SID beschreibt bspw. Domain und System in dem das Benutzerkonto gilt. Die UID am Ende ist vergleichbar mit der in Unix.

Ausserdem sagte ich bereits, dass der Benutzer bereits eingeloggt sein muss, fuer welchen du das erreichen willst. Ansonsten hast du keine Chance auf den (logischerweise) nicht-existenten Schluessel zuzugreifen.

Olli 13. Apr 2007 15:46

Re: Daten aus der Registry lesen mit TService
 
Delphi-Quellcode:
function ConvertSidToStringSidW(Sid: PSID; var StringSid: PWideChar): BOOL; stdcall; external 'Advapi32.dll';

function SIDToStr(SID: PSID): WideString;
var
  lpcsSid: PWideChar;
begin
   Result := 'Error';
   if(ConvertSidToStringSidW(SID, lpcsSid))
   try
     Result := WideString(lpcsSid);
   finally
     LocalFree(lpcsSid);
   end;
end;


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