FAQ VCL
Windows

:: Меню ::
:: На главную ::
:: FAQ ::
:: Заметки ::
:: Практика ::
:: Win API ::
:: Проекты ::
:: Скачать ::
:: Секреты ::
:: Ссылки ::

:: Сервис ::
:: Написать ::

:: MVP ::

:: RSS ::

Яндекс.Метрика

Как изменить системный разделитель целой части числа от дробной?

procedure SetDecimalSeparator(Ch: Char);
var
  DefLCID: LCID;
  Buffer: LPSTR;
begin
   Application.UpdateFormatSettings := True;
   StrPCopy(Buffer, Ch);
   DefLCID := GetThreadLocale;
   if SetLocaleInfo(DefLCID, LOCALE_SDECIMAL, Buffer) then
      DecimalSeparator := StrPas(Buffer)[1];
   Application.UpdateFormatSettings := False;
end;

// Или немного иначе
// procedure SetDecimalSeparator(Ch: TDecimalSeparator);
// var
//   DefLCID: LCID;
//   Buffer: LPTSTR;
//   OldUFS: Boolean;
// begin
//    OldUFS := Application.UpdateFormatSettings;
//    if not OldUFS then
//       Application.UpdateFormatSettings := True;
//  
//    GetMem(Buffer, 1);
//    StrPCopy(Buffer, Ch[1]);
//    DefLCID := GetThreadLocale;
//    if SetLocaleInfo(DefLCID, LOCALE_SDECIMAL, Buffer) then
//       FormatSettings.DecimalSeparator := StrPas(Buffer)[1];
//    FreeMem(Buffer);
//  
//    if not OldUFS then
//       Application.UpdateFormatSettings := False;
// end;

procedure TForm1.Button2Click(Sender: TObject);
begin
   SetDecimalSeparator('.');
   SendNotifyMessage(HWND_BROADCAST, WM_WININICHANGE, SPI_SETNONCLIENTMETRICS, 0);
end;


Как сохранить ScreenShot рабочего стола в файл?

// Способ первый
procedure GetWindowsScreen( PathToSave: string );
begin
   with TBitmap.Create do
   begin
      Width := Screen.Width;
      Height := Screen.Height;
      BitBlt( Canvas.Handle, 0, 0, Width, Height, GetDC( GetDesktopWindow ), 0, 0, SRCCOPY );
      SaveToFile( PathToSave );
      Free;
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   GetWindowsScreen( 'C:\Screen.bmp' );
end;

// Способ второй
procedure GetScreenShot;
var
  DC: HDC;
  Bmp: TBitmap;
  C: TCanvas;
begin
   DC := GetDC( 0 );
   if DC <= 0 then
   begin
      ShowMessage( 'Invalid DC!!!' );
      Exit;
   end;
   Bmp := TBitmap.Create;
   C := TCanvas.Create;
   try
      C.Handle := DC;
      Bmp.Width := Screen.Width;
      Bmp.Height := Screen.Height;
      Bmp.Canvas.CopyRect( Screen.DesktopRect, C, Screen.DesktopRect );
      Bmp.SaveToFile( 'c:\shot.bmp' );
   finally
      Bmp.Free;
      C.Free;
   end;
end;

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

// Способ третий
// Константа CAPTUREBLT позволяет включить полупрозрачные окна в скриншот.
// Эта константа не определена в модулях Delphi. Использовать ее можно
// только в системах Windows 98/Me, Windows 2000/XP и более новых.
procedure CaptureScreen( ABitmap: TBitmap );
const
  CAPTUREBLT = $40000000;
var
  hdcScreen: HDC;
  hdcCompatible: HDC;
  hbmScreen: HBITMAP;
begin
   // Create a normal DC and a memory DC for the entire screen. The
   // normal DC provides a "snapshot" of the screen contents. The
   // memory DC keeps a copy of this "snapshot" in the associated
   // bitmap.

   hdcScreen := CreateDC( 'DISPLAY', nil, nil, nil );
   hdcCompatible := CreateCompatibleDC( hdcScreen );
   // Create a compatible bitmap for hdcScreen.

   hbmScreen := CreateCompatibleBitmap( hdcScreen,
                                        GetDeviceCaps( hdcScreen, HORZRES ),
                                        GetDeviceCaps( hdcScreen, VERTRES ) );

   // Select the bitmaps into the compatible DC.
   SelectObject( hdcCompatible, hbmScreen );
   ABitmap.Handle := hbmScreen;
   BitBlt( hdcCompatible, 0, 0, ABitmap.Width, ABitmap.Height,
           hdcScreen, 0, 0, SRCCOPY or CAPTUREBLT );

   DeleteDC( hdcScreen );
   DeleteDC( hdcCompatible );
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Bmp: TBitmap;
begin
   Bmp := TBitmap.Create;
   CaptureScreen( Bmp );
   Bmp.SaveToFile( 'c:\shot.bmp' );
   Bmp.Free;
end;


Как определить, что пользователь изменил схему оформления XP?

const
   WM_THEMECHANGED = $031A;

type
  TForm1 = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
    procedure WMTHEMECHANGED( var Msg: TMessage ); message WM_THEMECHANGED;
  end;

{...}

implementation

{...}

procedure TForm1.WMTHEMECHANGED(var Msg: TMessage);
begin
   Caption := 'Theme changed';
   Msg.Result := 0;
end;


Как получить информацию об используемой схеме оформления Windows?

uses
   {...,} ComObj, SyncObjs;

var
  GetCurrentThemeName: function( pszThemeFileName: LPWSTR; cchMaxNameChars: Integer;
                                 pszColorBuff: LPWSTR; cchMaxColorChars: Integer;
                                 pszSizeBuff: LPWSTR; cchMaxSizeChars: Integer ): HRESULT; stdcall;

{...}

implementation

{...}

procedure TForm1.Button1Click(Sender: TObject);
var
  FileName, ColorScheme, SizeName: WideString;
  hThemeLib: THandle;
begin
   try
      hThemeLib := LoadLibrary( 'uxtheme.dll' );
      if hThemeLib > 0 then
         GetCurrentThemeName := GetProcAddress( hThemeLib, 'GetCurrentThemeName' );
      if Assigned( GetCurrentThemeName ) then
      begin
         SetLength( FileName, 255 );
         SetLength( ColorScheme, 255 );
         SetLength( SizeName, 255 );
         OleCheck( GetCurrentThemeName( PWideChar( FileName), 255,
                                        PWideChar( ColorScheme ), 255,
                                        PWideChar( SizeName ), 255 ) );
         ShowMessage( PWideChar( FileName ) );
         ShowMessage( PWideChar( ColorScheme ) );
         ShowMessage( PWideChar( SizeName ) );
      end;
   finally
      FreeLibrary( hThemeLib );
   end;
end;


Как программно вызвать режим Flip 3D в Windows Vista и выше?

// Способ первый
// Windows Aero должен быть включен
procedure TForm1.Button1Click(Sender: TObject);
const
  APPCOMMAND_DWM_FLIP3D = $360000;
begin
   SendMessage( Handle, WM_APPCOMMAND, Handle, APPCOMMAND_DWM_FLIP3D );
end;

// Способ второй
// Windows Aero должен быть включен
{...,} ShellAPI;

procedure TForm1.Button1Click(Sender: TObject);
begin
   ShellExecute( Handle, 'open', 'shell:::{3080F90E-D7AD-11D9-BD98-0000947B0257}', nil, nil, SW_SHOW );
end;

// Или немного иначе
{...,} shellAPI;

procedure TForm1.Button1Click(Sender: TObject);
var
  lpExecInfo: PShellExecuteInfoW;
begin
   New( lpExecInfo );
   lpExecInfo^.cbSize := SizeOf( TShellExecuteInfoW );
   lpExecInfo^.lpFile := 'shell:::{3080F90E-D7AD-11D9-BD98-0000947B0257}';
   lpExecInfo^.nShow := SW_NORMAL;
   ShellExecuteEx( lpExecInfo );
   Dispose( lpExecInfo );
end;


Как получить идентификатор безопасности (SID) учетной записи?

// Способ первый
type
  PTokenUser = ^TTokenUser;
  TTokenUser = packed record
    User: TSidAndAttributes;
  end;

const
  HEAP_ZERO_MEMORY = $00000008;
  SID_REVISION = 1; // Current revision level

{...}

implementation

{...}

function ConvertSid( Sid: PSID; pszSidText: PChar; var dwBufferLen: DWORD ): BOOL;
var
  psia: PSIDIdentifierAuthority;
  dwSubAuthorities: DWORD;
  dwSidRev: DWORD;
  dwCounter: DWORD;
  dwSidSize: DWORD;
begin
   Result := false;
   dwSidRev := SID_REVISION;
   if not IsValidSid( Sid ) then Exit;
   psia := GetSidIdentifierAuthority( Sid );
   dwSubAuthorities := GetSidSubAuthorityCount( Sid )^;
   dwSidSize := ( 15 + 12 + ( 12 * dwSubAuthorities ) + 1 ) * SizeOf( Char );

   if ( dwBufferLen < dwSidSize ) then
   begin
      dwBufferLen := dwSidSize;
      SetLastError( ERROR_INSUFFICIENT_BUFFER );
      Exit;
   end;

   StrFmt( pszSidText, 'S-%u-', [dwSidRev] );

   if ( psia.Value[0] <> 0 ) or ( psia.Value[1] <> 0 ) then
      StrFmt( pszSidText + StrLen( pszSidText ),
              '0x%.2x%.2x%.2x%.2x%.2x%.2x',
              [psia.Value[0], psia.Value[1], psia.Value[2],
              psia.Value[3], psia.Value[4], psia.Value[5]] )
   else
      StrFmt( pszSidText + StrLen( pszSidText ), '%u', [DWORD( psia.Value[5] ) +
              DWORD( psia.Value[4] shl 8 ) + DWORD( psia.Value[3] shl 16 ) +
              DWORD(psia.Value[2] shl 24)] );

   dwSidSize := StrLen(pszSidText);

   for dwCounter := 0 to dwSubAuthorities-1 do
   begin
      StrFmt( pszSidText + dwSidSize, '-%u', [GetSidSubAuthority( Sid, dwCounter )^] );
      dwSidSize := StrLen( pszSidText );
   end;

   Result := true;
end;

function ObtainTextSid( hToken: THandle; pszSid: PChar;
  var dwBufferLen: DWORD ): BOOL;
var
  dwReturnLength: DWORD;
  dwTokenUserLength: DWORD;
  tic: TTokenInformationClass;
  ptu: Pointer;
begin
   Result := false;
   dwReturnLength := 0;
   dwTokenUserLength := 0;
   tic := TokenUser;
   ptu := nil;

   if not GetTokenInformation( hToken, tic, ptu, dwTokenUserLength, dwReturnLength ) then
   begin
      if GetLastError = ERROR_INSUFFICIENT_BUFFER then
      begin
         ptu := HeapAlloc( GetProcessHeap, HEAP_ZERO_MEMORY, dwReturnLength );
         if ptu = nil then Exit;
         dwTokenUserLength := dwReturnLength;
         dwReturnLength    := 0;

         if not GetTokenInformation( hToken, tic, ptu, dwTokenUserLength, dwReturnLength ) then Exit;
      end
      else
         Exit;
   end;

   if not ConvertSid( ( PTokenUser( ptu ).User ).Sid, pszSid, dwBufferLen ) then Exit;
   if not HeapFree( GetProcessHeap, 0, ptu ) then Exit;
   Result := true;
end;

function GetCurrentUserSid: string;
var
  hAccessToken: THandle;
  bSuccess: BOOL;
  dwBufferLen: DWORD;
  szSid: array[0..260] of Char;
begin
   Result := '';

   bSuccess := OpenThreadToken( GetCurrentThread, TOKEN_QUERY, true, hAccessToken );
   if not bSuccess then
   begin
      if GetLastError = ERROR_NO_TOKEN then
         bSuccess := OpenProcessToken( GetCurrentProcess, TOKEN_QUERY, hAccessToken );
   end;
   if bSuccess then
   begin
      ZeroMemory( @szSid, SizeOf( szSid ) );
      dwBufferLen := SizeOf( szSid );

      if ObtainTextSid( hAccessToken, szSid, dwBufferLen ) then
         Result := szSid;
      CloseHandle( hAccessToken );
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   ShowMessage( GetCurrentUserSid );
end;

// Способ второй
// Получение имени пользователя
function GettingUserName: string;
var
  Size: cardinal;
  pStr: PChar;
  Res: boolean;
begin
   pStr := nil;
   Size := MAX_COMPUTERNAME_LENGTH + 1;
   try
      pStr := StrAlloc( Size );
      Res := GetUserName( pStr, Size );
      if Res then
         Result := StrPas( pStr )
      else
         Result := '';
   finally
      if pStr <> nil then StrDispose( pStr );
   end;
end;

// Получение имени компьютера
function GettingComputerName: string;
var
  Size: cardinal;
  pStr: PChar;
  Res: boolean;
begin
   pStr := nil;
   Size := MAX_COMPUTERNAME_LENGTH + 1;
   try
      pStr := StrAlloc( Size );
      Res := GetComputerName( pStr, Size );
      if Res then
         Result := StrPas( pStr )
      else
         Result := '';
   finally
      if pStr <> nil then StrDispose( pStr );
   end;
end;

function ConvertSIDToString( ASID: Pointer ): string;
var
  i: integer;
  SIDAuth: PSIDIdentifierAuthority;
  SIDSubAuth: Cardinal;
  SIDSubAuthCount: Byte;
begin
   Result := 'S-1-';
   SIDAuth := GetSidIdentifierAuthority( ASID );
   for i := 0 to 5 do
      if SIDAuth.Value[i] <> 0 then
         Result := Result+IntToStr( SIDAuth.Value[i] );
   SIDSubAuthCount := GetSidSubAuthorityCount( ASID )^;
   for i := 0 to SIDSubAuthCount-1 do
   begin
      SIDSubAuth := GetSidSubAuthority( ASID, i )^;
      Result := Result + '-' + IntToStr( SIDSubAuth );
   end;
end;

function GetSIDFromAccount( AMachine, AName: string ): string;
var
  SID: Pointer;
  szDomain: PChar;
  cbDomain, cbSID: Cardinal;
  SidLen, SubAuthCount, NameUse: Cardinal;
  Domain: string;
begin
   Result := '';
   cbDomain := 0;
   cbSID := 0;
   LookupAccountName( PChar( AMachine ), PChar( AName ), SID, cbSID,
                      szDomain, cbDomain, NameUse );
   szDomain := StrAlloc( cbDomain );
   SID := AllocMem( cbSID );
   if LookupAccountName( PChar( AMachine ), PChar( AName ), SID, cbSID,
                         szDomain, cbDomain, NameUse ) then
   begin
      Result := ConvertSIDToString( SID );
      SubAuthCount := GetSidSubAuthorityCount(SID)^;
      SidLen := GetLengthSid( SID );
      Domain := StrPas( szDomain );
   end;
   StrDispose( szDomain );
   Freemem( SID );
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   ShowMessage( GetSIDFromAccount( GettingComputerName, GettingUserName ) );
end;

// Способ третий
function ConvertSidToStringSid( Sid: PSID; var StringSid: PChar ): BOOL;
  stdcall; external advapi32 name 'ConvertSidToStringSidW';

function GetUserSIDStr( SystemName, AccountName: string ): string;
var
  PSID, PRef: Pointer;
  SIDSize, RefSize, peUse: Cardinal;
  sSID: PChar;
begin
   Result := '';
   SIDSize := 0;
   RefSize := 0;
   // Первый вызов функции позволяет получить необходимые размеры буферов
   // для SID и имени домена
   LookupAccountName( PChar( SystemName ), PChar( AccountName ), nil,
                      SIDSize, nil, RefSize, peUse );
   GetMem( PSID, SIDSize );
   GetMem( PRef, RefSize );
   try
      // Получаем SID учетной записи
      if not LookupAccountName( PChar( SystemName ), PChar( AccountName ), PSID,
                                SIDSize, PRef,RefSize,peUse ) then 
         RaiseLastOSError;
      // Конвертируем SID в строковое представление
      if ConvertSidToStringSid( PSID, sSID ) then
      begin
         SetLength( Result, StrLen( sSID ) );
         StrCopy(PChar(Result),sSID);
         LocalFree( Cardinal( sSID ) );
      end;
   finally
      FreeMem( PRef );
      FreeMem( PSID );
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   ShowMessage( GetUserSIDStr( '', 'system' ) );
end;


Как получить описатель (HWND) главного окна для текущей оболочки (shell) Windows?

// Способ первый
procedure TForm1.Button1Click(Sender: TObject);
begin
   ShowMessage( IntToStr( FindWindow( 'Progman', 'Program Manager' ) ) );
end;

// Способ второй
function GetShellWindow: HWND; stdcall; external 'user32.dll' name 'GetShellWindow';

implementation

procedure TForm1.Button1Click(Sender: TObject);
begin
   ShowMessage( IntToStr( GetShellWindow ) );
end;


Как в Windows XP запустить программу от имени системы (LOCAL SYSTEM)?

uses
  {...,} AccCtrl, AclAPI;

function GetOSVersion: Cardinal;
var
  OSVersionInfo: TOSVersionInfo;
begin
   Result := 0;
   FillChar( OSVersionInfo, SizeOf( OSVersionInfo ), 0 );
   OSVersionInfo.dwOSVersionInfoSize := SizeOf( OSVersionInfo );
   if GetVersionEx( OSVersionInfo ) then
   begin
      if OSVersionInfo.dwPlatformId = VER_PLATFORM_WIN32_NT then
      begin
         if OSVersionInfo.dwMajorVersion = 5 then
         begin
            if OSVersionInfo.dwMinorVersion = 0 then
               Result := 50
            else if OSVersionInfo.dwMinorVersion = 2 then
               Result := 52
            else if OSVersionInfo.dwMinorVersion = 1 then
               Result := 51
         end;
         if OSVersionInfo.dwMajorVersion = 6 then
         begin
            if OSVersionInfo.dwMinorVersion = 0 then
               Result := 60
            else if OSVersionInfo.dwMinorVersion = 1 then
              Result := 61;
         end;
      end;
   end;
end;

function RunAsSystem( ApplicationName: String ): Boolean;
var
  lpStartupInfo: TStartupInfo;
  lpProcessInformation: TProcessInformation;
  ppSecurityDescriptor: PPSecurity_Descriptor;
  ppDacl: PACL;
  hProcess, hToken: Cardinal;
begin
   Result := false;
   if ( GetOSVersion > 50 ) and ( GetOSVersion < 60 ) then
      hProcess:= OpenProcess( PROCESS_QUERY_INFORMATION, false, 4 )
   else
      hProcess:= OpenProcess( PROCESS_QUERY_INFORMATION, false, 8 );
   if hProcess <> 0 then
   begin
      try
         OpenProcessToken( hProcess, MAXIMUM_ALLOWED, hToken );
         if hToken <> 0 then
         begin
            if GetSecurityInfo( hToken, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, nil, nil, @ppDacl, nil, ppSecurityDescriptor ) = ERROR_SUCCESS then
            begin
               if SetSecurityInfo( hToken, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, nil, nil, nil, nil ) = ERROR_SUCCESS then
               begin
                  CloseHandle( hToken );
                  OpenProcessToken( hProcess, MAXIMUM_ALLOWED, hToken );
                  if hToken <> 0 then
                  begin
                     try
                        if ImpersonateLoggedOnUser( hToken ) then
                        begin
                           ZeroMemory( @lpStartupInfo, SizeOf( lpStartupInfo ) );
                           lpStartupInfo.cb:= SizeOf( lpStartupInfo );
                           if CreateProcessAsUser( hToken, PChar( ApplicationName ), '', nil, nil, false, CREATE_DEFAULT_ERROR_MODE, nil, nil, lpStartupInfo, lpProcessInformation ) then
                              Result := true;
                           RevertToSelf;
                        end;
                        SetSecurityInfo( hToken, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, nil, nil, ppDacl, nil );
                     finally
                        CloseHandle( hToken );
                     end;
                  end;
               end;
            end;
         end;
      finally
         CloseHandle( hProcess );
      end;
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   RunAsSystem( Edit1.Text );
end;


Как получить дату установки Windows?

// Способ первый
uses
  Registry;

procedure TForm1.Button1Click(Sender: TObject);
var
  Reg: TRegistry;
  ZoneName, tmp: string;
  UTC: Integer;
  InstallDate: DWORD;
  _date, _time: DWORD;
  t: TDateTime;
begin
   Reg := TRegistry.Create(KEY_READ or KEY_QUERY_VALUE or KEY_WOW64_64KEY);
   Reg.RootKey := HKEY_LOCAL_MACHINE;

   // Получение часового пояса
   if Reg.OpenKeyReadOnly( 'SYSTEM\CurrentControlSet\Control\TimeZoneInformation' ) then
   begin
      ZoneName := Reg.ReadString( 'TimeZoneKeyName' );
      Reg.CloseKey;
      if Reg.OpenKeyReadOnly( 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\' + ZoneName ) then
      begin
         tmp := Reg.ReadString( 'Display' );
         UTC := StrToInt( Copy( tmp, 5, 3 ) ); // Тут лучше использовать регулярное выражение
         Reg.CloseKey;
      end;
   end;

   // Определение даты установки Windows
   if Reg.OpenKeyReadOnly( 'SOFTWARE\Microsoft\Windows NT\CurrentVersion' ) then
   begin
      InstallDate := Reg.ReadInteger( 'InstallDate' );
      _date := InstallDate div 86400;
      _time := InstallDate mod 86400;
      t := {дата} EncodeDate( 1970, 1, 1 ) + _date +
          {время} EncodeTime( _time div 3600 + UTC, ( _time div 60 ) mod 60, _time mod 60, 0 );
      ShowMessage( DateTimeToStr( t ) );
   end;

   Reg.CloseKey;
   Reg.Free;
end;

// Способ второй
uses
  DateUtils;

function GetWindowsSetupDate: string;
const
  regpath = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion';
  ValueName = 'InstallDate';
var
  hSubKey: HKEY;
  BufSize: DWORD;
  Buf: DWORD;
  MyValType: DWORD;
begin
   if RegOpenKey(HKEY_LOCAL_MACHINE, regpath, hSubKey) = ERROR_SUCCESS then
      if RegQueryValueEx(hSubKey, ValueName, nil, @MyValType, nil, @BufSize) = ERROR_SUCCESS then
      begin
         if RegQueryValueEx(hSubKey, ValueName, nil, @MyValType, @Buf, @BufSize) = ERROR_SUCCESS then
            Result := DateTimeToStr(UnixToDateTime(Buf));
         // Еще нужно добавить смещение (UTC) от часового пояся (см. первый способ)
      end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   ShowMessage(GetWindowsSetupDate);
end;


Как запустить проводник Windows с окном поиска?

// Пример демонстрирует использование DDE, чтобы запустить диалоговое окно Проводника Windows для поиска файлов.
// Пример открывает поиск файлов в каталоге C:\Temp:

uses
  {...,} DdeMan;

procedure TForm1.Button1Click(Sender: TObject);
begin
   with TDDEClientConv.Create( Self ) do
   begin
      ConnectMode := ddeManual;
      ServiceApplication := 'explorer.exe';
      SetLink( 'Folders', 'AppProperties' ) ;
      OpenLink;
      ExecuteMacro( '[FindFolder(, C:\Temp)]', false );
      CloseLink;
      Free;
   end;
end;

При использовании материала - ссылка на сайт обязательна