2006. május 31., szerda

How to retrieve and change display settings


Problem/Question/Abstract:

I can't find a way to enumerate or test display frequencies. When I use ChangeDisplaySettings to test for a given mode it returns DISP_CHANGE_SUCCESSFUL, no matter what I put in for dmDisplayFrequency. Should I just attempt to set a good median freq such as 75Hz and forget about it? Any other ideas?

Answer:

type
  PdmArray = ^TDmArray;
  TDmArray = array[0..0] of TDeviceMode;

  TForm1 = class(TForm)
    { ... }
  private
    lpDmArray: PDmArray;
    NumModes: integer;
    BitsPerPixel: DWord;
    RefreshRate: DWord;
    CurrentDisplayMode: integer;
    SelectedDisplayMode: integer;
    { ... }
  public
  end;

procedure TForm1.RetrieveDisplayModes;
var
  I: Integer;
  MoreModes: Bool;
  dm: TDeviceMode;
begin
  StringGridDisplayModes.ColCount := 5;
  StringGridDisplayModes.RowCount := 2;
  StringGridDisplayModes.Cells[0, 0] := 'Mode';
  StringGridDisplayModes.Cells[1, 0] := 'Resulution';
  StringGridDisplayModes.Cells[2, 0] := 'Bits per Pixel (Colors)';
  StringGridDisplayModes.Cells[3, 0] := 'DisplayFrequency';
  StringGridDisplayModes.Cells[4, 0] := 'DeviceName';
  for I := 0 to StringGridDisplayModes.ColCount do
    StringGridDisplayModes.Cells[I, 1] := '';
  MoreModes := TRUE;
  I := 0;
  while MoreModes do
  begin
    MoreModes := EnumDisplaySettings(nil, I, dm);
    StringGridDisplayModes.Cells[0, StringGridDisplayModes.RowCount - 1] :=
      IntToStr(I);
    StringGridDisplayModes.Cells[1, StringGridDisplayModes.RowCount - 1] :=
      IntToStr(dm.dmPelsWidth) + ' * ' + IntToStr(dm.dmPelsHeight);
    StringGridDisplayModes.Cells[2, StringGridDisplayModes.RowCount - 1] :=
      IntToStr(dm.dmBitsPerPel);
    StringGridDisplayModes.Cells[3, StringGridDisplayModes.RowCount - 1] :=
      IntToStr(dm.dmDisplayFrequency);
    StringGridDisplayModes.Cells[4, StringGridDisplayModes.RowCount - 1] :=
      dm.dmDeviceName;
    if (dm.dmPelsWidth = Screen.Width) and (dm.dmPelsHeight = Screen.Height) and
      (dm.dmBitsPerPel = BitsPerPixel) and (dm.dmDisplayFrequency = RefreshRate) then
      CurrentDisplayMode := I;
    StringGridDisplayModes.RowCount := StringGridDisplayModes.RowCount + 1;
    Inc(i);
  end;
  StringGridDisplayModes.RowCount := StringGridDisplayModes.RowCount - 1;
  NumModes := I;
  GetMem(lpDmArray, sizeof(TDeviceMode) * NumModes);
  FillChar(lpDmArray^, sizeof(TDeviceMode) * NumModes, #0);

{$IFOPT R+}
{$DEFINE CKRANGE}
{$R-}
{$ENDIF}

  for I := 0 to (NumModes - 1) do
    EnumDisplaySettings(nil, I, lpDmArray[i]);

{$IFDEF CKRANGE}
{$UNDEF CKRANGE}
{$R+}
{$ENDIF}

end;

procedure TForm1.ButtonRetrieveDisplayModesClick(Sender: TObject);
begin
  RetrieveDisplayModes;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  try
    FreeMem(lpDmArray, sizeof(TDeviceMode) * NumModes);
  except
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  DC: hDC;
begin
  DC := GetDC(0);
  BitsPerPixel := GetDeviceCaps(DC, BITSPIXEL);
  RefreshRate := GetDeviceCaps(DC, VREFRESH);
  ReleaseDC(0, DC);
  StatusBar.Panels[2].Text := 'Resolution: ' + IntToStr(Screen.Width) + ' * ' +
    IntToStr(Screen.Height)
    + ', ColorDept: ' + IntToStr(BitsPerPixel) + ', RefreshRate: '
    + IntToStr(RefreshRate);
  ButtonRetrieveDisplayModesClick(Sender);
end;

procedure Tform1.SetDisplayMode(Mode: integer);
var
  ReturnVal: LongInt;
begin

{$IFOPT R+}
{$DEFINE CKRANGE}
{$R-}
{$ENDIF}

  ReturnVal := ChangeDisplaySettings(lpDmArray[Mode], CDS_UPDATEREGISTRY);

{$IFDEF CKRANGE}
{$UNDEF CKRANGE}
{$R+}
{$ENDIF}

  if ReturnVal <> DISP_CHANGE_SUCCESSFUL then
    case ReturnVal of
      DISP_CHANGE_SUCCESSFUL: ShowMessage('DISP_CHANGE_SUCCESSFUL');
      DISP_CHANGE_RESTART: ShowMessage('DISP_CHANGE_RESTART');
      DISP_CHANGE_BADFLAGS: ShowMessage('DISP_CHANGE_BADFLAGS');
      DISP_CHANGE_FAILED: ShowMessage('DISP_CHANGE_FAILED');
      DISP_CHANGE_BADMODE: ShowMessage('DISP_CHANGE_BADMODE');
      DISP_CHANGE_NOTUPDATED: ShowMessage('DISP_CHANGE_NOTUPDATED');
    end;
end;

2006. május 30., kedd

How to call and display a form, when its name is stored in the field of a TTable


Problem/Question/Abstract:

My table has a field named FormName, in which I store the name of the forms my application is using. How can I display the form of which the name is stored in the table?

Answer:

The best you can do is:

if Table1.FieldByName('NameForm').AsString = 'SomeForm' then
  SomeForm.Show
else if Table1.FieldByName('NameForm').AsString = 'SomeOtherForm' then
  SomeOtherForm.Show;

2006. május 29., hétfő

How to copy all files from one directory to another


Problem/Question/Abstract:

Creating a new directory (folder) is no problem. There is the MkDir() procedure. But how does one copy all files from another directory into this new one within Delphi run time? I am also concerned that any pseudo DOS command will not be available in the future, especially Windows NT 5 (2000).

Answer:

Solve 1:

uses
  shellapi

function FileManager(xSourcePath, xDestPath, xPara: string): Boolean;
var
  PFileMsg: TSHFileOpStruct;
  mNowPath: string;
begin
  Result := False;
  FillChar(PFileMsg, sizeof(PFileMsg), #0);
  if pos('.', xpara) = 0 then
    exit;
  mNowPath := GetCurrentDir;
  if xSourcePath <> '' then
    if not DirectoryExists(xSourcePath) then
    begin
      showmessage('The source path does not exist !');
      exit;
    end;
  if xDestPath <> '' then
    if not DirectoryExists(xDestPath) then
    begin
      showmessage('The destination path does not exist !');
      exit;
    end;
  if SetCurrentDirectory(Pchar(xSourcePath)) then
  begin
    with PFileMsg do
    begin
      if Owner is TForm then
        Wnd := TForm(Owner).Handle
      else
        Wnd := Application.Handle;
      if xDestPath <> '' then
      begin
        wFunc := FO_COPY;
        PTo := pChar(xDestPath);
        fFlags := FOF_MULTIDESTFILES + FOF_NOCONFIRMATION;
      end
      else
      begin
        wFunc := FO_DELETE;
        fFlags := FOF_ALLOWUNDO + FOF_NOCONFIRMATION;
      end;
      pFrom := PChar(xPara + #0#0);
    end;
    SHFileOperation(PFileMsg);
    SetCurrentDirectory(Pchar(mNowPath));
    Application.ProcessMessages;
    Result := True;
  end;
end;


Example:

CopyFile:
FileManager('C:\Demo', 'C:\Temp', '*.*');

DeleteFile: (delete C: \Demo\ * . * )
FileManager('C:\Demo', '', '*.*');


Solve 2:

uses
  ShellAPI;

procedure TForm1.BtnCopyClick(Sender: TObject);
var
  fileOp: TShFileOpStruct;
  fromDir: string;
  toDir: string;
begin
  FillChar(fileOp, Sizeof(TShFileOpStruct), 0);
  fromDir := DirectoryListBox1.Directory + '\*.*'#0;
  toDir := DirectoryListBox2.Directory + #0;
  with fileOp do
  begin
    wnd := Handle;
    wfunc := FO_COPY;
    pFrom := PChar(fromDir);
    pTo := PChar(toDir);
    fFlags := FOF_ALLOWUNDO;
    fAnyOperationsAborted := false;
    hNameMappings := nil;
    lpszProgressTitle := nil;
  end;
  SHFileOperation(fileOp);
end;

2006. május 28., vasárnap

Justify the caption of a TForm


Problem/Question/Abstract:

I'd like to insert enough spaces between two strings to make a caption for a titlebar show the pieces justified left and right. For example, program name on the left and copyright notice on the right.

Answer:

The DrawText API function supports drawing justified text. Below is an example:

{ ... }
var
  R: TRect;
  fmt: UINT;
begin
  R := ClientRect; {define your rectangle to draw the text}
  with Canvas do {canvas to paint on}
  begin
    fmt := DT_LEFT;
    DrawText(Handle, PChar(LeftSide), Length(LeftSide), R, fmt);
    fmt := DT_RIGHT;
    DrawText(Handle, PChar(RightSide), Length(RightSide), R, fmt);
  end;
end;

2006. május 27., szombat

How to store events in a TList


Problem/Question/Abstract:

How do you store events in a list? Let's say a TTimer descendant has to process a number of events of other components.

Answer:

Since TNotifyEvents are methods of objects, you need to store the objects in the list so the hidden "self" parameter can also be stored in the list. Example:


unit timeru;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls,
    StdCtrls;

type
  TMethodContainer = class
    TheMethod: TNotifyEvent;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
    x, y, z: integer;
    FOnTimerList: TList;
    procedure UpdateEdits(Sender: TObject);
    procedure SetOnTimer(Value: TNotifyEvent);
    procedure ClearTimer(Value: TNotifyEvent);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  inc(x);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  inc(y);
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  inc(z);
end;

procedure TForm1.UpdateEdits(Sender: TObject);
begin
  edit1.text := 'X = ' + inttostr(x);
  edit2.text := 'Y = ' + inttostr(y);
  edit3.text := 'Z = ' + inttostr(z);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  i: integer;
begin
  for i := 0 to FOnTimerList.Count - 1 do
    with TMethodContainer(FOnTimerList.Items[i]) do
      if assigned(TheMethod) then
        TheMethod(Self);
end;

procedure TForm1.SetOnTimer(Value: TNotifyEvent);
var
  TM: TMethodContainer;
begin
  if Assigned(Value) then
  begin
    Timer1.enabled := false;
    TM := TMethodContainer.create;
    TM.TheMethod := value;
    FOnTimerList.Add(pointer(TM));
    Timer1.enabled := true;
  end;
end;

procedure TForm1.ClearTimer(Value: TNotifyEvent);
var
  i: integer;
  found: boolean;

  function IsEqual(var p1, p2): boolean;
  begin
    result := pointer(p1) <> pointer(p2);
  end;

begin
  if Assigned(Value) then
  begin
    Timer1.enabled := false;
    i := 0;
    found := false;
    while (i < FOnTimerList.count) and not (found) do
    begin
      with TMethodContainer(FOnTimerList.Items[i]) do
        found := IsEqual(TheMethod, Value);
      if not (found) then
        inc(i);
    end;
    if found then
    begin
      TMethodContainer(FOnTimerList.Items[i]).Free;
      FOnTimerList.delete(i);
    end;
    Timer1.enabled := true;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FOnTimerList := TList.create;
  SetOnTimer(Button1Click);
  SetOnTimer(Button2Click);
  SetOnTimer(Button3Click);
  SetOnTimer(UpdateEdits);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ClearTimer(UpdateEdits);
  ClearTimer(Button3Click);
  ClearTimer(Button2Click);
  ClearTimer(Button1Click);
  FOnTimerList.free;
end;

end.

2006. május 26., péntek

How to activate the previous instance of an application


Problem/Question/Abstract:

How to activate the previous instance of an application

Answer:

Place the following at the start of your project file:

begin
  if HPrevInst < > 0 then
  begin
    ActivatePreviousInstance;
    Exit;
  end;
  { ... }

and include the following unit:

unit PrevInst;

interface

uses
  WinProcs, WinTypes, SysUtils;

type
  PHWnd = ^HWnd;

function EnumFunc(Wnd: HWnd; TargetWindow: PHWnd): Bool; export;
procedure ActivatePreviousInstance;

implementation

function EnumFunc(Wnd: HWnd; TargetWindow: PHWnd): Bool;
var
  ClassName: array[0..30] of char;
begin
  Result := True;
  if GetWindowWord(Wnd, GWW_HINSTANCE) = HPrevInst then
  begin
    GetClassName(Wnd, ClassName, 30);
    if StrIComp(ClassName, 'TApplication') = 0 then
    begin
      TargetWindow^ := Wnd;
      Result := False;
    end;
  end;
end;

procedure ActivatePreviousInstance;
var
  PrevInstWnd: HWnd;
begin
  PrevInstWnd := 0;
  EnumWindows(@EnumFunc, Longint(@PrevInstWnd));
  if PrevInstWnd <> 0 then
    if IsIconic(PrevInstWnd) then
      ShowWindow(PrevInstWnd, SW_RESTORE)
    else
      BringWindowToTop(PrevInstWnd);
end;

end.

2006. május 25., csütörtök

How to trap changes of the clipboard content


Problem/Question/Abstract:

Is there a way to use an OnChange event for the clipboard? I want to avoid to check for a change of the clipboard content every millisecond.

Answer:

An application can register itself in the clipboard viewer chain. The first window in this chain always receives the messages. Every window is responsible to pass the messages on to the next one.

unit Unit1;

interface

uses
  Windows, Messages, Forms, Classes, Controls, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FNextClipboardViewer: HWND;
    procedure WMChangeCBChain(var Msg: TWMChangeCBChain); message WM_CHANGECBCHAIN;
    procedure WMDrawClipboard(var Msg: TWMDrawClipboard); message WM_DRAWCLIPBOARD;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  { Initialize variable }
  FNextClipboardViewer := 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if FNextClipboardViewer <> 0 then
    MessageBox(0, 'This window is already registered!', nil, 0)
  else
    { Add to clipboard chain }
    FNextClipboardViewer := SetClipboardViewer(Handle);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  { Remove from clipboard chain }
  ChangeClipboardChain(Handle, FNextClipboardViewer);
  FNextClipboardViewer := 0;
end;

procedure TForm1.WMChangeCBChain(var Msg: TWMChangeCBChain);
begin
  inherited;
  { mark message as done }
  Msg.Result := 0;
  { the chain has changed }
  if Msg.Remove = FNextClipboardViewer then
    { The next window in the clipboard viewer chain had been removed. We recreate it. }
    FNextClipboardViewer := Msg.Next
  else
    { Inform the next window in the clipboard viewer chain }
    SendMessage(FNextClipboardViewer, WM_CHANGECBCHAIN, Msg.Remove, Msg.Next);
end;

procedure TForm1.WMDrawClipboard(var Msg: TWMDrawClipboard);
begin
  inherited;
  { Clipboard content has changed }
  try
    MessageBox(0, 'Clipboard content has changed!', 'Clipboard Viewer', MB_ICONINFORMATION);
  finally
    { Inform the next window in the clipboard viewer chain }
    SendMessage(FNextClipboardViewer, WM_DRAWCLIPBOARD, 0, 0);
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if FNextClipboardViewer <> 0 then
  begin
    { Remove from clipboard chain }
    ChangeClipboardChain(Handle, FNextClipboardViewer);
    FNextClipboardViewer := 0;
  end;
end;

end.

2006. május 24., szerda

Detecting Softice in W9x and NT (W2K - dunno)


Problem/Question/Abstract:

Detecting Softice in W9x and NT (W2K - dunno)

Answer:

The Cracker can still defeat this protection by using FrogIce, nothing can stop SoftIce hehe. Anyway here is sum code you just instert into your Applcation.

//SoftIce in W9x

function IsSoftIce95Loaded: boolean;
var
  hFile: Thandle;
begin
  result := false;
  hFile := CreateFileA('\.SICE', GENERIC_READ or GENERIC_WRITE,
    FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL, 0);
  if (hFile <> INVALID_HANDLE_VALUE) then
  begin
    CloseHandle(hFile);
    result := TRUE;
  end;
end;
// SoftIce in NT OS

function IsSoftIceNTLoaded: boolean;
var
  hFile: Thandle;
begin
  result := false;
  hFile := CreateFileA('\.NTICE', GENERIC_READ or GENERIC_WRITE,
    FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL, 0);
  if (hFile <> INVALID_HANDLE_VALUE) then
  begin
    CloseHandle(hFile);
    result := TRUE;
  end;
end;
//to detect it
if IsSoftIce95Loaded or IsSoftIceNTLoaded then
  Application.Terminate
    {if you insert a "Nag" (Message telling him he uses SoftIce) then a amatuer cracker w'll find this protection in notime}
  //bestway of using this thing is in "project Unit"

2006. május 23., kedd

Get rid of the BDE


Problem/Question/Abstract:

I have (mostly) converted an application from Paradox to DBISAM. How do I know if I have really got the BDE out of my program? I have 150 forms in the application and I have been through them all fairly methodically, but how can I be sure that I'm not compiling in bits of BDE every time? Is there a thing in the "uses" clause that is there for BDE and not for DBISAM? Is there a way of looking at the EXE to see if there is BDE stuff in it?

Answer:

Here is a list of possible approaches:

Build the application with a MAP file (under linker options) and examine the map file looking for DBTABLES, BDE, or BDECONST. If any of these are included you're using the BDE. Having DBTABLES in a uses clause anywhere in the program is the most likely reason for the BDE to be used.
Another option would be to remove or rename DBTABLES.DCU before doing a build all. That way the compiler will show you exactly where you are inadvertently using the BDE. Just searching unit files for particular units may not be good enough as you may be using third party components without source code that may themselves use the BDE.
I know 2 ways to do that: Look out for calls in the uses clause: dbtables, bde, bdi. And the final killer is if you can test the app in a machine without BDE. If you want to do that in your own machine you can simply rename the HKEY_current_user/Software/Borland to something else (I use something like xxBorland) and the HKEY_Local_Machine/Software/Borland to something else also (as before, I use xxBorland). After renaming these keys in the registry the BDE is out and you can test your app in your machine. If it works, it'll be ok. Don't forget to rename back those keys in the registry to be able to use your Delphi again.
All I did was rename the BDE directory. A lot easier than messing with the registry

2006. május 22., hétfő

How to change the volume


Problem/Question/Abstract:

How can I change the volume?

Answer:

procedure SetVolume(X: Word);
var
  iErr: Integer;
  i: integer;
  a: TAuxCaps;
begin
  for i := 0 to auxGetNumDevs do
  begin
    auxGetDevCaps(i, Addr(a), SizeOf(a));
    if a.wTechnology = AUXCAPS_CDAUDIO then
      break;
  end;

  // Sets volume equally for left and right channels.
  // VOLUME := LEFT * $10000 + RIGHT * 1 (or the other way? :)

  iErr := auxSetVolume(i, (X * $10001));
  if (iErr = 0) then
    ShowMessage('No audio devices are available!');
end;

function GetVolume: Word;
var
  iErr: Integer;
  i: integer;
  a: TAuxCaps;
  vol: word;
begin
  for i := 0 to auxGetNumDevs do
  begin
    auxGetDevCaps(i, Addr(a), SizeOf(a));
    if a.wTechnology = AUXCAPS_CDAUDIO then
      break;
  end;
  iErr := auxGetVolume(i, addr(vol));
  GetVolume := vol;
  if (iErr = 0) then
    ShowMessage('No audio devices are available!');
end;

You'll have to modify AUXCAPS_CDAUDIO to whatever suits
(check out available values via the Ctrl+Space shortcut in Delphi 3)

2006. május 21., vasárnap

Remote NT/W2000 Server Admin and Information Classes


Problem/Question/Abstract:

Remote NT/W2000 Server Admin and Information Classes  

Version 1.0.1 Available from mheydon@pgbison.co.za

  Added property LastErrorStr to ALL classes ( Stores last error string on a failed API call)

  Added property ShowErrorDialog to ALL classes ( Denotes whether LastErrorStr should be automatically
                                                                                   displayed or handled by user from property LastErrorStr)

  Added property Active to ALL classes (except TNTServerEventLog)  ( Denotes that the class created OK and all                                                                                                                  lib functions  are loaded and active.)

  Added property LogIsOpen to TNTServerEventLog  ( Denotes if an eventlog is currently Open)

  Added OVERLOAD parameters to ValidateUserLogonAPI and  ValidateUserLogonSSP to accomodate Error                 Message on failure.

  Added OPTION ShowCreateErrDialog : boolean = false to Create constructors to enable/disable error dialog on              class create.


Answer:

This unit defines Classes that access remote machines and gains information from them. The unit currentlty only reads information. Many of the calls eg. NetServerGetInfo have equivalent NetServerSetInfo calls. It will be easy enough to modify these classes if write functionality is desired. (Bravehearts ONLY)

The following classes are implemeted ...

   TNTServerInfo      = class(TObject)

   TNTServerEventLog  = class(TObject)

   TNTServerServices  = class(TObject)

   TNTServerSessions  = class(TObject)

   TNTServerOpenFiles = class(TObject)

   TNTServerGroups    = class(TObject)

   TNTServerDisks     = class(TObject)

Plus Procedures and Functions ...

    procedure GetServerResources(const RootObject : string;
                                StringList : TStrings;
                                RecursiveEnum : boolean;
                                ResourceTypes : TNTServerResSet = [resAny])


   function ValidateUserLogon(const UserName : string;
                              const Domain : string;
                              const PassWord : string) : boolean;

NOTE : for Remote Registry Access see Borland's "TRegistry.RegistryConnect()"


Methods that set TStrings set the Items or Lines property to semi-colon ";" delimited fields. This format is ideal for my component TMultiColListBox or function General.ExtractField(). Flag DWORD fields can be tested via function General.AndEqual()
eg.
        if AndEqual(SI.ServerType,SV_TYPE_SERVER_UNIX) then ...

Most of the functions that set TStrings also allow a SET of Field Options which control the information returned in the columns of the individual Items/Lines of the TStringList.

Certain Classes have OVERLOADED Create constructors. This allows you to either Create an ATTACHED instance or a Simple UNATTACHED instance.In the case of TNTServerEventLog you can Create an instance that attachesto the server an opens the SourceName eventlog immediately, or just create the Class and then call OpenLog() to attach to the log. NOTE : OpenLog and similar methods in other classes will automatically close any previously opened log before opening the new log. Calling the Free method also closes any open log thus there is no need for a CloseLog() or similar methods.

DOCUMENTATION

----------------------------------------------------------------------------------------------------------------------------------------------

CLASS
  TNTServerInfo

  This class retrieves info on the specified server and users on that server. Use GetServerInfo() and GetUserInfo()       to load the Read Only info properties. Set properties LoadLoggedOnUserList and LoadActualUserList to true or
  false depending on wether this info is required or not. If they are NOT required then it is advisable to set them to     false form performance reasons.

METHODS
  Create
  Create an instance. No parameters required.

  GetServerInfo(const MachineName : string) : boolean
  Loads the read only properties of the specified server.

  GetUserInfo(const MachineName : string const UserName : string) : boolean
  Loads the read only properties of the Sub-Properties of  property UserInfo.

PROPERTIES
  Comment                                         - read only string of Server Comment
  Version                                                 - read only string of LAN manager version
  UserPath                                         - read only string of path to User directories
  ServerType                                 - read only DWORD flag of server types (see Constants)
  MaxUserAccounts                 - read only DWORD user count who can log in
  ServerVisible                                 - read only boolean denotes if server is visible on network
  AutoDisconnectTime         - read only DWORD AutoDisconnectTime in minutes
  AnnounceRate                         - read only DWORD server network announce rate i seconds
  AnnounceDelta                         - read only DWORD delta value of announce rate in msecs
  ServerDateTime                 - read only TDateTime System Date and Time of server
  OffsetFromGMT                 - read only integer number of hours diff to GMT
  LoggedOnUsers                         - read only DWORD number of logged on users
  LoggedOnUserList         - TStringList of names of logged on users
  ActualUserList                         - TStringList of names of actual user accounts

  LoadLoggedOnUserList - boolean flag to optimize performance
  LoadActualUserList                 - boolean flag to optimize performance

  UserInfo - read only TNetUserInfo that has sub-properties ..
             .AccountName                     : string
             .PasswordAgeDays         : double num days since password changed
             .Privilege                               : byte (see constants)
             .HomeDirectory                  : string
             .Comment                                : string
             .UserFlags                              : LongWord determines features (see constants)
             .ScriptPath                              : string path of login script if any
             .FullName                                : string
             .UserComment             : string
             .Workstations                    : string comma sep list of logon workstations
             .LastLogon                       : TdateTime
             .LastLogoff                      : TDateTime
             .AcctExpires                            : TDateTime
             .MaxStorage                     : LongWord max amount of diskspace for user
             .BadPWCount              : LongWord number of incorrect password tries
             .LogonCount                      : LongWord number of successful logons
             .LogonServer                     : string of server for logon requests
             .CountryCode                     : LongWord
             .CodePage                        : LongWord
             .UserID                                  : LongWord relative ID (RID) of user
             .Profile                                 : string
             .HomeDirDrive            : string drive letter of home directory
             .PasswordExpired         : boolean of password expired or not

----------------------------------------------------------------------------------------------------------------------------------------------

CLASS
  TNTServerEventLog

  Allows reading of remote server event logs. Can create class to  connect immediately connect to required log, or      to manually open logs. Source Names are the names refered to in registry key                                                            "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog".
  These names can be loaded into a list via method SourceNames(). The logs can be read either first to last or last      to first and can be traversed in either direction. The Read() method load the  read only property fields.

METHODS
  Create(const MachineName : string; const SourceName : string;
             StartingRecord : TEventLogStartPos); overload
  or
  Create(const MachineName : string); overload
  Overloaded Create for instance creation. Either connect to event log immediately or later using OpenLog()

  OpenLog(const SourceName : string; StartingRecord : TEventLogStartPos) : boolean
  Attempts to open the specified log. If a previous log was active it is closed first.

  Read(Direction : TEventLogReadDir) : boolean
  Loads the read only property fields of property LogInfo in the direction specified. Properties EOF and BOF              indicate end of file and beginning of file error conditions. The method returns false in these cases. Direction may    be rForwards or rBackwards.

  SourceNames(StringList : TStrings)
  Loads the Source Names from the registry of the remote server into a stringlist.

  Clear
  Clears ALL entries in the open event log.

PROPERTIES
  Active                         - read only set to true if an event log is currently open
  NumEntries         - read only DWORD number of entries in current log
  Bof                                 - read only boolean indicating Beginning of File after Read()
  Eof                                 - read only boolean indicating End of File after Read()

  LogInfo - read only TLogInfoRecord that has Sub-Properties ...
            .RecordNumber          : DWORD
            .LogTime                       : TDateTime of time generated
            .LogText                       : TStrings containg text of log message
            .EventType                     : string of type of event
            .EventID                               : DWORD
            .EventCategory         : word
            .SourceName                    : string application source name
            .ComputerName          : string of computer name that created event

----------------------------------------------------------------------------------------------------------------------------------------------

CLASS
  TNTServerServices

  Allows control of remote server services. Information can be loaded into the read only Sub-Properties of                  ServiceInfo by the method GetServiceInfo(). A string list can be loaded with optional field selection
  and Types and States. Service controls only support Stop and Start at this stage.

METHODS
  Create(const MachineName : string)
  Creates an instance that connects to specified server

  ServiceNames(StringList : TStrings;
                         ServiceFields : TNTServerServiceFieldSet = [fServiceName];
                         ServiceTypes : TNTServerServiceTypes = stAllServices;
                         ServiceStates : TNTServerServiceStates = sAllStates);
  Loads a string list with optional field set of [fServiceName,fDisplayName,fStatus]. Services types can be
  stWin32,stDrivers or stAllServices. Service states can be sActive,sInActive  or sAllStates.

  GetServiceInfo(const ServiceName : string) : boolean
  Loads read only Sub-Properties of property ServiceInfo

  ServiceStop(const ServiceName : string) : boolean
  Stop the service specified by name

  ServiceStart(const ServiceName : string) : boolean
  Start the service specified by name

PROPERTIES
  ServiceInfo - read only that has Sub-Properties ...
                .DisplayName                      : string
                .Status                                           : string
                .StartType                                : string specifies how the service is started
                .ErrorControl                     : string specifies error severity
                .BinaryPathName           : string actual exe/binary file
                .ServiceStartName         : string account name of "Run As"
                .TypeFlag                                 : DWORD flag (see SERVER_STATUS WinApi Help)
                .ControlsFlag                     : DWORD flag (see SERVER_STATUS WinApi Help)

----------------------------------------------------------------------------------------------------------------------------------------------

CLASS
  NTServerSessions

  Retrieves information on sessions of remote server. Selectable fields are available in the returned list.

METHODS
  Create
  Create an instance of class.

  SessionNames(const MachineName : string;
                          StringList : TStrings;
                          SessionFields : TNTServerSessionsFieldSet = [fClientName])
  Loads a string list of sessions. Optional fields can be specified as a set
  [fClientName,fUserName,fNumOpens,fTimeActive,fIdleTime,fUserFlag,fClientType,fTransport]

----------------------------------------------------------------------------------------------------------------------------------------------

CLASS
  TNTServerOpenFiles

  Retrieves information of open files on remote server. Selectable fields are available in the returned list.

METHODS
  Create
  Create an instance of class.

  OpenFiles(const MachineName : string;
                   StringList : TStrings;
                   OpenFileFields : TNTServerOpenFileFieldSet = [fPathName])
  Loads a string list of open files. Optional fields can be specified as a set of                                                                [fPathName,fUser,fFileID,fPermissions,fNumLocks]

----------------------------------------------------------------------------------------------------------------------------------------------

CLASS
  TNTServerGroups

  Retrieves information of groups on remote server. Selectable fields are available in the returned list.

METHODS
  Create
  Create an instance of the class.

  GroupNames(const MachineName : string;
                        StringList : TStrings;
                        GroupFields : TNTServerGroupsFieldSet = [fGroupName])
  Loads a string list of groups. Optional fields can be specified as a set of                                                                     [fGroupname,fComment,fGroupID,fAttributeFlag]

----------------------------------------------------------------------------------------------------------------------------------------------

CLASS
  TNTServerDisks

  Retrieves a list of disks on the remote server

METHODS
  Create
  Create an instance of the class.

  DiskNames(const MachineName : string; StringList : TStrings)
  Loads a string list of disks on the specified server. eg. "A:" "C:" etc.

----------------------------------------------------------------------------------------------------------------------------------------------

procedure GetServerResources(const RootObject : string;
                                                 StringList : TStrings;
                                                 RecursiveEnum : boolean;
                                                 ResourceTypes : TNTServerResSet = [resAny])

  Loads a string list of resources from a remote server. RootObject is the starting point eg. '\\pgbbxbfs1'. If                  RootObject is '' then the start point is the root of the network. RecursiveEnum denotes whether
  to recurse through all sub containers as well. ResourceType may be a set of                                                               [resAny,resDomain,resServer,resDisk,resPrinter]

----------------------------------------------------------------------------------------------------------------------------------------------

function ValidateUserLogon(const UserName : string;
                                             const Domain : string;
                                             const PassWord : string) : boolean

  Validates a users Name,Domain and Password returning true if OK else false

----------------------------------------------------------------------------------------------------------------------------------------------


unit W2000Admin;
interface

uses Windows, SysUtils, Classes, DateUtils, Forms, Controls, WinSvc,
  Registry;

const
  // Property TNTServerInfo.UserInfo.UserFlags constants Mask
  UF_SCRIPT = 1;
  UF_ACCOUNTDISABLE = 2;
  UF_HOMEDIR_REQUIRED = 8;
  UF_LOCKOUT = $10;
  UF_PASSWD_NOTREQD = $20;
  UF_PASSWD_CANT_CHANGE = $40;
  UF_DONT_EXPIRE_PASSWD = $10000;
  UF_MNS_LOGON_ACCOUNT = $20000;

  // Property TNTServerInfo.UserInfo.Privilege constants
  USER_PRIV_GUEST = 0;
  USER_PRIV_USER = 1;
  USER_PRIV_ADMIN = 2;

  // Property TNTServerInfo.ServerType Mask of following values
  SV_TYPE_UNKNOWN = 0;
  SV_TYPE_WORKSTATION = $00000001;
  SV_TYPE_SERVER = $00000002;
  SV_TYPE_SQLSERVER = $00000004;
  SV_TYPE_DOMAIN_CTRL = $00000008;
  SV_TYPE_DOMAIN_BAKCTRL = $00000010;
  SV_TYPE_TIME_SOURCE = $00000020;
  SV_TYPE_AFP = $00000040;
  SV_TYPE_NOVELL = $00000080;
  SV_TYPE_DOMAIN_MEMBER = $00000100;
  SV_TYPE_PRINTQ_SERVER = $00000200;
  SV_TYPE_DIALIN_SERVER = $00000400;
  SV_TYPE_SERVER_UNIX = $00000800;
  SV_TYPE_NT = $00001000;
  SV_TYPE_WFW = $00002000;
  SV_TYPE_SERVER_MFPN = $00004000;
  SV_TYPE_SERVER_NT = $00008000;
  SV_TYPE_POTENTIAL_BROWSER = $00010000;
  SV_TYPE_BACKUP_BROWSER = $00020000;
  SV_TYPE_MASTER_BROWSER = $00040000;
  SV_TYPE_DOMAIN_MASTER = $00080000;
  SV_TYPE_SERVER_OSF = $00100000;
  SV_TYPE_SERVER_VMS = $00200000;
  SV_TYPE_WINDOWS = $00400000;
  SV_TYPE_DFS = $00800000;
  SV_TYPE_ALTERNATE_XPORT = $20000000;
  SV_TYPE_LOCAL_LIST_ONLY = $40000000;
  SV_TYPE_DOMAIN_ENUM = $80000000;
  SV_TYPE_ALL = $FFFFFFFF;

  // Event Log Read Constants
  EVENTLOG_SEQUENTIAL_READ = 1;
  EVENTLOG_SEEK_READ = 2;
  EVENTLOG_FORWARDS_READ = 4;
  EVENTLOG_BACKWARDS_READ = 8;
  MAX_PREFERRED_LENGTH = $FFFFFFFF;

  // Session Info User Flags
  SESS_GUEST = 1;
  SESS_NOENCRYPTION = 2;

  // Open File Permiisions
  PERM_FILE_READ = 1;
  PERM_FILE_WRITE = 2;
  PERM_FILE_CREATE = 4;

type
  // Types for GetServerResources()
  TNTServerRes = (resAny, resDomain, resServer, resDisk, resPrinter);
  TNTServerResSet = set of TNTServerRes;

  // Types for NTServerServices
  TNTServerServiceTypes = (stWin32, stDrivers, stAllServices);
  TNTServerServiceStates = (sActive, sInActive, sAllStates);
  TNTServerServiceFields = (fServiceName, fDisplayName, fStatus);
  TNTServerServiceFieldSet = set of TNTServerServiceFields;

  // Types for NTServerSessions
  TNTServerSessionsFields = (fClientName, fUserName, fNumOpens,
    fTimeActive, fIdleTime, fUserFlag,
    fClientType, fTransport);
  TNTServerSessionsFieldSet = set of TNTServerSessionsFields;

  // Types for TNTServerOpenFiles
  TNTServerOpenFileFields = (fPathName, fUser, fFileID, fPermissions,
    fNumLocks);
  TNTServerOpenFileFieldSet = set of TNTServerOpenFileFields;

  // Types for TNTServerGroups
  TNTServerGroupsFields = (fGroupname, fComment, fGroupID, fAttributeFlag);
  TNTServerGroupsFieldSet = set of TNTServerGroupsFields;

  // Types for TNTServerEventLog
  TEventLogReadDir = (rForwards, rBackwards);
  TEventLogStartPos = (sFirst, sLast);

  // -------------------------------------
  // External DLL mappings to NETAPI32.DLL
  // -------------------------------------

  TNetRemoteTOD = function(pszServer: PWideChar;
    var pbBuffer: pointer): DWORD; stdcall;

  TNetServerGetInfo = function(pszServer: PWideChar;
    Level: DWORD;
    var pbBuffer: pointer): DWORD; stdcall;

  TNetWkstaGetInfo = function(pszServer: PWideChar;
    Level: DWORD;
    var pbBuffer: pointer): DWORD; stdcall;

  TNetApiBufferFree = function(Buffer: pointer): DWORD; stdcall;

  TNetWkstaUserEnum = function(pszServer: PWideChar;
    Level: DWORD;
    var pbBuffer: pointer;
    PrefMaxLen: LongInt;
    var EntriesRead: DWORD;
    var TotalEntries: DWORD;
    var ResumeHandle: DWORD): DWORD; stdcall;

  TNetUserEnum = function(pszServer: PWideChar;
    Level: DWORD;
    Filter: DWORD;
    var pbBuffer: pointer;
    PrefMaxLen: LongInt;
    var EntriesRead: DWORD;
    var TotalEntries: DWORD;
    var ResumeHandle: DWORD): DWORD; stdcall;

  TNetUserGetInfo = function(pszServer: PWideChar;
    pszUserName: PWideChar;
    Level: DWORD;
    var pbBuffer: pointer): DWORD; stdcall;

  TNetSessionEnum = function(pszServer: PWideChar;
    pszClientName: PWideChar;
    pszUserName: PWideChar;
    Level: DWORD;
    var pbBuffer: pointer;
    PrefMaxLen: LongInt;
    var EntriesRead: DWORD;
    var TotalEntries: DWORD;
    var ResumeHandle: DWORD): DWORD; stdcall;

  TNetFileEnum = function(pszServer: PWideChar;
    pszBasePath: PWideChar;
    pszUserName: PWideChar;
    Level: DWORD;
    var pbBuffer: pointer;
    PrefMaxLen: LongInt;
    var EntriesRead: DWORD;
    var TotalEntries: DWORD;
    var ResumeHandle: DWORD): DWORD; stdcall;

  TNetGroupEnum = function(pszServer: PWideChar;
    Level: DWORD;
    var pbBuffer: pointer;
    PrefMaxLen: LongInt;
    var EntriesRead: DWORD;
    var TotalEntries: DWORD;
    var ResumeHandle: DWORD): DWORD; stdcall;

  TNetServerDiskEnum = function(pszServer: PWideChar;
    Level: DWORD;
    var pbBuffer: pointer;
    PrefMaxLen: LongInt;
    var EntriesRead: DWORD;
    var TotalEntries: DWORD;
    var ResumeHandle: DWORD): DWORD; stdcall;

  // Record Structure as returned by NTServerInfo.GetUserInfo()
  // in property TNTServerInfo.UserInfo
  TNetUserInfo = record
    AccountName: string;
    PasswordAgeDays: double;
    Privilege: byte;
    HomeDirectory: string;
    Comment: string;
    UserFlags: LongWord;
    ScriptPath: string;
    FullName: string;
    UserComment: string;
    Workstations: string;
    LastLogon: TdateTime;
    LastLogoff: TDateTime;
    AcctExpires: TDateTime;
    MaxStorage: LongWord;
    BadPWCount: LongWord;
    LogonCount: LongWord;
    LogonServer: string;
    CountryCode: LongWord;
    CodePage: LongWord;
    UserID: LongWord;
    Profile: string;
    HomeDirDrive: string;
    PasswordExpired: boolean;
  end;

  // ======================
  // Class TNTServerInfo
  // ======================
  TNTServerInfo = class(TObject)
  private
    FUserInfo: TNetUserInfo;
    FLoggedOnUserList,
      FActualUserList: TStringList;
    FServerdateTime: TDateTime;
    FLoadActualUserList,
      FLoadLoggedOnUserList,
      FServerVisible: boolean;
    FOffsetFromGMT: integer;
    FLoggedOnUsers,
      FAnnounceRate,
      FAnnounceDelta,
      FAutoDisconnectTime,
      FMaxUserAccounts,
      FServerType: DWORD;
    FUserPath,
      FVersion,
      FComment: string;
    FLibHandle: THandle;
    FNetRemoteTOD: TNetRemoteTOD;
    FNetServerGetInfo: TNetServerGetInfo;
    FNetWkstaGetInfo: TNetWkstaGetInfo;
    FNetWkstaUserEnum: TNetWkstaUserEnum;
    FNetUserEnum: TNetUserEnum;
    FNetUserGetInfo: TNetUserGetInfo;
    FNetApiBufferFree: TNetApiBufferFree;
  public
    constructor Create;
    destructor Destroy; override;
    // Call to load read-only properties
    function GetServerInfo(const MachineName: string): boolean;
    function GetUserInfo(const MachineName: string;
      const UserName: string): boolean;

    // Read only server properties
    property Comment: string read FComment;
    property Version: string read FVersion;
    property UserPath: string read FUserPath;
    property ServerType: DWORD read FServerType;
    property MaxUserAccounts: DWORD read FMaxUserAccounts;
    property ServerVisible: boolean read FServerVisible;
    property AutoDisconnectTime: DWORD read FAutoDisconnectTime;
    property AnnounceRate: DWORD read FAnnounceRate;
    property AnnounceDelta: DWORD read FAnnounceDelta;
    property ServerDateTime: TDateTime read FServerDateTime;
    property OffsetFromGMT: integer read FOffsetFromGMT;
    property LoggedOnUsers: DWORD read FLoggedOnUsers;
    property LoggedOnUserList: TStringList read FLoggedOnUserList;
    property ActualUserList: TStringList read FActualUserList;
    property UserInfo: TNetUserInfo read FUserInfo;

    // performance properties
    property LoadLoggedOnUserList: boolean read FLoadLoggedOnUserList
      write FLoadLoggedOnUserList;
    property LoadActualUserList: boolean read FLoadActualUserList
      write FLoadActualUserList;
  end;

  // Record structure for property TNTServerEventLog.LogInfo
  TLogInfoRecord = record
    RecordNumber: DWORD;
    LogTime: TDateTime;
    LogText: TStrings;
    EventType: string;
    EventID: DWORD;
    EventCategory: word;
    SourceName: string;
    ComputerName: string;
  end;

  // ========================
  // Class NTServerEventLog
  // ========================
  TNTServerEventLog = class(TObject)
  private
    FMachineName: string;
    FBof, FEof: boolean;
    FLogText: TStringList;
    FLogInfo: TLogInfoRecord;
    FBaseDate: TDateTime;
    ELogHandle: THandle;
    FActive: boolean;
    FRecordOffset,
      FNumEntries: DWORD;
  public
    constructor Create(const MachineName: string;
      const SourceName: string;
      StartingRecord: TEventLogStartPos); overload;
    constructor Create(const MachineName: string); overload;
    destructor Destroy; override;
    function OpenLog(const SourceName: string;
      StartingRecord: TEventLogStartPos): boolean;
    function Read(Direction: TEventLogReadDir): boolean;
    procedure SourceNames(StringList: TStrings);
    procedure Clear;
    property Active: boolean read FActive;
    property NumEntries: DWORD read FNumEntries;
    property Bof: boolean read FBof;
    property Eof: boolean read FEof;
    property LogInfo: TLogInfoRecord read FLogInfo;
  end;

  // Record Structure for TNTServerServices.ServiceInfo
  TNTServerServiceInfo = record
    DisplayName: string;
    Status: string;
    StartType: string;
    ErrorControl: string;
    BinaryPathName: string;
    ServiceStartName: string;
    TypeFlag: DWORD;
    ControlsFlag: DWORD;
  end;

  // ==================
  // TNTServerServices
  // ==================
  TNTServerServices = class(TObject)
  private
    FHandle: THandle;
    FActive: boolean;
    FServiceInfo: TNTServerServiceInfo;
  public
    constructor Create(const MachineName: string);
    destructor Destroy; override;
    procedure ServiceNames(StringList: TStrings;
      ServiceFields: TNTServerServiceFieldSet = [fServiceName];
      ServiceTypes: TNTServerServiceTypes = stAllServices;
      ServiceStates: TNTServerServiceStates = sAllStates);
    function GetServiceInfo(const ServiceName: string): boolean;
    function ServiceStop(const ServiceName: string): boolean;
    function ServiceStart(const ServiceName: string): boolean;
    property ServiceInfo: TNTServerServiceInfo read FServiceInfo;
  end;

  // ====================
  // TNTServerSessions
  // ====================
  TNTServerSessions = class(TObject)
  private
    FLibHandle: THandle;
    FNetSessionEnum: TNetSessionEnum;
    FNetApiBufferFree: TNetApiBufferFree;
  public
    constructor Create;
    destructor Destroy; override;
    procedure SessionNames(const MachineName: string;
      StringList: TStrings;
      SessionFields: TNTServerSessionsFieldSet = [fClientName]);
  end;

  // ====================
  // TNTServerGroups
  // ====================
  TNTServerGroups = class(TObject)
  private
    FLibHandle: THandle;
    FNetGroupEnum: TNetGroupEnum;
    FNetApiBufferFree: TNetApiBufferFree;
  public
    constructor Create;
    destructor Destroy; override;
    procedure GroupNames(const MachineName: string;
      StringList: TStrings;
      GroupFields: TNTServerGroupsFieldSet = [fGroupName]);
  end;

  // ====================
  // TNTServerDiskc
  // ====================
  TNTServerDisks = class(TObject)
  private
    FLibHandle: THandle;
    FNetServerDiskEnum: TNetServerDiskEnum;
    FNetApiBufferFree: TNetApiBufferFree;
  public
    constructor Create;
    destructor Destroy; override;
    procedure DiskNames(const MachineName: string;
      StringList: TStrings);
  end;

  // =======================
  // TNTServerOpenFiles
  // =======================
  TNTServerOpenFiles = class(TObject)
  private
    FLibHandle: THandle;
    FNetFileEnum: TNetFileEnum;
    FNetApiBufferFree: TNetApiBufferFree;
  public
    constructor Create;
    destructor Destroy; override;
    procedure OpenFiles(const MachineName: string;
      StringList: TStrings;
      OpenFileFields: TNTServerOpenFileFieldSet = [fPathName]);

  end;

  // ====================
  // System Functions
  // ====================

procedure GetServerResources(const RootObject: string;
  StringList: TStrings;
  RecursiveEnum: boolean;
  ResourceTypes: TNTServerResSet = [resAny]);

function ValidateUserLogon(const UserName: string;
  const Domain: string;
  const PassWord: string): boolean;

// -----------------------------------------------------------------------------
implementation

type

  // NetGetServerInfo Internal Structure
  PServerInfo102 = ^TServerInfo102;
  TServerInfo102 = packed record
    sv102_platform_id: DWORD;
    sv102_name: PWideChar;
    sv102_version_major: DWORD;
    sv102_version_minor: DWORD;
    sv102_type: DWORD;
    sv102_comment: PWideChar;
    sv102_users: DWORD;
    sv102_disc: LongWord;
    sv102_hidden: BOOL;
    sv102_announce: DWORD;
    sv102_anndelta: DWORD;
    sv102_userpath: PWideChar;
  end;

  // NetRemoteTOD Internal Structure
  PTimeOfDayInfo = ^TTimeOfDayInfo;
  TTimeOfDayInfo = packed record
    tod_elapsedt: DWORD;
    tod_msecs: DWORD;
    tod_hours: LongInt;
    tod_mins: DWORD;
    tod_secs: DWORD;
    tod_hunds: DWORD;
    tod_timezone: LongInt;
    tod_tinterval: DWORD;
    tod_day: DWORD;
    tod_month: DWORD;
    tod_year: DWORD;
    tod_weekday: DWORD;
  end;

  // NetWkstaGetInfo Internal Structure
  PWkstaInfo102 = ^TWkstaInfo102;
  TWkstaInfo102 = packed record
    wki102_platform_id: DWORD;
    wki102_computername: PWideChar;
    wki102_langroup: PWideChar;
    wki102_ver_major: DWORD;
    wki102_ver_minor: DWORD;
    wki102_lanroot: PWideChar;
    wki102_logged_on_users: DWORD;
  end;

  // NetWkstaUserEnum - NetUserEnum Internal Structure
  PWkstaUserInfo = ^TWkstaUserInfo;
  TWkstauserInfo = packed record
    wkui1_username: PWideChar;
  end;

  // Internal structure for GetUserInfo
  // maps into TNetUserInfo
  PNetUserInfoInternal = ^TNetUserInfoInternal;
  TNetUserInfoInternal = packed record
    AccountName: PWideChar;
    Password: PWideChar;
    PasswordAge: DWORD;
    Priv: DWORD;
    HomeDir: PWideChar;
    Comment: PWideChar;
    Flags: DWORD;
    ScriptPath: PWideChar;
    AuthFlags: PWideChar;
    FullName: PWideChar;
    UsrComment: PWideChar;
    Parms: PWideChar;
    Workstations: PWideChar;
    LastLogon: DWORD;
    LastLogoff: DWORD;
    AcctExpires: DWORD;
    MaxStorage: DWORD;
    UnitsPerWeek: DWORD;
    LogonHours: PBYTE;
    BadPWCount: DWORD;
    NumLogons: DWORD;
    LogonServer: PWideChar;
    CountryCode: DWORD;
    CodePage: DWORD;
    UserID: DWORD;
    PrimaryGroupID: DWORD;
    Profile: PWideChar;
    HomeDirDrive: PWideChar;
    PasswordExpired: DWORD;
  end;

  // Internal Structure for TNTServerSessions
  PSessionInfoStruc = ^TSessionInfoStruc;
  TSessionInfoStruc = packed record
    ses_cname: PWideChar;
    ses_username: PWideChar;
    ses_numopens: DWORD;
    ses_time: DWORD;
    ses_idletime: DWORD;
    ses_userflags: DWORD;
    ses_cltypename: PWideChar;
    ses_transport: PWideChar;
  end;

  // Internal Structure for TNTServerOpenFiles
  PFileInfoStruc = ^TFileInfoStruc;
  TFileInfoStruc = packed record
    f_id: DWORD;
    f_permissions: DWORD;
    f_numlocks: DWORD;
    f_pathname: PWideChar;
    f_username: PWideChar;
  end;

  // Internal Structure for TNTServerGroups
  PGroupInfoStruc = ^TGroupInfoStruc;
  TGroupInfoStruc = packed record
    g_name: PWideChar;
    g_comment: PWideChar;
    g_id: DWORD;
    g_attributes: DWORD;
  end;

  // Internal Structure for TNTServerDisks
  PDiskInfoStruc = ^TDiskInfoStruc;
  TDiskInfoStruc = packed record
    d_name: array[1..6] of char;
  end;

  // Internal structure for event logs
  PEventLogStruc = ^TEventLogStruc;
  TEventLogStruc = packed record
    Length: DWORD;
    Reserved: DWORD;
    RecordNumber: DWORD;
    TimeGenerated: DWORD;
    TimeWritten: DWORD;
    EventID: DWORD;
    EventType: WORD;
    NumStrings: WORD;
    EventCategory: WORD;
    ReservedFlags: WORD;
    ClosingRecordNumber: DWORD;
    StringOffset: DWORD;
    UserSidLength: DWORD;
    UserSidOffset: DWORD;
    DataLength: DWORD;
    DataOffset: DWORD;
    // Then follows Variant Area .....
    // TCHAR SourceName[]
    // TCHAR Computername[]
    // SID   UserSid
    // TCHAR Strings[]
    // BYTE  Data[]
    // CHAR  Pad[]
    // DWORD Length;
  end;

  // =============================================================================
  // TNTServerInfo
  // =============================================================================

constructor TNTServerInfo.Create;
begin
  FLoggedOnUserList := TStringList.Create;
  FActualUserList := TStringList.Create;
  FAutoDisconnectTime := 0;
  FComment := '';
  FVersion := '';
  FServerType := 0;
  FMaxUserAccounts := 0;
  FUserPath := '';
  FServerVisible := false;
  FAnnounceRate := 0;
  FAnnounceDelta := 0;
  FServerDateTime := 0.0;
  FOffsetFromGMT := 0;
  FLoggedOnUsers := 0;
  FLoadLoggedOnuserList := true;
  FLoadActualUserList := true;

  FLibHandle := LoadLibrary('NETAPI32.DLL');

  if FLibHandle <> 0 then
  begin
    @FNetRemoteTOD := GetProcAddress(FLibHandle, 'NetRemoteTOD');
    @FNetServerGetInfo := GetProcAddress(FLibHandle, 'NetServerGetInfo');
    @FNetWkstaGetInfo := GetProcAddress(FLibHandle, 'NetWkstaGetInfo');
    @FNetWkstaUserEnum := GetProcAddress(FLibHandle, 'NetWkstaUserEnum');
    @FNetUserEnum := GetProcAddress(FLibHandle, 'NetUserEnum');
    @FNetUserGetInfo := GetProcAddress(FLibHandle, 'NetUserGetInfo');
    @FNetApiBufferFree := GetProcAddress(FLibHandle, 'NetApiBufferFree');
  end;
end;

destructor TNTServerInfo.Destroy;
begin
  if FLibHandle <> 0 then
  try
    FreeLibrary(FLibHandle);
  except
  end;
  FLoggedOnUserList.Free;
  FActualUserList.Free;

  inherited Destroy;
end;

// ======================================
// Retrieve all server information
// ======================================

function TNTServerInfo.GetServerInfo(const MachineName: string): boolean;
var
  pBuffer, pDateBuffer,
    pWkstaBuffer, pUserBuffer: pointer;
  pIncUser: PWkstaUserInfo;
  Retvar: boolean;
  pszServer: array[0..128] of WideChar;
  i, NetResult: integer;
  EntriesRead, TotalEntries,
    ResumeHandle: DWORD;
begin
  FLoggedOnUserList.Clear;
  FActualUserList.Clear;
  pBuffer := nil;
  pDateBuffer := nil;
  pWkstaBuffer := nil;
  pUserBuffer := nil;
  Retvar := false;

  if FLibHandle <> 0 then
  begin
    NetResult := FNetServerGetInfo(StringToWideChar(MachineName, pszServer,
      129), 102, pBuffer);
    if NetResult = 0 then
    begin
      // SUCCESS
      Retvar := true;

      with PServerInfo102(pBuffer)^ do
      begin
        if sv102_comment <> nil then
          FComment := WideCharToString(sv102_comment)
        else
          FComment := '';

        if sv102_userpath <> nil then
          FUserPath := WideCharToString(sv102_userpath)
        else
          FUserPath := '';

        FVersion := IntToStr(sv102_version_major) + '.' +
          IntToStr(sv102_version_minor);
        FServerType := sv102_type;
        FMaxUserAccounts := sv102_users;
        FServerVisible := not boolean(sv102_hidden);
        FAutoDisconnectTime := sv102_disc;
        FAnnounceRate := sv102_announce;
        FAnnounceDelta := sv102_anndelta;
      end;

      // Try get Server Date Time
      NetResult := FNetRemoteTOD(pszServer, pDateBuffer);

      if NetResult = 0 then
      begin
        with PTimeOfDayInfo(pDateBuffer)^ do
        begin
          // Adjust to GMT
          FOffsetFromGMT := tod_timezone div 60;
          tod_hours := tod_hours - FOffsetFromGMT;
          FOffsetFromGMT := FOffsetFromGMT * -1;
          if not TryEncodeDateTime(tod_year, tod_month, tod_day,
            tod_hours, tod_mins, tod_secs, 0,
            FServerDateTime) then
            FServerDateTime := 0.0;
        end;
      end
      else
        FServerDateTime := 0.0;

      if pDateBuffer <> nil then
        FNetApiBufferFree(pDateBuffer);

      // Try get logged on user count
      NetResult := FNetWkstaGetInfo(pszServer, 102, pWkstaBuffer);
      if NetResult = 0 then
      begin
        with PWkstaInfo102(pWkstaBuffer)^ do
        begin
          FLoggedOnUsers := wki102_logged_on_users;
        end;
      end
      else
        FLoggedOnUsers := 0;

      if pWkstaBuffer <> nil then
        FNetApiBufferFree(pWkstaBuffer);

      // Add logged on user name to string list if required
      if FLoadLoggedOnUserList then
      begin
        ResumeHandle := 0;
        NetResult := FNetWkstaUserEnum(pszServer, 0, pUserBuffer, -1, EntriesRead,
          TotalEntries, Resumehandle);
        if NetResult = 0 then
        begin
          pIncUser := pUserBuffer;

          for i := 1 to EntriesRead do
          begin
            FLoggedOnUserList.Add(WideCharToString(pIncUser^.wkui1_username));
            inc(pIncUser);
          end;
        end;

        if pUserBuffer <> nil then
          FNetApiBufferFree(pUserBuffer);
      end;

      // Add actual user name to string list if required
      if FLoadActualUserList then
      begin
        pUserBuffer := nil;
        ResumeHandle := 0;
        NetResult := FNetUserEnum(pszServer, 0, 0, pUserBuffer, -1, EntriesRead,
          TotalEntries, Resumehandle);
        if NetResult = 0 then
        begin
          pIncUser := pUserBuffer;

          for i := 1 to EntriesRead do
          begin
            FActualUserList.Add(WideCharToString(pIncUser^.wkui1_username));
            inc(pIncUser);
          end;
        end;

        if pUserBuffer <> nil then
          FNetApiBufferFree(pUserBuffer);
      end;
    end
    else
    begin
      // FAILURE
      Retvar := false;
      FAutoDisconnectTime := 0;
      FComment := '';
      FVersion := '';
      FServerType := 0;
      FMaxUserAccounts := 0;
      FUserPath := '';
      FServerVisible := false;
      FAnnounceRate := 0;
      FAnnounceDelta := 0;
      FServerDateTime := 0.0;
      FOffsetFromGMT := 0;
      FLoggedOnUsers := 0;
    end;

    if pBuffer <> nil then
      FNetApiBufferFree(pBuffer);
  end;

  Result := Retvar;
end;

// ==============================================
// Load userinfo into property userinfo
// ==============================================

function TNTServerInfo.GetUserInfo(const MachineName: string;
  const UserName: string): boolean;
var
  pBuffer: pointer;
  Retvar: boolean;
  pszUserName,
    pszServer: array[0..128] of WideChar;
  NetResult: integer;
  BaseDate: TDateTime;
begin
  pBuffer := nil;
  Retvar := false;
  BaseDate := EncodeDateTime(1970, 1, 1, 0, 0, 0, 0);

  if FLibHandle <> 0 then
  begin
    NetResult := FNetUserGetInfo(StringToWideChar(MachineName, pszServer, 129),
      StringToWideChar(UserName, pszUserName, 129),
      3, pBuffer);
    if NetResult = 0 then
    begin
      // SUCCESS
      Retvar := true;
      with PNetUserInfoInternal(pBuffer)^ do
      begin
        FUserInfo.AccountName := WideCharToString(AccountName);
        FUserInfo.PasswordAgeDays := PasswordAge / 60.0 / 60.0 / 24.0;
        FUserInfo.Privilege := priv;
        FUserInfo.HomeDirectory := WideCharToString(HomeDir);
        FUserInfo.Comment := WideCharToString(Comment);
        FuserInfo.UserFlags := Flags;
        FUserInfo.ScriptPath := WideCharToString(ScriptPath);
        FUserInfo.FullName := WideCharToString(FullName);
        FUserInfo.UserComment := WideCharToString(UsrComment);
        FUserInfo.WorkStations := WideCharToString(WorkStations);
        FUserInfo.LastLogOn := IncSecond(BaseDate, LastLogon);
        FUserInfo.LastLogOff := IncSecond(BaseDate, LastLogOff);
        FUserInfo.AcctExpires := IncSecond(BaseDate, AcctExpires);
        FUserInfo.MaxStorage := MaxStorage;
        FUserInfo.BadPWCount := BadPWCount;
        FUserInfo.LogonCount := NumLogons;
        FUserInfo.LogonServer := LogonServer;
        FUserInfo.CountryCode := CountryCode;
        FUserInfo.CodePage := CodePage;
        FUserInfo.UserID := UserID;
        FUserInfo.Profile := WideCharToString(Profile);
        FUserInfo.HomeDirDrive := WideCharToString(HomeDirDrive);
        FUserInfo.PasswordExpired := (PasswordExpired <> 0);
      end;
    end
    else
    begin
      // FAILURE
      Retvar := false;
      FuserInfo.AccountName := '';
      FUserInfo.PasswordAgeDays := 0.0;
      FUserInfo.Privilege := 0;
      FUserInfo.HomeDirectory := '';
      FUserInfo.Comment := '';
      FUserInfo.UserFlags := 0;
      FUserInfo.ScriptPath := '';
      FUserInfo.FullName := '';
      FUserInfo.UserComment := '';
      FUserInfo.Workstations := '';
      FUserInfo.LastLogon := BaseDate;
      FUserInfo.LastLogoff := BaseDate;
      FUserInfo.AcctExpires := BaseDate;
      FUserInfo.MaxStorage := 0;
      FUserInfo.BadPWCount := 0;
      FUserInfo.LogonCount := 0;
      FUserInfo.LogonServer := '';
      FUserInfo.CountryCode := 0;
      FUserInfo.CodePage := 0;
      FUserInfo.UserID := 0;
      FUserInfo.Profile := '';
      FUserInfo.HomeDirDrive := '';
      FUserInfo.PasswordExpired := true;
    end;

    if pBuffer <> nil then
      FNetApiBufferFree(pBuffer);
  end;

  Result := Retvar;
end;

// =============================================================================
// TNTServerEventLog
// =============================================================================

constructor TNTServerEventLog.Create(const MachineName: string;
  const SourceName: string;
  StartingRecord: TEventLogStartPos);
begin
  FMachineName := MachineName;
  FBaseDate := EncodeDateTime(1970, 1, 1, 0, 0, 0, 0);
  ELogHandle := OpenEventLog(PChar(FMachineName), PChar(SourceName));
  FLogText := TStringList.Create;

  if ELogHandle <> 0 then
  begin
    FActive := true;
    if not GetNumberOfEventLogRecords(ELogHandle, FNumEntries) then
      FNumEntries := 0;

    if FNumEntries = 0 then
    begin
      FBof := true;
      FEof := true;
    end
    else
    begin
      if StartingRecord = sFirst then
        FRecordOffset := 1
      else
        FRecordOffset := FNumEntries;
      FBof := false;
      FEof := false;
    end;
  end
  else
  begin
    FActive := false;
    FNumEntries := 0;
    FRecordOffset := 0;
    FEof := true;
    FBof := true;
  end;

end;

constructor TNTServerEventLog.Create(const MachineName: string);
begin
  FMachineName := MachineName;
  FBaseDate := EncodeDateTime(1970, 1, 1, 0, 0, 0, 0);
  ELogHandle := 0;
  FLogText := TStringList.Create;
  FActive := false;
  FNumEntries := 0;
  FRecordOffset := 0;
  FEof := true;
  FBof := true;
end;

function TNTServerEventLog.OpenLog(const SourceName: string;
  StartingRecord: TEventLogStartPos): boolean;
begin
  if FActive then
    CloseEventLog(ELogHandle);
  ELogHandle := OpenEventLog(PChar(FMachineName), PChar(SourceName));

  if ELogHandle <> 0 then
  begin
    FActive := true;
    if not GetNumberOfEventLogRecords(ELogHandle, FNumEntries) then
      FNumEntries := 0;

    if FNumEntries = 0 then
    begin
      FBof := true;
      FEof := true;
    end
    else
    begin
      if StartingRecord = sFirst then
        FRecordOffset := 1
      else
        FRecordOffset := FNumEntries;
      FBof := false;
      FEof := false;
    end;
  end
  else
  begin
    FActive := false;
    FNumEntries := 0;
    FRecordOffset := 0;
    FEof := true;
    FBof := true;
  end;

  Result := FActive;
end;

destructor TNTServerEventLog.Destroy;
begin
  if FActive then
    CloseEventLog(ELogHandle);
  FLogText.Free;
  inherited Destroy;
end;

function TNTServerEventLog.Read(Direction: TEventLogReadDir): boolean;
var
  Retvar: boolean;
  ReadDirection,
    BytesToRead,
    i, BytesRead,
    BytesNeeded: DWORD;
  Rec: TEventLogStruc;
  Buffer: pointer;
  TxtStrings: PChar;
begin
  if FActive and (FNumEntries > 0) then
  begin
    FLogText.Clear;

    if Direction = rForwards then
      ReadDirection := EVENTLOG_SEEK_READ or EVENTLOG_FORWARDS_READ
    else
      ReadDirection := EVENTLOG_SEEK_READ or EVENTLOG_BACKWARDS_READ;

    // Cause buffer too small error to get actual buffer size
    ReadEventLog(ELogHandle, ReadDirection, FRecordOffset, @Rec,
      SizeOf(TEventLogStruc), BytesRead, BytesNeeded);
    // Calc actual size required
    GetMem(Buffer, BytesNeeded);
    BytesToRead := BytesNeeded;
    // Now read the record into correct sized buffer
    Retvar := ReadEventLog(ELogHandle, ReadDirection, FRecordOffset, Buffer,
      BytesToRead, BytesRead, BytesNeeded);

    if Retvar then
    begin
      with PEventLogStruc(Buffer)^ do
      begin
        FLogInfo.RecordNumber := FRecordOffset;
        FLogInfo.LogTime := IncSecond(FBaseDate, TimeGenerated);
        FLogInfo.EventID := EventID;
        FLogInfo.EventCategory := EventCategory;

        case EventType of
          EVENTLOG_ERROR_TYPE: FLogInfo.EventType := 'ERROR';
          EVENTLOG_WARNING_TYPE: FLogInfo.EventType := 'WARNING';
          EVENTLOG_INFORMATION_TYPE: FLogInfo.EventType := 'INFORMATION';
          EVENTLOG_AUDIT_SUCCESS: FLogInfo.EventType := 'AUDIT SUCCESS';
          EVENTLOG_AUDIT_FAILURE: FLogInfo.EventType := 'AUDIT FAILURE';
        else
          FLogInfo.EventType := 'UNKNOWN';
        end;

        // Message Text
        TxtStrings := Buffer;
        inc(TxtStrings, StringOffset);
        FLogText.Add(TxtStrings);
        BytesNeeded := System.Length(TxtStrings);

        for i := 2 to NumStrings do
        begin
          inc(TxtStrings, BytesNeeded + 1);
          FLogText.Add(TxtStrings);
          BytesNeeded := System.Length(TxtStrings);
        end;

        FLogInfo.LogText := FLogText;

        // SourceName
        TxtStrings := Buffer;
        inc(TxtStrings, SizeOf(TEventLogStruc));
        BytesNeeded := System.Length(TxtStrings);
        FLogInfo.SourceName := TxtStrings;

        // ComputerName
        inc(TxtStrings, BytesNeeded + 1);
        FLogInfo.ComputerName := TxtStrings;
      end;
    end
    else
    begin
      FLogInfo.RecordNumber := 0;
      FLogInfo.LogTime := FBaseDate;
      FLogInfo.EventType := '';
      FLogInfo.EventID := 0;
      FLogInfo.EventCategory := 0;
      FLogInfo.SourceName := '';
    end;

    FreeMem(Buffer);
    FBof := false;
    FEof := false;

    if Direction = rForwards then
    begin
      inc(FRecordOffset);
      if FRecordOffset > FNumEntries then
      begin
        FEof := true;
        FRecordOffset := FNumEntries;
      end;
    end
    else
    begin
      dec(FRecordOffset);
      if FRecordOffset = 0 then
      begin
        FBof := true;
        FRecordOffset := 1;
      end;
    end;
  end
  else
    Retvar := false;

  Result := Retvar;
end;

procedure TNTServerEventLog.Clear;
begin
  if FActive then
    ClearEventLog(ELogHandle, nil);
  FNumEntries := 0;
end;

procedure TNTServerEventLog.SourceNames(StringList: TStrings);
const
  KEYPATH = 'SYSTEM\CurrentControlSet\Services\Eventlog';
var
  WinReg: TRegistry;
begin
  StringList.Clear;
  StringList.BeginUpdate;
  WinReg := TRegistry.Create;
  WinReg.RootKey := HKEY_LOCAL_MACHINE;

  if WinReg.RegistryConnect(FMachineName) then
  begin
    if WinReg.OpenKeyReadOnly(KEYPATH) then
    begin
      WinReg.GetKeyNames(StringList);
      WinReg.CloseKey;
    end;
  end;

  WinReg.Free;
  StringList.EndUpdate;
end;

// =============================================================================
// TNTServerServices
// =============================================================================

constructor TNTServerServices.Create(const MachineName: string);
begin
  FHandle := OpenSCManager(PChar(MachineName), nil, SC_MANAGER_ALL_ACCESS);
  FActive := FHandle <> 0;
end;

destructor TNTServerServices.Destroy;
begin
  if FActive then
    CloseServiceHandle(FHandle);
  inherited Destroy;
end;

procedure TNTServerServices.ServiceNames(StringList: TStrings;
  ServiceFields: TNTServerServiceFieldSet = [fServiceName];
  ServiceTypes: TNTServerServiceTypes = stAllServices;
  ServiceStates: TNTServerServiceStates = sAllStates);
const
  MAXSERVICE = 100;

var
  SvcArr: array[1..MAXSERVICE] of _ENUM_SERVICE_STATUSA;
  ListServices, ListStates,
    E, i, BuffSize, NumEntries,
    BytesNeeded, ResumeHandle: DWORD;
  Data: string;
begin
  StringList.Clear;
  StringList.BeginUpdate;

  if FActive then
  begin
    if ServiceFields = [] then
      ServiceFields := [fServiceName];
    ResumeHandle := 0;

    case ServiceTypes of
      stWin32: ListServices := SERVICE_WIN32;
      stDrivers: ListServices := SERVICE_DRIVER;
      stAllServices: ListServices := SERVICE_TYPE_ALL;
    else
      ListServices := SERVICE_TYPE_ALL;
    end;

    case ServiceStates of
      sActive: ListStates := SERVICE_ACTIVE;
      sInActive: ListStates := SERVICE_INACTIVE;
      sAllStates: ListStates := SERVICE_STATE_ALL;
    else
      ListStates := SERVICE_STATE_ALL;
    end;

    while true do
    begin
      BuffSize := SizeOf(SvcArr);
      EnumServicesStatus(FHandle, ListServices, ListStates, SvcArr[1],
        BuffSize, BytesNeeded, NumEntries, ResumeHandle);
      E := GetLastError;

      if E in [NO_ERROR, ERROR_MORE_DATA] then
      begin
        for i := 1 to NumEntries do
        begin
          Data := '';
          if fServiceName in ServiceFields then
            Data := Data + ';' + SvcArr[i].lpServiceName;
          if fDisplayName in ServiceFields then
            Data := Data + ';' + SvcArr[i].lpDisplayName;

          if fStatus in ServiceFields then
          begin
            case SvcArr[i].ServiceStatus.dwCurrentState of
              SERVICE_STOPPED: Data := Data + ';' + 'STOPPED';
              SERVICE_START_PENDING: Data := Data + ';' + 'STARTING';
              SERVICE_STOP_PENDING: Data := Data + ';' + 'STOPPING';
              SERVICE_RUNNING: Data := Data + ';' + 'RUNNING';
              SERVICE_CONTINUE_PENDING: Data := Data + ';' + 'CONTINUING';
              SERVICE_PAUSE_PENDING: Data := Data + ';' + 'PAUSING';
              SERVICE_PAUSED: Data := Data + ';' + 'PAUSED';
            else
              Data := Data + ';' + 'UNKNOWN';
            end;
          end;

          delete(Data, 1, 1);
          StringList.Add(Data);
        end;
      end;

      if (E = NO_ERROR) or (E <> ERROR_MORE_DATA) then
        break;
    end;
  end;

  StringList.EndUpdate;
end;

function TNTServerServices.GetServiceInfo(const ServiceName: string): boolean;
var
  SHandle: THandle;
  Retvar: boolean;
  Status: TServiceStatus;
  Buffer: pointer;
  Dummy: DWORD;
  Config: PQueryServiceConfig;
  ActualSize, BytesNeeded: DWORD;
begin
  FillChar(FServiceInfo, SizeOf(TNTServerServiceInfo), 0);
  Retvar := false;

  if FActive then
  begin
    SHandle := OpenService(FHandle, PChar(ServiceName), SERVICE_ALL_ACCESS);

    if SHandle <> 0 then
    begin
      if QueryServiceStatus(SHandle, Status) then
      begin
        case Status.dwCurrentState of
          SERVICE_STOPPED: FServiceInfo.Status := 'STOPPED';
          SERVICE_START_PENDING: FServiceInfo.Status := 'STARTING';
          SERVICE_STOP_PENDING: FServiceInfo.Status := 'STOPPING';
          SERVICE_RUNNING: FServiceInfo.Status := 'RUNNING';
          SERVICE_CONTINUE_PENDING: FServiceInfo.Status := 'CONTINUING';
          SERVICE_PAUSE_PENDING: FServiceInfo.Status := 'PAUSING';
          SERVICE_PAUSED: FServiceInfo.Status := 'PAUSED';
        else
          FServiceInfo.Status := 'UNKNOWN';
        end;

        FServiceInfo.TypeFlag := Status.dwServiceType;
        FServiceInfo.ControlsFlag := Status.dwControlsAccepted;
      end;

      // Force error to get actual size required
      QueryServiceConfig(SHandle, @Dummy, SizeOf(Dummy), BytesNeeded);
      GetMem(Buffer, BytesNeeded);
      ActualSize := BytesNeeded;

      if QueryServiceConfig(SHandle, Buffer, ActualSize, BytesNeeded) then
      begin
        Config := Buffer;

        case Config.dwStartType of
          SERVICE_BOOT_START: FServiceInfo.StartType := 'BOOT';
          SERVICE_SYSTEM_START: FServiceInfo.StartType := 'SYSTEM';
          SERVICE_AUTO_START: FServiceInfo.StartType := 'AUTO';
          SERVICE_DEMAND_START: FServiceInfo.StartType := 'MANUAL';
          SERVICE_DISABLED: FServiceInfo.StartType := 'DISABLED';
        else
          FServiceInfo.StartType := 'UNKNOWN';
        end;

        case Config.dwErrorControl of
          SERVICE_ERROR_IGNORE: FServiceInfo.ErrorControl := 'IGNORE';
          SERVICE_ERROR_NORMAL: FServiceInfo.ErrorControl := 'NORMAL';
          SERVICE_ERROR_SEVERE: FServiceInfo.ErrorControl := 'SEVERE';
          SERVICE_ERROR_CRITICAL: FServiceInfo.ErrorControl := 'CRITICAL';
        else
          FServiceInfo.ErrorControl := 'UNKNOWN';
        end;

        FServiceInfo.BinaryPathName := Config.lpBinaryPathName;
        FServiceInfo.DisplayName := Config.lpDisplayName;
        FServiceInfo.ServiceStartName := Config.lpServiceStartName;
      end;

      FreeMem(Buffer);
      CloseServiceHandle(SHandle);
    end;
  end;

  Result := Retvar;
end;

function TNTServerServices.ServiceStop(const ServiceName: string): boolean;
var
  SHandle: THandle;
  Status: TServiceStatus;
  dwCheckPoint: DWORD;
  Retvar: boolean;
begin
  Screen.Cursor := crHourGlass;
  Application.ProcessMessages;
  Retvar := false;

  if FActive then
  begin
    SHandle := OpenService(FHandle, PChar(ServiceName), SERVICE_ALL_ACCESS);

    if SHandle <> 0 then
    begin
      if (ControlService(SHandle, SERVICE_CONTROL_STOP, Status)) then
      begin
        if (QueryServiceStatus(SHandle, Status)) then
        begin
          while (SERVICE_STOPPED <> Status.dwCurrentState) do
          begin
            dwCheckPoint := Status.dwCheckPoint;
            Sleep(Status.dwWaitHint);

            if (not QueryServiceStatus(SHandle, Status)) then
            begin
              // couldn't check status
              break;
            end;

            if (Status.dwCheckPoint < dwCheckPoint) then
              break;
          end;
        end;
      end;
      CloseServiceHandle(SHandle);
    end;

    Retvar := (SERVICE_STOPPED = Status.dwCurrentState);
  end;

  Screen.Cursor := crDefault;
  Result := Retvar;
end;

function TNTServerServices.ServiceStart(const ServiceName: string): boolean;
var
  SHandle: THandle;
  Status: TServiceStatus;
  Temp: PChar;
  dwCheckPoint: DWORD;
  Retvar: boolean;
begin
  Screen.Cursor := crHourGlass;
  Application.ProcessMessages;
  Retvar := false;

  if FActive then
  begin
    Status.dwCurrentState := 1;

    SHandle := OpenService(FHandle, PChar(ServiceName), SERVICE_ALL_ACCESS);

    if SHandle <> 0 then
    begin
      Temp := nil;
      if (StartService(SHandle, 0, Temp)) then
        if (QueryServiceStatus(SHandle, Status)) then
        begin
          while (SERVICE_RUNNING <> Status.dwCurrentState) do
          begin
            dwCheckPoint := Status.dwCheckPoint;
            Sleep(Status.dwWaitHint);
            if (not QueryServiceStatus(SHandle, Status)) then
              break;
            if (Status.dwCheckPoint < dwCheckPoint) then
            begin
              // QueryServiceStatus didn't increment dwCheckPoint
              break;
            end;
          end;
        end;
      CloseServiceHandle(SHandle);
    end;

    Retvar := (SERVICE_RUNNING = Status.dwCurrentState);
  end;

  Screen.Cursor := crDefault;
  Result := Retvar;
end;

// =============================================================================
// TNTServerSessions
// =============================================================================

constructor TNTServerSessions.Create;
begin
  FLibHandle := LoadLibrary('NETAPI32.DLL');

  if FLibHandle <> 0 then
  begin
    @FNetSessionEnum := GetProcAddress(FLibHandle, 'NetSessionEnum');
    @FNetApiBufferFree := GetProcAddress(FLibHandle, 'NetApiBufferFree');
  end;

end;

destructor TNTServerSessions.Destroy;
begin
  if FLibHandle <> 0 then
  try
    FreeLibrary(FLibHandle);
  except
  end;

  inherited Destroy;
end;

procedure TNTServerSessions.SessionNames(const MachineName: string;
  StringList: TStrings;
  SessionFields: TNTServerSessionsFieldSet = [fClientName]);
var
  i, E, ResumeHandle,
    TotalEntries, EntriesRead: DWORD;
  pSessBuffer: pointer;
  pIncSess: PSessionInfoStruc;
  pszServer: array[0..128] of WideChar;
  Data: string;
begin
  StringList.Clear;
  StringList.BeginUpdate;

  if FLibHandle <> 0 then
  begin
    pSessBuffer := nil;
    if SessionFields = [] then
      SessionFields := [fClientName];
    ResumeHandle := 0;
    E := FNetSessionEnum(StringToWideChar(MachineName, pszServer, 129), nil,
      nil, 502, pSessBuffer, -1, EntriesRead, TotalEntries,
      Resumehandle);
    if E = 0 then
    begin
      pIncSess := pSessBuffer;

      for i := 1 to EntriesRead do
      begin
        Data := '';
        if fClientName in SessionFields then
          Data := Data + ';' + WideCharToString(pIncSess^.ses_cname);
        if fUserName in SessionFields then
          Data := Data + ';' + WideCharToString(pIncSess^.ses_username);
        if fNumOpens in SessionFields then
          Data := Data + ';' + IntToStr(pIncSess^.ses_numopens);
        if fTimeActive in SessionFields then
          Data := Data + ';' + IntToStr(pIncSess^.ses_time);
        if fIdleTime in SessionFields then
          Data := Data + ';' + IntToStr(pIncSess^.ses_idletime);

        if fUserFlag in SessionFields then
        begin
          case pIncSess^.ses_userflags of
            SESS_GUEST: Data := Data + ';' + 'GUEST';
            SESS_NOENCRYPTION: Data := Data + ';' + 'NO ENCRYPTION';
          else
            Data := Data + ';' + 'NONE';
          end;
        end;

        if fClientType in SessionFields then
          Data := Data + ';' + WideCharToString(pIncSess^.ses_cltypename);
        if fTransport in SessionFields then
          Data := Data + ';' + WideCharToString(pIncSess^.ses_transport);

        delete(Data, 1, 1);
        StringList.Add(Data);
        inc(pIncSess);
      end;
    end;

    if pSessBuffer <> nil then
      FNetApiBufferFree(pSessBuffer);
  end;

  StringList.EndUpdate;
end;

// =============================================================================
// TNTServerSessions
// =============================================================================

constructor TNTServerOpenFiles.Create;
begin
  FLibHandle := LoadLibrary('NETAPI32.DLL');

  if FLibHandle <> 0 then
  begin
    @FNetFileEnum := GetProcAddress(FLibHandle, 'NetFileEnum');
    @FNetApiBufferFree := GetProcAddress(FLibHandle, 'NetApiBufferFree');
  end;

end;

destructor TNTServerOpenFiles.Destroy;
begin
  if FLibHandle <> 0 then
  try
    FreeLibrary(FLibHandle);
  except
  end;

  inherited Destroy;
end;

procedure TNTServerOpenFiles.OpenFiles(const Machinename: string;
  StringList: TStrings;
  OpenFileFields: TNTServerOpenFileFieldSet = [fPathName]);
var
  i, E, ResumeHandle,
    TotalEntries, EntriesRead: DWORD;
  pFileBuffer: pointer;
  pIncBuff: PFileInfoStruc;
  pszServer: array[0..128] of WideChar;
  Perm, Data: string;
begin
  StringList.Clear;
  StringList.BeginUpdate;

  if FLibHandle <> 0 then
  begin
    pFileBuffer := nil;
    if OpenFileFields = [] then
      OpenFileFields := [fPathName];
    ResumeHandle := 0;
    E := FNetFileEnum(StringToWideChar(MachineName, pszServer, 129), nil,
      nil, 3, pFileBuffer, -1, EntriesRead, TotalEntries,
      Resumehandle);
    if E = 0 then
    begin
      pIncBuff := pFileBuffer;

      for i := 1 to EntriesRead do
      begin
        Data := '';

        if fPathName in OpenFileFields then
          Data := Data + ';' + WideCharToString(pIncBuff^.f_pathname);
        if fUser in OpenFileFields then
          Data := Data + ';' + WideCharToString(pIncBuff^.f_username);
        if fFileID in OpenFileFields then
          Data := Data + ';' + IntToStr(pIncBuff^.f_id);

        if fPermissions in OpenFileFields then
        begin
          Perm := '';
          if (PERM_FILE_READ and pIncBuff^.f_permissions) = PERM_FILE_READ then
            Perm := 'R'
          else
            Perm := '-';

          if (PERM_FILE_WRITE and pIncBuff^.f_permissions) = PERM_FILE_WRITE then
            Perm := Perm + 'W'
          else
            Perm := Perm + '-';

          if (PERM_FILE_CREATE and pIncBuff^.f_permissions) = PERM_FILE_CREATE then
            Perm := Perm + 'C'
          else
            Perm := Perm + '-';

          Data := Data + ';' + Perm;
        end;

        if fNumLocks in OpenFileFields then
          Data := Data + ';' + IntToStr(pIncBuff^.f_NumLocks);

        delete(Data, 1, 1);
        StringList.Add(Data);
        inc(pIncBuff);
      end;
    end;

    if pFileBuffer <> nil then
      FNetApiBufferFree(pFileBuffer);
  end;

  StringList.EndUpdate;
end;

// =============================================================================
// TNTServerGroups
// =============================================================================

constructor TNTServerGroups.Create;
begin
  FLibHandle := LoadLibrary('NETAPI32.DLL');

  if FLibHandle <> 0 then
  begin
    @FNetGroupEnum := GetProcAddress(FLibHandle, 'NetGroupEnum');
    @FNetApiBufferFree := GetProcAddress(FLibHandle, 'NetApiBufferFree');
  end;

end;

destructor TNTServerGroups.Destroy;
begin
  if FLibHandle <> 0 then
  try
    FreeLibrary(FLibHandle);
  except
  end;

  inherited Destroy;
end;

procedure TNTServerGroups.GroupNames(const Machinename: string;
  StringList: TStrings;
  GroupFields: TNTServerGroupsFieldSet = [fGroupName]);
var
  i, E, ResumeHandle,
    TotalEntries, EntriesRead: DWORD;
  pGroupBuffer: pointer;
  pIncBuff: PGroupInfoStruc;
  pszServer: array[0..128] of WideChar;
  Data: string;
begin
  StringList.Clear;
  StringList.BeginUpdate;

  if FLibHandle <> 0 then
  begin
    pGroupBuffer := nil;
    if GroupFields = [] then
      GroupFields := [fGroupName];
    ResumeHandle := 0;
    E := FNetGroupEnum(StringToWideChar(MachineName, pszServer, 129),
      2, pGroupBuffer, -1, EntriesRead, TotalEntries,
      Resumehandle);
    if E = 0 then
    begin
      pIncBuff := pGroupBuffer;

      for i := 1 to EntriesRead do
      begin
        Data := '';

        if fGroupName in GroupFields then
          Data := Data + ';' + WideCharToString(pIncBuff^.g_name);
        if fComment in GroupFields then
          Data := Data + ';' + WideCharToString(pIncBuff^.g_comment);
        if fGroupID in GroupFields then
          Data := Data + ';' + IntToStr(pIncBuff^.g_id);
        if fAttributeFlag in GroupFields then
          Data := Data + ';' + IntToStr(pIncBuff^.g_attributes);

        delete(Data, 1, 1);
        StringList.Add(Data);
        inc(pIncBuff);
      end;
    end;

    if pGroupBuffer <> nil then
      FNetApiBufferFree(pGroupBuffer);
  end;

  StringList.EndUpdate;
end;

// =============================================================================
// TNTServerDisks
// =============================================================================

constructor TNTServerDisks.Create;
begin
  FLibHandle := LoadLibrary('NETAPI32.DLL');

  if FLibHandle <> 0 then
  begin
    @FNetServerDiskEnum := GetProcAddress(FLibHandle, 'NetServerDiskEnum');
    @FNetApiBufferFree := GetProcAddress(FLibHandle, 'NetApiBufferFree');
  end;

end;

destructor TNTServerDisks.Destroy;
begin
  if FLibHandle <> 0 then
  try
    FreeLibrary(FLibHandle);
  except
  end;

  inherited Destroy;
end;

procedure TNTServerDisks.DiskNames(const Machinename: string;
  StringList: TStrings);
var
  i, E, ResumeHandle,
    TotalEntries, EntriesRead: DWORD;
  pDiskBuffer: pointer;
  pIncBuff: PDiskInfoStruc;
  pszServer: array[0..128] of WideChar;
begin
  StringList.Clear;
  StringList.BeginUpdate;

  if FLibHandle <> 0 then
  begin
    pDiskBuffer := nil;
    ResumeHandle := 0;
    E := FNetServerDiskEnum(StringToWideChar(MachineName, pszServer, 129),
      0, pDiskBuffer, -1, EntriesRead, TotalEntries,
      Resumehandle);
    if E = 0 then
    begin
      pIncBuff := pDiskBuffer;

      for i := 1 to EntriesRead do
      begin
        StringList.Add(WideCharToString(@pIncBuff^.d_name));
        inc(pIncBuff);
      end;
    end;

    if pDiskBuffer <> nil then
      FNetApiBufferFree(pDiskBuffer);
  end;

  StringList.EndUpdate;
end;

// =============================================================================
// GetServerResources()
//
// Return ';' delimted list of Resources from
// a given starting point '' enums from ROOT
// Return String Format
// 'Name;Object;Type;Status;Provider;Comment'
// =============================================================================

procedure GetServerResources(const RootObject: string;
  StringList: TStrings;
  RecursiveEnum: boolean;
  ResourceTypes: TNTServerResSet = [resAny]);
var
  NetResource: TNetResource;
  SaveRecurse: boolean;
  RootName: string;

  procedure RecurseResources(Pnr: PNetResource);
  var
    Buffer: pointer;
    FHandle: THandle;
    i, E, NumEntries, BuffSize: DWORD;
    Data: string;
    Ok: boolean;
    IsRoot: boolean;
  begin
    IsRoot := (Pnr.lpLocalName = nil);
    BuffSize := $4000;
    E := WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, Pnr, FHandle);

    if E = NO_ERROR then
    begin
      // Get required size - force errors and value => BuffSize
      GetMem(Buffer, BuffSize);
      while true do
      begin
        NumEntries := $FFFFFFFF;
        E := WNetEnumResource(FHandle, NumEntries, Buffer, BuffSize);
        if (E <> NO_ERROR) then
          break; // No more items or error
        Pnr := Buffer;

        for i := 1 to NumEntries do
        begin
          Ok := false;
          if IsRoot then
            Ok := true;

          if resAny in ResourceTypes then
            Ok := true
          else
          begin
            if (resPrinter in ResourceTypes) and
              (Pnr.dwType = RESOURCETYPE_PRINT) then
              Ok := true;
            if (resDisk in ResourceTypes) and
              (Pnr.dwType = RESOURCETYPE_DISK) then
              Ok := true;
            if (resServer in ResourceTypes) and
              (Pnr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER) and
              ((Pnr.dwType <> RESOURCETYPE_DISK) and
              (Pnr.dwType <> RESOURCETYPE_PRINT)) then
              Ok := true;
            if (resDomain in ResourceTypes) and
              (Pnr.dwDisplayType = RESOURCEDISPLAYTYPE_DOMAIN) and
              ((Pnr.dwType <> RESOURCETYPE_DISK) and
              (Pnr.dwType <> RESOURCETYPE_PRINT)) then
              Ok := true;
          end;

          if Ok then
          begin
            Data := Pnr.lpRemoteName + ';';

            case Pnr.dwDisplayType of
              RESOURCEDISPLAYTYPE_DOMAIN: Data := Data + 'DOMAIN;';
              RESOURCEDISPLAYTYPE_GENERIC: Data := Data + 'OTHER;';
              RESOURCEDISPLAYTYPE_SERVER: Data := Data + 'SERVER;';
              RESOURCEDISPLAYTYPE_SHARE: Data := Data + 'SHARE;';
              RESOURCEDISPLAYTYPE_FILE: Data := Data + 'FILE;';
              RESOURCEDISPLAYTYPE_GROUP: Data := Data + 'GROUP;';
              RESOURCEDISPLAYTYPE_NETWORK: Data := Data + 'NETWORK;';
              RESOURCEDISPLAYTYPE_SHAREADMIN: Data := Data + 'SHAREADMIN;';
              RESOURCEDISPLAYTYPE_DIRECTORY: Data := Data + 'DIRECTORY;';
              RESOURCEDISPLAYTYPE_TREE: Data := Data + 'TREE;';
              RESOURCEDISPLAYTYPE_NDSCONTAINER: Data := Data + 'NDSCONTAINER;';
            else
              Data := Data + 'UNKNOWN;';
            end;

            case Pnr.dwType of
              RESOURCETYPE_ANY: Data := Data + 'OTHER;';
              RESOURCETYPE_DISK: Data := Data + 'DISK;';
              RESOURCETYPE_PRINT: Data := Data + 'PRINTER;';
            else
              Data := Data + 'UNKNOWN;';
            end;

            case Pnr.dwUsage of
              RESOURCEUSAGE_CONNECTABLE: Data := Data + 'CONNECTABLE;';
              RESOURCEUSAGE_CONTAINER: Data := Data + 'CONTAINER;';
              RESOURCEUSAGE_NOLOCALDEVICE: Data := Data + 'NOLOCALDEVICE;';
              RESOURCEUSAGE_SIBLING: Data := Data + 'SIBLING;';
              RESOURCEUSAGE_ATTACHED: Data := Data + 'ATTACHED;';
            else
              Data := Data + 'UNKNOWN;';
            end;

            Data := Data + Pnr.lpProvider + ';';
            Data := Data + Pnr.lpComment;
            StringList.Add(Data);
          end;

          if RecursiveEnum and
            ((Pnr.dwUsage and RESOURCEUSAGE_CONTAINER) > 0) then
            RecurseResources(Pnr);

          inc(Longint(Pnr), SizeOf(TNetResource));
        end;
      end;

      FreeMem(Buffer);
      WNetCloseEnum(FHandle);
    end;
  end;

begin
  StringList.Clear;
  Screen.Cursor := crHourGlass;
  Application.ProcessMessages;
  Fillchar(NetResource, SizeOf(TNetResource), 0);
  NetResource.dwScope := RESOURCE_GLOBALNET;
  NetResource.dwUsage := RESOURCEUSAGE_CONTAINER;
  NetResource.lpRemoteName := PChar(RootObject);
  SaveRecurse := RecursiveEnum;
  StringList.BeginUpdate;

  // if RootObject is '' then getfirst entry
  if RootObject = '' then
  begin
    RecursiveEnum := false;
    RecurseResources(@NetResource);
    if (StringList.Count > 0) then
    begin
      RootName := copy(StringList[0], 1, pos(';', StringList[0]) - 1);
      Fillchar(NetResource, SizeOf(TNetResource), 0);
      NetResource.dwScope := RESOURCE_GLOBALNET;
      NetResource.dwUsage := RESOURCEUSAGE_RESERVED;
      NetResource.dwDisplayType := RESOURCEDISPLAYTYPE_NETWORK;
      NetResource.lpRemoteName := PChar(RootName);
    end;
    RecursiveEnum := SaveRecurse;
    StringList.Clear;
  end;

  RecurseResources(@NetResource);
  StringList.EndUpdate;
  Screen.Cursor := crDefault;
end;

// ================================================
// Validate Username,Domain and Password logon
// ================================================

function ValidateUserLogon(const UserName: string;
  const Domain: string;
  const PassWord: string): boolean;
var
  Retvar: boolean;
  LHandle: THandle;
begin

  Retvar := LogonUser(PChar(UserName), PChar(Domain), PChar(PassWord),
    LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT,
    LHandle);
  if Retvar then
    CloseHandle(LHandle);
  Result := Retvar;
end;

end.