2010. április 14., szerda

How to get the file name (and directory) of a file in the clipboard


Problem/Question/Abstract:

I need to have the filename and directory of the file which is in the clipboard. For example: In the Explorer I select a file, press ctrl+c and in my application I would like copy or do anything with it.

Answer:

procedure TMainForm.Button1Click(Sender: TObject);
var
  hDrop: THandle;
  cnt, i: Integer;
  filename: array[0..MAX_PATH - 1] of Char;
begin
  if ClipBoard.HasFormat(CF_HDROP) then
  begin
    hDrop := ClipBoard.GetAsHandle(CF_HDROP);
    cnt := DragQueryFile(hDrop, $FFFFFFFF, nil, 0);
    Memo.Clear;
    Memo.Lines.Add(Format('Found %d files:', [cnt]));
    for i := 0 to cnt - 1 do
    begin
      DragQueryFile(hDrop, i, @filename, MAX_PATH);
      Memo.lines.Add(Format('%2d: %s', [i + 1, filename]));
    end;
  end
  else
    MessageDlg(' No filename in clipboard!', mtInformation, [mbOk], 0);
end;

2010. április 13., kedd

Accessing HotMail from Delphi


Problem/Question/Abstract:

Is it possible to access a HotMail account through the POP3 and SMTP? I know its a web front end but I'd like to write a automated application which can send and receive mail using a HotMail account.

Answer:

HotMail is not accessible through POP/SMTP. you must use their web interface.

There is no possible shortcut to read mail but you can open the default HotMail account in new message mode using this ShellExecute call:


program dummy;

var
  ToAddress: string;
  EightSpaces: string;

begin
  ToAddress := 'john@pacbell.net';
  // Don't know why but this is required to get the
  // correct compose address...
  EightSpaces := '        ';
  ShellExecute(Handle, PChar('open'), PChar('rundll32.exe'),
    PChar('C:\PROGRA~1\INTERN~1\HMMAPI.DLL,MailToProtocolHandler'
    + EightSpaces + ToAddress), nil, SW_NORMAL)
end.

2010. április 12., hétfő

How to get the system's colour palette


Problem/Question/Abstract:

I would like to automatically select colours for painting. At program start I want to use the 16 colour palette. Should the user need more colours, I would like to switch to the 256 colour palette. How can I do this?

Answer:

You can retrieve the system palette. Palettes are normally used in 16 and 256 colour mode, but the Highcolor mode also uses a system palette, which defines a range of standard colours. The following example shows how to retrieve the system palette in 256c mode:


procedure TForm1.Button1Click(Sender: TObject);
type
  TPal = array[0..255] of TPaletteEntry;
var
  pPal: ^TPal;
  i, numEntries: Integer;
begin
  pPal := nil;
  numEntries := GetSystemPaletteEntries(Canvas.handle, 0, 8, pPal^);
  if numEntries > 256 then
    numEntries := 256;
  pPal := AllocMem(numEntries * Sizeof(TPaletteEntry));
  GetSystemPaletteEntries(Canvas.Handle, 0, numEntries, pPal^);
  memo1.clear;
  for i := 0 to numEntries - 1 do
    with pPal^[i] do
      memo1.lines.add(Format('Color %d: R= %d, G= %d, B= %d', [i, pered, pegreen, peblue]));
end;

2010. április 11., vasárnap

Get the local charset of a system


Problem/Question/Abstract:

How to get the local charset of a system

Answer:

function TranslateCharsetInfoEx(lpSrc: PDWORD; var lpCs: TCharsetInfo;
  dwFlags: DWORD): BOOL; stdcall; external 'gdi32.dll' name 'TranslateCharsetInfo';

procedure SetLocaleCharset;
var
  LCID, CP: Cardinal;
  Buf: array[0..6] of Char;
  CSI: TCharsetInfo;
begin
  LCID := GetThreadLocale;
  GetLocaleInfo(LCID, LOCALE_IDefaultAnsiCodePage, Buf, 6);
  CP := StrToIntDef(Buf, GetACP);
  TranslateCharsetInfoEx(Pointer(CP), CSI, TCI_SRCCODEPAGE);
  LocaleCharSet := CSI.ciCharset;
end;

2010. április 10., szombat

Solving some problems of the TRichEdit component


Problem/Question/Abstract:

Solving some problems of the TRichEdit component.

Answer:

Problem 1
========
If you work with a TRichEdit, you'll find out that it has some problems.
In the Delphi help files you'll find a FindText and a ReplaceText
procedure as an example. Those procedures work fine on most operating
systems, but NOT on Windows 2000! If you set RichEdit1.SelStart and
RichEdit1.SelLength, under most operating systems the RichEdit scrolls
to the selected text. But not on Windows 2000. There you'll have to add
a: SendMessage(EM_SCROLLCARET,0,0); after you made the selection with
SelStart and SelLength. Only then the RichEdit under Windows 2000 will
scroll to the position of the cursor!

Problem 2
========
If you drop a RichEdit on a form, Delphi (5) doesn't add the richedit.dcu
unit to your uses clause. However, if you have included the richedit.dcu
unit yourself (manually) to your uses clause, then the following problem
occures:

In the unit "messages.dcu": EM_SCROLLCARET is defined as:
   EM_SCROLLCARET = $00B7;
  
In the unit "richedit.dcu": EM_SCROLLCARET is defined as:
   EM_SCROLLCARET = WM_USER + 49;
  
Because WM_USER is defined as $0400 (in messages.dcu) the value of
EM_SCROLLCARET becomes: $400 + 49 (decimal) = 1073 (decimal) after you
have added "richedit" to your uses clause. And that is the reason why
EM_SCROLLCARET will not work anymore... (At least under W2000. I didn't
test it on other operating systems.)

A workaround for this problem is to globally add:

Const
   EM_SCROLLCARET = $00B7;
  
to your unit.            

2010. április 9., péntek

How to use non-standard colors in Windows


Problem/Question/Abstract:

My client wants to preserve a unique look and feel that does not depend upon the Windows color settings. For example, no matter what color the user has selected for his button color, my client wants to define the color for his application. A specific example involves SpinEdits. I'd like to define the color of the edit and the spin buttons. The easiest way for me to do this is to be able to redefine Windows colors internal to my application.

Answer:

The problem is more complicated as it seems at first glance. If your application stays on top long enough and you are pretty sure that the users aren't switching between apps at runtime, you can change the colors system wide. The sample below (quite simple, but enough to give you an idea how to do it) will help you to have the same colors on all machines, independent of the user choice. The advantage of this approach is, that you are able to use standard controls (which works fine most of the time).

In this sample I am changing 11 options. Have a look in the help files for all available options.

unit Unit1;

interface

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

type
  tarr = array[1..11] of integer;
  TForm1 = class(TForm)
    Button2: TButton;
    procedure Button2Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    procedure FormDeactivate(Sender: TObject);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  ind: tarr;

implementation

{$R *.DFM}

procedure TForm1.Button2Click(Sender: TObject);
var
  p1, p2: tarr;
begin
  p1[1] := COLOR_ACTIVEBORDER;
  p1[2] := COLOR_APPWORKSPACE;
  p1[3] := COLOR_BTNFACE;
  p1[4] := COLOR_BTNHILIGHT;
  p1[5] := COLOR_BTNSHADOW;
  p1[6] := COLOR_BTNTEXT;
  p1[7] := COLOR_GRAYTEXT;
  p1[8] := COLOR_INACTIVEBORDER;
  p1[9] := COLOR_INACTIVECAPTION;
  {p1[9] := COLOR_INACTIVECAPTIONTEXT;}
  p1[10] := COLOR_MENU;
  p1[11] := COLOR_SCROLLBAR;
  p2[1] := 128 * 16 * 16 * 16 * 16 + 111 * 16 * 16 + 150;
  p2[2] := 84 * 16 * 16 * 16 * 16 + 72 * 16 * 16 + 100;
  p2[3] := 128 * 16 * 16 * 16 * 16 + 111 * 16 * 16 + 150;
  p2[4] := 192 * 16 * 16 * 16 * 16 + 184 * 16 * 16 + 203;
  p2[5] := 84 * 16 * 16 * 16 * 16 + 72 * 16 * 16 + 100;
  p2[6] := 0;
  p2[7] := 84 * 16 * 16 * 16 * 16 + 72 * 16 * 16 + 100;
  p2[8] := 128 * 16 * 16 * 16 * 16 + 111 * 16 * 16 + 150;
  p2[9] := 84 * 16 * 16 * 16 * 16 + 72 * 16 * 16 + 100;
  p2[10] := 128 * 16 * 16 * 16 * 16 + 111 * 16 * 16 + 150;
  p2[11] := 192 * 16 * 16 * 16 * 16 + 184 * 16 * 16 + 203;
  setsyscolors(11, p1, p2);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  p1: tarr;
begin
  p1[1] := COLOR_ACTIVEBORDER;
  p1[2] := COLOR_APPWORKSPACE;
  p1[3] := COLOR_BTNFACE;
  p1[4] := COLOR_BTNHILIGHT;
  p1[5] := COLOR_BTNSHADOW;
  p1[6] := COLOR_BTNTEXT;
  p1[7] := COLOR_GRAYTEXT;
  p1[8] := COLOR_INACTIVEBORDER;
  p1[9] := COLOR_INACTIVECAPTION;
  {p1[9] := COLOR_INACTIVECAPTIONTEXT;}
  p1[10] := COLOR_MENU;
  p1[11] := COLOR_SCROLLBAR;
  setsyscolors(11, p1, ind);
end;

procedure TForm1.FormDeactivate(Sender: TObject);
var
  p1: tarr;
begin
  p1[1] := COLOR_ACTIVEBORDER;
  p1[2] := COLOR_APPWORKSPACE;
  p1[3] := COLOR_BTNFACE;
  p1[4] := COLOR_BTNHILIGHT;
  p1[5] := COLOR_BTNSHADOW;
  p1[6] := COLOR_BTNTEXT;
  p1[7] := COLOR_GRAYTEXT;
  p1[8] := COLOR_INACTIVEBORDER;
  p1[9] := COLOR_INACTIVECAPTION;
  {p1[9] := COLOR_INACTIVECAPTIONTEXT;}
  p1[10] := COLOR_MENU;
  p1[11] := COLOR_SCROLLBAR;
  setsyscolors(11, p1, ind);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ind[1] := getsyscolor(COLOR_ACTIVEBORDER);
  ind[2] := getsyscolor(COLOR_APPWORKSPACE);
  ind[3] := getsyscolor(COLOR_BTNFACE);
  ind[4] := getsyscolor(COLOR_BTNHILIGHT);
  ind[5] := getsyscolor(COLOR_BTNSHADOW);
  ind[6] := getsyscolor(COLOR_BTNTEXT);
  ind[7] := getsyscolor(COLOR_GRAYTEXT);
  ind[8] := getsyscolor(COLOR_INACTIVEBORDER);
  ind[9] := getsyscolor(COLOR_INACTIVECAPTION);
  {ind[9] := getsyscolor(COLOR_INACTIVECAPTIONTEXT);}
  ind[10] := getsyscolor(COLOR_MENU);
  ind[11] := getsyscolor(COLOR_SCROLLBAR);
  application.OnDeactivate := formdeactivate;
  application.OnActivate := button2click;
end;

end.

2010. április 8., csütörtök

How to get a list of all network adapters of a PC


Problem/Question/Abstract:

How to get a list of all network adapters of a PC

Answer:

Example which can be used for all other hardware, too. Doesn't seem to work for Win 2000 though.

uses
  Registry;

procedure GetNetworkAdapters(const List: TStrings);
var
  R: TRegistry;
  i: Integer;
begin
  List.BeginUpdate;
  try
    R := TRegistry.Create;
    try
      R.RootKey := HKEY_DYN_DATA;
      if R.OpenKeyReadOnly('\Config Manager\Enum') then
      try
        R.GetKeyNames(List);
      finally
        R.CloseKey;
      end;
      for i := List.Count - 1 downto 0 do
        if (List[i] = '') or
                                        not R.OpenKeyReadOnly('\Config Manager\Enum\' + List[i]) then
          List.Delete(i)
        else
        try
          List[i] := R.ReadString('HardwareKey');
        finally
          R.CloseKey;
        end;
      R.RootKey := HKEY_LOCAL_MACHINE;
      for i := List.Count - 1 downto 0 do
        if (List[i] = '') or not R.OpenKeyReadOnly('\Enum\' + List[i]) then
          List.Delete(i)
        else
        try
          if CompareText(R.ReadString('Class'), 'net') = 0 then
            List[i] := R.ReadString('DeviceDesc')
          else
            List.Delete(i);
        finally
          R.CloseKey;
        end;
    finally
      R.Free;
    end;
  finally
    List.EndUpdate;
  end;
end;

2010. április 7., szerda

How to disable the CD autorun feature through code


Problem/Question/Abstract:

How to disable the CD autorun feature through code

Answer:

Solve 1:

uses
  Registry;

function IsCdAutoRunOn: bool;
var
  reg: TRegistry;
  AutoRunSetting: integer;
begin
  reg := TRegistry.Create;
  reg.RootKey := HKEY_CURRENT_USER;
  reg.OpenKey('Software\Microsoft\Windows\' + 'CurrentVersion\Policies\Explorer',
    false);
  reg.ReadBinaryData('NoDriveTypeAutoRun', +AutoRunSetting, sizeof(AutoRunSetting));
  reg.CloseKey;
  reg.free;
  result := not ((AutoRunSetting and (1 shl 5)) <> 0);
end;

procedure SetCdAutoRun(bOn: bool);
var
  reg: TRegistry;
  AutoRunSetting: integer;
begin
  reg := TRegistry.Create;
  reg.RootKey := HKEY_CURRENT_USER;
  reg.LazyWrite := false;
  reg.OpenKey('Software\Microsoft\Windows\' + 'CurrentVersion\Policies\Explorer',
    false);
  reg.ReadBinaryData('NoDriveTypeAutoRun', +AutoRunSetting, sizeof(AutoRunSetting));
  if bOn then
    AutoRunSetting := AutoRunSetting and not (1 shl 5)
  else
    AutoRunSetting := AutoRunSetting or (1 shl 5);
  reg.WriteBinaryData('NoDriveTypeAutoRun', +AutoRunSetting, sizeof(AutoRunSetting));
  reg.CloseKey;
  reg.free;
end;


Solve 2:

After an hour of tinkering with some samples on the Internet I came up with the proper registry setting to turn on/off the CDROM AutoRun feature. The first thing wrong with all the samples was the type of value we need to store in the registry, I found that binary is what works. The next thing most samples neglected to say, besides being just wrong, was that you need to reboot your system after making the change. This tip was tested on Windows98, so this registry setting may differ a little for other operating systems. We will be using the TRegistry object using TRegistry.KeyExists, TRegistry.OpenKey, and TRegistry.WriteBinaryData

First we will need to add Registry to our unit's uses clause
Next we will need a TButton and a TButton.OnClick event. You can easily create this event through the Object Inspector Events tab
We will then declare a new procedure that will make the settings according to the boolean parameter sent in
In our routine we will first create our TRegistry object, then we will set the TRegistry.RootKey. Next we will check to see if the CDROM registry key exists before trying to open it. After opening the CDROM registry key we will add AutoRun to it according to the boolean parameter sent into the procedure
TRegistry.KeyExists returns TRUE is the specified registry key exists. The only parameter is a string to the key in the registry. If we set TRegistry.RootKey then we do not have to specify it within the string parameter
TRegistry.OpenKey returns TRUE if the specified registry key opens successfully. The first parameter is a string to the key in the registry. As mentioned above, if we set TRegistry.RootKey then we do not have to specify it within the string parameter. The second parameter is a boolean value telling Windows whether or not to create the key if it does not exist, we will set this to FALSE
TRegistry.WriteBinaryData stores binary data to the registry. The first parameter is the name to stoe under the open registry key. The second parameter is the value to store. The last parameter is the size of the binary value we are storing


{...}

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure SetCDAutoRun(AAutoRun: Boolean);
  public
    { Public declarations }
  end;

  {...}

procedure TForm1.Button1Click(Sender: TObject);
begin
  SetCDAutoRun(FALSE);
end;

procedure TForm1.SetCDAutoRun(AAutoRun: Boolean);
const
  DoAutoRun: array[Boolean] of Integer = (0, 1);
var
  Reg: TRegistry;
begin
  try
    { create our registry object }
    Reg := TRegistry.Create;
    { set our registry root key }
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    { verify that our CDROM class key exists }
    if Reg.KeyExists('System\CurrentControlSet\Services\Class\CDROM') then
      { try to open our CDROM class key }
      if Reg.OpenKey('System\CurrentControlSet\Services\Class\CDROM', FALSE) then
        { add AutoRun to our CDROM class key }
        Reg.WriteBinaryData('AutoRun', DoAutoRun[AAutoRun], 1);
  finally
    { free our registry object }
    Reg.Free;
  end;
  { showmessage that the changes will happen on reboot }
  ShowMessage('Your settings will take effect on the next reboot of Windows.');
end;

{...}

2010. április 6., kedd

How to minimize and restore all open applications


Problem/Question/Abstract:

How to minimize and restore all open applications

Answer:

procedure MinimizeAll;
begin
  { [Window key] + 'M' minimizes all windows, [Win][Shift] + 'M' restores them }
  keybd_event(VK_LWIN, MapvirtualKey(VK_LWIN, 0), 0, 0);
  keybd_event(Ord('M'), MapvirtualKey(Ord('M'), 0), 0, 0);
  keybd_event(Ord('M'), MapvirtualKey(Ord('M'), 0), KEYEVENTF_KEYUP, 0);
  keybd_event(VK_LWIN, MapvirtualKey(VK_LWIN, 0), KEYEVENTF_KEYUP, 0);
end;

procedure UnMinimizeAll;
begin
  { [Window key] + 'M' minimizes all windows, [Win][Shift] + 'M' restores them }
  keybd_event(VK_LWIN, MapvirtualKey(VK_LWIN, 0), 0, 0);
  keybd_event(VK_SHIFT, MapvirtualKey(VK_SHIFT, 0), 0, 0);
  keybd_event(Ord('M'), MapvirtualKey(Ord('M'), 0), 0, 0);
  keybd_event(Ord('M'), MapvirtualKey(Ord('M'), 0), KEYEVENTF_KEYUP, 0);
  keybd_event(VK_SHIFT, MapvirtualKey(VK_SHIFT, 0), KEYEVENTF_KEYUP, 0);
  keybd_event(VK_LWIN, MapvirtualKey(VK_LWIN, 0), KEYEVENTF_KEYUP, 0);
end;

2010. április 5., hétfő

"ntdll.DbgUserBreakPoint" during debug startup


Problem/Question/Abstract:

Whenever I start a program from the IDE with debugging enabled, the CPU window pops up and it reports something like ntdll.DbgUserBreakPoint - but I do not have any breakpoints set in my project.

Answer:

This is debug code in NTDLL.DLL itself. It gets executed if you have more than 16 colors for the application icon. You can help yourself by changing the number of colors for icons (Control Panel, Display Properties).

Microsoft must have hard-coded a debug breakpoint (int 3) in ntdll.dll to trace a bug and then they forgot to take it out for final build of Windows NT.

2010. április 4., vasárnap

Insert a row in a TStringGrid at a certain position


Problem/Question/Abstract:

Is there a method to insert a row into a StringGrid or do I have to shuffle all higher rows up 1?

Answer:

TCustomGrid has a very useful but unfortunately protected method named MoveRow. You can get at it using the cracker class strategy:

type
  TGridCracker = class(TCustomGrid);

{insert a line at row 5}
stringgrid1.rowcount := stringgrid1.rowcount + 1;
TGridCracker(stringgrid1).Moverow(stringgrid1.rowcount - 1, 5);

You first add a new empty line, then move it to the required position. This automatically moves all following lines one position down.

2010. április 3., szombat

Several color rows in TStringGrid


Problem/Question/Abstract:

Show several color in the rows of a TStringGrid.

Answer:

Using the OnDrawCell event...

Put this code in the event OnDrawCell of the StringGrid:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
begin
  with (Sender as TStringGrid) do begin
    if (Arow And 1)=1 then Canvas.Font.Color := clRed   { En rojo / in red }
                      else Canvas.Font.Color := clBlue; { En Azul / in Blue }

    Canvas.TextRect( Rect,
                     Rect.Left + 2,
                     Rect.Top + 2,
                     Cells[Acol,Arow]);
  end;
end;

This example shows the even lines with a color and the odd with other...

2010. április 2., péntek

How to convert a word wrap in a TMemo to a CR/LF


Problem/Question/Abstract:

Using a TMemo, is it possible to convert each wrap to a CR/LF, or even at a specific column (like 67 or 80)?

Answer:

const
  limit = 67;
var
  S: string;
  i: Integer;
begin
  S := Memo1.Text;
  Memo1.Clear;
  repeat
    i := limit;
    while S[i] <> ' ' do
      Dec(i);
    Memo1.Lines.Add(Trim(Copy(S, 1, i)));
    Delete(S, 1, i);
  until
    Length(S) = 0;
end;

2010. április 1., csütörtök

AdjustTokenPrivileges function to enable a privilege (NT)


Problem/Question/Abstract:

For some functions you need to get the right privileges on a Windows NT machine. (e.g: To shut down or restart windows with ExitWindowsEx or to change the system time)

Answer:

The following code provides a procedure to adjust the privileges. The AdjustTokenPrivileges() function enables or disables privileges in the specified access token.

// NT Defined Privileges from winnt.h

const
  SE_CREATE_TOKEN_NAME = 'SeCreateTokenPrivilege';
  SE_ASSIGNPRIMARYTOKEN_NAME = 'SeAssignPrimaryTokenPrivilege';
  SE_LOCK_MEMORY_NAME = 'SeLockMemoryPrivilege';
  SE_INCREASE_QUOTA_NAME = 'SeIncreaseQuotaPrivilege';
  SE_UNSOLICITED_INPUT_NAME = 'SeUnsolicitedInputPrivilege';
  SE_MACHINE_ACCOUNT_NAME = 'SeMachineAccountPrivilege';
  SE_TCB_NAME = 'SeTcbPrivilege';
  SE_SECURITY_NAME = 'SeSecurityPrivilege';
  SE_TAKE_OWNERSHIP_NAME = 'SeTakeOwnershipPrivilege';
  SE_LOAD_DRIVER_NAME = 'SeLoadDriverPrivilege';
  SE_SYSTEM_PROFILE_NAME = 'SeSystemProfilePrivilege';
  SE_SYSTEMTIME_NAME = 'SeSystemtimePrivilege';
  SE_PROF_SINGLE_PROCESS_NAME = 'SeProfileSingleProcessPrivilege';
  SE_INC_BASE_PRIORITY_NAME = 'SeIncreaseBasePriorityPrivilege';
  SE_CREATE_PAGEFILE_NAME = 'SeCreatePagefilePrivilege';
  SE_CREATE_PERMANENT_NAME = 'SeCreatePermanentPrivilege';
  SE_BACKUP_NAME = 'SeBackupPrivilege';
  SE_RESTORE_NAME = 'SeRestorePrivilege';
  SE_SHUTDOWN_NAME = 'SeShutdownPrivilege';
  SE_DEBUG_NAME = 'SeDebugPrivilege';
  SE_AUDIT_NAME = 'SeAuditPrivilege';
  SE_SYSTEM_ENVIRONMENT_NAME = 'SeSystemEnvironmentPrivilege';
  SE_CHANGE_NOTIFY_NAME = 'SeChangeNotifyPrivilege';
  SE_REMOTE_SHUTDOWN_NAME = 'SeRemoteShutdownPrivilege';
  SE_UNDOCK_NAME = 'SeUndockPrivilege';
  SE_SYNC_AGENT_NAME = 'SeSyncAgentPrivilege';
  SE_ENABLE_DELEGATION_NAME = 'SeEnableDelegationPrivilege';
  SE_MANAGE_VOLUME_NAME = 'SeManageVolumePrivilege';

  // Enables or disables privileges depending on the bEnabled value

function NTSetPrivilege(sPrivilege: string; bEnabled: Boolean): Boolean;
var
  hToken: THandle;
  TokenPriv: TOKEN_PRIVILEGES;
  PrevTokenPriv: TOKEN_PRIVILEGES;
  ReturnLength: Cardinal;
begin
  Result := True;
  // Only for Windows NT/2000/XP and later.
  if not (Win32Platform = VER_PLATFORM_WIN32_NT) then
    Exit;
  Result := False;

  // obtain the processes token
  if OpenProcessToken(GetCurrentProcess(),
    TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then
  begin
    try
      // Get the locally unique identifier (LUID) .
      if LookupPrivilegeValue(nil, PChar(sPrivilege),
        TokenPriv.Privileges[0].Luid) then
      begin
        TokenPriv.PrivilegeCount := 1; // one privilege to set

        case bEnabled of
          True: TokenPriv.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
          False: TokenPriv.Privileges[0].Attributes := 0;
        end;

        ReturnLength := 0; // replaces a var parameter
        PrevTokenPriv := TokenPriv;

        // enable or disable the privilege

        AdjustTokenPrivileges(hToken, False, TokenPriv, SizeOf(PrevTokenPriv),
          PrevTokenPriv, ReturnLength);
      end;
    finally
      CloseHandle(hToken);
    end;
  end;
  // test the return value of AdjustTokenPrivileges.
  Result := GetLastError = ERROR_SUCCESS;
  if not Result then
    raise Exception.Create(SysErrorMessage(GetLastError));
end;