Получить серийный номер диска

function GetHardDiskSerial(const DriveLetter: Char): string;

var

NotUsed: DWORD;

VolumeFlags: DWORD;

VolumeInfo: array[0..MAX_PATH] of Char;

VolumeSerialNumber: DWORD;

begin

GetVolumeInformation(PChar(DriveLetter ':\'),

nil, SizeOf(VolumeInfo), @VolumeSerialNumber, NotUsed,

VolumeFlags, nil, 0);

Result := Format('Label = %s VolSer = %8.8X',

[VolumeInfo, VolumeSerialNumber])

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

ShowMessage(GetHardDiskSerial('c'));

end;

Получить объем диска и его свободного места

{

* Place a Button1 and DriveComboBox1 on your form.

* The function "SetCurrentDir" well be true if the disk in drive

* The procedure "GetDiskFreeSpaceEx" returns the free and total disk size

}


uses

SysUtils;

implementation

function GetDiskSize(drive: Char; var free_size, total_size: Int64): Boolean;

var

RootPath: array[0..4] of Char;

RootPtr: PChar;

current_dir: string;

begin

RootPath[0] := Drive;

RootPath[1] := ':';

RootPath[2] := '\';

RootPath[3] := #0;

RootPtr := RootPath;

current_dir := GetCurrentDir;

if SetCurrentDir(drive ':\') then

begin

GetDiskFreeSpaceEx(RootPtr, Free_size, Total_size, nil);

// this to turn back to original dir

SetCurrentDir(current_dir);

Result := True;

end

else

begin

Result := False;

Free_size := -1;

Total_size := -1;

end;

end;

procedure TForm1.Button1Click(Sender: TObject);

var

free_size, total_size: Int64;

begin

if GetDiskSize(DriveComboBox1.Drive, free_size, total_size) then

ShowMessage('free space ='

IntToStr(free_size) #13 'total size='

IntToStr(total_size))

else

ShowMessage('No disk in drive!');

end;

Получение информации о диске

function GetVolumeInfoFVS(const Dir:string;

var FileSystemName,VolumeName:string;var Serial:longint):boolean;

{Получение информации о диске

Dir - каталог или буква требуемого диска

FileSystemName - название файловой системы

VolumeName - метка диска

Serial - серийный номер диска

В случае ошибки функция возвращает false}


var

root:pchar;

res:longbool;

VolumeNameBuffer,FileSystemNameBuffer:pchar;

VolumeNameSize,FileSystemNameSize:DWord;

VolumeSerialNumber,MaximumComponentLength,FileSystemFlags:DWORD;

s:string;

n:integer;

begin

n:=pos(':',Dir);

if n> 0 then s:=copy(Dir,1,n 1) else s:=s ':';

if s[length(s)]=':' then s:=s '\';

root:=pchar(s);

getMem(VolumeNameBuffer,256);

getMem(FileSystemNameBuffer,256);

VolumeNameSize:=255;

FileSystemNameSize:=255;

res:=GetVolumeInformation(Root,VolumeNameBuffer,VolumeNameSize

,@VolumeSerialNumber,

MaximumComponentLength, FileSystemFlags

,FileSystemNameBuffer,FileSystemNameSize);

Result:=res;

VolumeName:=VolumeNameBuffer;

FileSystemName:=FileSystemNameBuffer;

Serial:=VolumeSerialNumber;

freeMem(VolumeNameBuffer,256);

freeMem(FileSystemNameBuffer,256);

end;

Получение идентификатора диска

Как получить идентификатор находящегося в CD-ROM’е аудио-компакта?

const

MCI_INFO_PRODUCT = $00000100;

MCI_INFO_FILE = $00000200;

MCI_INFO_MEDIA_UPC = $00000400;

MCI_INFO_MEDIA_IDENTITY = $00000800;

MCI_INFO_NAME = $00001000;

MCI_INFO_COPYRIGHT = $00002000;

{ блок параметров для командного сообщения MCI_INFO }

type

PMCI_Info_ParmsA = ^TMCI_Info_ParmsA;

PMCI_Info_ParmsW = ^TMCI_Info_ParmsW;

PMCI_Info_Parms = PMCI_Info_ParmsA;

TMCI_Info_ParmsA = record

dwCallback: DWORD;

lpstrReturn: PAnsiChar;

dwRetSize: DWORD;

end;

TMCI_Info_ParmsW = record

dwCallback: DWORD;

lpstrReturn: PWideChar;

dwRetSize: DWORD;

end;

TMCI_Info_Parms = TMCI_Info_ParmsA;

Идентификатор возвращается функцией MCI_INFO_MEDIA_IDENTITY в виде строки с десятичным числом. Для получения дополнительной информации обратитесь к электронной справке (Win32 и компонент TMediaPlayer).

Исправления

// метка диска

procedure GetDriveInfo(VolumeName: string;

var VolumeLabel, SerialNumber, FileSystem: string);

var

VolLabel, FileSysName: array[0..255] of char;

SerNum: pdword;

MaxCompLen, FileSysFlags: dword;

begin

New(SerNum);

GetVolumeInformation(PChar(VolumeName), VolLabel,

255, SerNum, MaxCompLen, FileSysFlags, FileSysName, 255);

VolumeLabel := VolLabel;

SerialNumber := Format('%x', [SerNum^]);

FileSystem := FileSysName;

Dispose(SerNum);

end;

// далее

var

VolLabel, SN, FileSystem, S: string;

begin

s := 'g:\'; // имя CD дисковода

GetDriveInfo(S, VolLabel, SN, FileSystem);

получаем:

VolLabel - 'ARMSTRONG' // метка диска

SN - B5FF77AD // номер серийный

FileSystem - CDFS // тип файловой системы

Работает не только для CD для всех типов дисков ... Далее:

// метка диска

procedure GetAllDrive(Sender: TObject);

var

i, mask: integer;

s: string;

begin

mask := GetLogicalDrives;

i := 0;

while mask 0 do

begin

s := chr(ord('a') i) ':\';

if (mask and 1) 0 then

case GetDriveType(PChar(s)) of

0: ListBox1.Items.Add(s ' unknown.');

1: ListBox1.Items.Add(s ' not exist.');

DRIVE_REMOVABLE: ListBox1.Items.Add(s ' removable.'); // floppy,zip

DRIVE_FIXED: ListBox1.Items.Add(s ' fixed.');

DRIVE_REMOTE: ListBox1.Items.Add(s ' network.');

DRIVE_CDROM: ListBox1.Items.Add(s ' CD-ROM.');

DRIVE_RAMDISK: ListBox1.Items.Add(s ' RAM.');

end;

inc(i);

mask := mask shr 1;

end;

end;

В ListBox1 получаем все диски на данном компьютере.

Поличение серийного номера IDE диска

Функция получает серийный номер первого физического диска IDE (не серийный номер тома!).

Используется S.M.A.R.T. API, а под Windows NT/2K/XP запрос производится не напрямую к диску,

а через miniport драйвер контроллера, что позволяет читать серийный номер не имея прав администратора.

Функция может не работать, если первый контролер в системе не ATA или если первое устройство

не является винчестером, который поддерживает SMART (современные винчестеры поддерживают).

Если Вы хотите получить другие параметры диска/других дисков, то смотрите пример IdeInfo2 с моего сайта.

На Windows 9x требует наличия драйвера smartvsd.vxd (должен быть в стандартной поставке),

просто скопируйте его в \windows\system\iosubsys и перезагрузите компьютер.

Зависимости: Windows, SysUtils

function GetIdeDiskSerialNumber: string;

type

TSrbIoControl = packed record

HeaderLength: ULONG;

Signature: array[0..7] of Char;

Timeout: ULONG;

ControlCode: ULONG;

ReturnCode: ULONG;

Length: ULONG;

end;

SRB_IO_CONTROL = TSrbIoControl;

PSrbIoControl = ^TSrbIoControl;

TIDERegs = packed record

bFeaturesReg: Byte; // Used for specifying SMART "commands".

bSectorCountReg: Byte; // IDE sector count register

bSectorNumberReg: Byte; // IDE sector number register

bCylLowReg: Byte; // IDE low order cylinder value

bCylHighReg: Byte; // IDE high order cylinder value

bDriveHeadReg: Byte; // IDE drive/head register

bCommandReg: Byte; // Actual IDE command.

bReserved: Byte; // reserved for future use. Must be zero.

end;

IDEREGS = TIDERegs;

PIDERegs = ^TIDERegs;

TSendCmdInParams = packed record

cBufferSize: DWORD; // Buffer size in bytes

irDriveRegs: TIDERegs; // Structure with drive register values.

bDriveNumber: Byte; // Physical drive number to send command to (0,1,2,3).

bReserved: array[0..2] of Byte; // Reserved for future expansion.

dwReserved: array[0..3] of DWORD; // For future use.

bBuffer: array[0..0] of Byte; // Input buffer.

end;

SENDCMDINPARAMS = TSendCmdInParams;

PSendCmdInParams = ^TSendCmdInParams;

TIdSector = packed record

wGenConfig: Word;

wNumCyls: Word;

wReserved: Word;

wNumHeads: Word;

wBytesPerTrack: Word;

wBytesPerSector: Word;

wSectorsPerTrack: Word;

wVendorUnique: array[0..2] of Word;

sSerialNumber: array[0..19] of Char;

wBufferType: Word;

wBufferSize: Word;

wECCSize: Word;

sFirmwareRev: array[0..7] of Char;

sModelNumber: array[0..39] of Char;

wMoreVendorUnique: Word;

wDoubleWordIO: Word;

wCapabilities: Word;

wReserved1: Word;

wPIOTiming: Word;

wDMATiming: Word;

wBS: Word;

wNumCurrentCyls: Word;

wNumCurrentHeads: Word;

wNumCurrentSectorsPerTrack: Word;

ulCurrentSectorCapacity: ULONG;

wMultSectorStuff: Word;

ulTotalAddressableSectors: ULONG;

wSingleWordDMA: Word;

wMultiWordDMA: Word;

bReserved: array[0..127] of Byte;

end;

PIdSector = ^TIdSector;

const

IDE_ID_FUNCTION = $EC;

IDENTIFY_BUFFER_SIZE = 512;

DFP_RECEIVE_DRIVE_DATA = $0007C088;

IOCTL_SCSI_MINIPORT = $0004D008;

IOCTL_SCSI_MINIPORT_IDENTIFY = $001B0501;

DataSize = sizeof(TSendCmdInParams) - 1 IDENTIFY_BUFFER_SIZE;

BufferSize = SizeOf(SRB_IO_CONTROL) DataSize;

W9xBufferSize = IDENTIFY_BUFFER_SIZE 16;

var

hDevice: THandle;

cbBytesReturned: DWORD;

pInData: PSendCmdInParams;

pOutData: Pointer; // PSendCmdInParams;

Buffer: array[0..BufferSize - 1] of Byte;

srbControl: TSrbIoControl absolute Buffer;

procedure ChangeByteOrder(var Data; Size: Integer);

var

ptr: PChar;

i: Integer;

c: Char;

begin

ptr := @Data;

for i := 0 to (Size shr 1) - 1 do

begin

c := ptr^;

ptr^ := (ptr 1)^;

(ptr 1)^ := c;

Inc(ptr, 2);

end;

end;

begin

Result := '';

FillChar(Buffer, BufferSize, #0);

if Win32Platform = VER_PLATFORM_WIN32_NT then

begin // Windows NT, Windows 2000

// Get SCSI port handle

hDevice := CreateFile('\\.\Scsi0:', GENERIC_READ or GENERIC_WRITE,

FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

if hDevice = INVALID_HANDLE_VALUE then

Exit;

try

srbControl.HeaderLength := SizeOf(SRB_IO_CONTROL);

System.Move('SCSIDISK', srbControl.Signature, 8);

srbControl.Timeout := 2;

srbControl.Length := DataSize;

srbControl.ControlCode := IOCTL_SCSI_MINIPORT_IDENTIFY;

pInData := PSendCmdInParams(PChar(@Buffer) SizeOf(SRB_IO_CONTROL));

pOutData := pInData;

with pInData^ do

begin

cBufferSize := IDENTIFY_BUFFER_SIZE;

bDriveNumber := 0;

with irDriveRegs do

begin

bFeaturesReg := 0;

bSectorCountReg := 1;

bSectorNumberReg := 1;

bCylLowReg := 0;

bCylHighReg := 0;

bDriveHeadReg := $A0;

bCommandReg := IDE_ID_FUNCTION;

end;

end;

if not DeviceIoControl(hDevice, IOCTL_SCSI_MINIPORT, @Buffer,

BufferSize, @Buffer, BufferSize, cbBytesReturned, nil) then

Exit;

finally

CloseHandle(hDevice);

end;

end

else

begin // Windows 95 OSR2, Windows 98

hDevice := CreateFile('\\.\SMARTVSD', 0, 0, nil, CREATE_NEW, 0, 0);

if hDevice = INVALID_HANDLE_VALUE then

Exit;

try

pInData := PSendCmdInParams(@Buffer);

pOutData := PChar(@pInData^.bBuffer);

with pInData^ do

begin

cBufferSize := IDENTIFY_BUFFER_SIZE;

bDriveNumber := 0;

with irDriveRegs do

begin

bFeaturesReg := 0;

bSectorCountReg := 1;

bSectorNumberReg := 1;

bCylLowReg := 0;

bCylHighReg := 0;

bDriveHeadReg := $A0;

bCommandReg := IDE_ID_FUNCTION;

end;

end;

if not DeviceIoControl(hDevice, DFP_RECEIVE_DRIVE_DATA, pInData,

SizeOf(TSendCmdInParams) - 1, pOutData, W9xBufferSize,

cbBytesReturned, nil) then

Exit;

finally

CloseHandle(hDevice);

end;

end;

with PIdSector(PChar(pOutData) 16)^ do

begin

ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));

SetString(Result, sSerialNumber, SizeOf(sSerialNumber));

end;

end;

Пример использования:

var

s: string;

rc: DWORD;

begin

s := GetIdeDiskSerialNumber;

if s = '' then

begin

rc := GetLastError;

if rc = 0 then

WriteLn('IDE drive is not support SMART feature')

else

WriteLn(SysErrorMessage(rc));

end

else

WriteLn('Disk serial number: ''', s, '''');

end.

Узнать текущую раскладку клавиатуры в любом активном окне

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Автор: KosilkA

WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****

>> Узнать текущую раскладку клавиатуры в любом активном окне

возвращает числовое значение соответственно установленной раскладке

Зависимости: windows

Автор: KosilkA, gloom@imail.ru, Koenigsberg

Copyright: delphi help и немного усердия 🙂

Дата: 4 декабря 2003 г.

***************************************************** }

if GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow, nil)) = 67699721 then

//раскладка английская

else

//раскладка НЕанглийская, например 68748313 соответствует русской

{/codecitation}

Список установленных раскладок клавиатуры

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Оформил: DeeCo

Автор: http://www.swissdelphicenter.ch

procedure GetKLList(List: TStrings);

var

AList : array [0..9] of Hkl;

AklName: array [0..255] of Char;

i: Longint;

begin

List.Clear;

for i := 0 to GetKeyboardLayoutList(SizeOf(AList), AList) — 1 do

begin

GetLocaleInfo(LoWord(AList[i]), LOCALE_SLANGUAGE, AklName, SizeOf(AklName));

List.AddObject(AklName, Pointer(AList[i]));

end;

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

GetKLList(ListBox1.Items);

end;

procedure TForm1.ListBox1Click(Sender: TObject);

begin

with Sender as TListBox do

ActivateKeyboardLayout(Hkl(Items.Objects[ItemIndex]), 0);

end;

{/codecitation}

Сменить язык

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

FIDO’шник решил блеснуть знанием английского языка. Заходит в пивную.

— Пива энд воблы. Ничего, что я по-англицки?

Эта программа при нажатии на Button1 меняет язык на следующий, при нажатии на Button2 – на русский, а на Button3 – на английский. Каждую секунду программа выводит в заголовок окна число, определяющее текущий язык.

procedure TForm1.Button1Click(Sender: TObject);

begin

ActivateKeyboardLayout(HKL_NEXT, 0);

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

ActivateKeyboardLayout(LoadKeyboardLayout(‘00000419’, 0), 0);

end;

procedure TForm1.Button3Click(Sender: TObject);

begin

ActivateKeyboardLayout(LoadKeyboardLayout(‘00000409’, 0), 0);

end;

procedure TForm1.Timer1Timer(Sender: TObject);

var

s: array [0..63] of char;

begin

GetKeyboardLayoutName(s);

Form1.Caption := s;

end;

{/codecitation}

Регионарные стандарты

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Автор: Vit

WEB-сайт: http://forum.vingrad.ru

В Дельфи есть предопределенные переменные языковых установок и форматов:

// SysUtils

var CurrencyString: string;

var CurrencyFormat: Byte;

var NegCurrFormat: Byte;

var ThousandSeparator: Char;

var DecimalSeparator: Char;

var CurrencyDecimals: Byte;

var DateSeparator: Char;

var ShortDateFormat: string;

var LongDateFormat: string;

var TimeSeparator: Char;

var TimeAMString: string;

var TimePMString: string;

var ShortTimeFormat: string;

var LongTimeFormat: string;

var ShortMonthNames: array[1..12] of string;

var LongMonthNames: array[1..12] of string;

var ShortDayNames: array[1..7] of string;

var LongDayNames: array[1..7] of string;

var SysLocale: TSysLocale;

var EraNames: array[1..7] of string;

var EraYearOffsets: array[1..7] of Integer;

var TwoDigitYearCenturyWindow: Word = 50;

var TListSeparator: Char;

{/codecitation}

Программа с многоязычным интерфейсом

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Hа уроке классической литературы. Учительница:

— Кто может привести пример языка, на котором никто сегодня не говорит, но который является фундаментом других языков?

Вовочка:

— HТМL.

Автор благодарит читателя этих уроков, программиста Alexey Salo за идею, без которой написание данного материала было бы невозможным. Сразу отмечу, что урок получился не совсем для начинающих, количество написанного кода в примерах испугало даже меня, но тем не менее, попробуйте разобраться. Повсюду даны описание команд, некоторые мы рассматривали в предыдущих уроках. Возможно непонятные моменты рассмотрены более подробно.

Для начала немного теории

Ваша собственная программа может быть полезна не только вам, но и вашим друзьям, организации, где вы работаете. Если вы работаете не только на себя. Живя в нашем веке компьютерных технологий, информация может распространяться с довольно большой скоростью. Примером тому служат нашумевшие недавно волны интернет-вирусов, за считанные дни облетевшие по многим серверам мира. Так же дело и обстоит с полезными программами. Отличием полезной программы от вредоносной есть сам метод переноса от компьютера к компьютеру. По степени ее уникальности и полезности она может понравиться многим. Я не буду говорить о методах рекламы программных продуктов, они такие же самые, как и реклама обычных продуктов, будь то интернет-ресурс или обычных хозяйственный товар. Дело в том, что ваша программа, выпущенная в свободное распространение (выложенная на сайте, отправленная друзьям по почте и т.п.) абсолютно независимо от вашего желания может попасть любому человеку. Этот человек может быть другой национальности, абсолютно не понимающ

ий русского языка.

Если ваша программа изначально рассчитана на свободное распространение, свободное распространение с ограниченными функциями для последующего приобретения, если ваша программа может оказаться полезной для многих (утилита, игра, экранная заставка), то надо стараться изначально ее оформлять с англоязычным интерфейсом. Все дело в том, что большинство пользователей компьютеров в полной мере или частично знакомы с английским языком. Следовательно, разобраться в такой вашей программе смогут больше человек в мире, чем, скажем в программе с белорусским языковым интерфейсом. Здесь имеется в виду ни что иное, как глобальное внедрение вашего приложения в масштабе целой планеты, а не касательно, скажем, ваших знакомых. Но делая программу, даже с языком, являющимся международным (даже с китайским языком :), трудно рассчитывать на популярность во многих странах.

Большую популярность получили программы с многоязычным интерфейсом. Я имею в виду, что описанные выше программы получают большую популярность, чем аналогичные с однотипным диалоговым языком. К примеру, это некоторые командные оболочки (FAR, Windows Commander), антивирус DrWeb, интернет броузер Opera. В таких программах нужный язык можно выбрать из списка в окнах настройки.

В большинстве случаев, весь языковой интерфейс хранится в отдельном файле. И это правильно. Не стоит загромождать исполняемый EXE файл излишком информации, а настройка и адаптация нового языка, первоначально не включенного в такой программный пакет, происходит быстро и без перекомпиляции всего проекта.

Какой же метод выбрать для хранения и последующего считывания данных из файла языков. В большей степени для этого подходит рассмотренные нами в 16 уроке ini файлы. Такие файлы легко переносятся на другой компьютер вместе с самой программой, возможно редактировать из любого текстового редактора, изменить текст может любой человек.

Различные секции в таком ini-файле будут хранить в себе отдельные языковые интерфейсы. Например, секция [RUSSIAN] будет озаглавливать русскоязычный внешний вид программы, [ENGLISH] — англоязычный, и т.д. Я думаю, что с этим проблем у пользователя не будет.

Названия хранимых параметров состоят из названия окна (формы), в котором находится компонент плюс название самого компонента. Параметр должен состоять из одного слова. Хранимая величина — текст, который отображается на экране на этом компоненте. Это может быть свойство Caption или свойство Text, в зависимости от типа (класс) компонента. Например, для компонента Button1, находящегося в окне Form1 записываемый параметр и значение выглядит:

Form1Button1=Кнопка1

В нашей программе при чтении такого параметра должна произойти замена:

Form1.Button1.Caption := ‘Кнопка1’;

Естественно, это делается автоматически для всех визуальных компонентов, на каких есть текст. Проблема может состоять в том, что таких компонентов на каждой форме может быть, скажем 200. Тогда это очень загромоздит программный код. При оперативном исправлении такой программы (добавление, удаление компонентов), необходимо будет исправлять и эту часть кода. Выходом из создавшейся проблемы может быть свойства для определенного окна ComponentCount и Components. Свойство Components позволяет через массив получить доступ к любому элементу управления формы. Свойтсво ComponentCount показывает, сколько этих элементов управления (компонентов) у нас присутствует в окне. Нам нужно будет просто организовать цикл от 1 до ComponentsCount и для каждого компонента прочитать соответствующее значение Caption или Text из INI файла.

Внутри такого цикла нужно определять тип компонента. Ведь для кнопки (Button, BitBtn, SpeedButton), метки (Label, StaticText), флажка (CheckBox, RadioButton) и пр. свойство Caption определяет текст, который будет виден на этом компоненте. Для Edit, Memo, ComboBox и пр. свойтсво Text. Следовательно, очень важно верно определить тип, выбранного из цикла компонента, чтобы в последствии правильно занести соответствующее значение в соответствующее свойство.

Следующим этапом, когда мы определили тип компонента, следует само чтение данных из ini-файла. Вот примерный кусок кода такой программы:

// если в окне есть хотя бы один элемент управления (компонент)

if ComponentCount0 then

// цикл от 1 до кол-ва компонентов

for i:=1 to ComponentCount do

// если текущий элемент является элементом класса TButton, то

if Components[i-1].ClassType = TButton then

(Components[i-1] as TButton).Caption:= ЧТЕНИЕ_ДАННЫХ_ИЗ_INI

Разъясню последнюю строчку из этого примера. Через

(Components[i-1] as TButton)

Мы получаем доступ к свойствам компонента, представляя его к классу TButton. Для этого в предпоследней строке примера мы и производим проверку класса выбранного циклом компонента. Если такую проверку не производить, то во время выполнения программы при обращении, скажем к компоненту класса TEdit к свойству Caption, появится сообщение об ошибке (У TEdit свойство Text!).

(i-1) как вы наверное уже догадались, список массива элементов управления формы начинается с нуля. А заканчивается ComponentCount-1.

Еще один нюанс. Если такой многоязычный ini-файл был потерян в процессе эксплуатации, то пользователя рискует остаться вообще без каких либо намеков в вашей программе. Поэтому рекомендую все-таки изначально давать текст вашим компонентам как в обычной программе. В случае невозможности чтения данных из INI файла текст на этих компонентах останется нетронутым.

Теперь я даю вам универсальный модуль, который должен присутствовать в каждом оконном модуле вашей программы, где необходимо менять язык по указанию пользователя. Я являюсь автором этого куска программы, поэтому, возможно, что-то можно и сократить, тем не менее…

В раздел Uses необходимо дописать модуль для работы с ini-файлами:

uses IniFiles;

Раздел public дописываем одну строку объявления процедуры:

public

{ Public declarations }

procedure ChangeLang(LangSection: string);

Сама процедура пишется изначально вручную вместе с заголовком:

procedure TForm1.ChangeLang(LangSection: string);

var

// временная числовая переменная для выборки всех компонентов

i: Integer;

LangIniFile: TIniFile;

// строковая переменная для получения каталога, где находится запущенный EXE файл

ProgramPath: string;

begin

// если в окне больше одного компонента

if ComponentCount 0 then

begin

// получаем каталог, где лежит запущенный EXE файл

ProgramPath := ExtractFileDir(Application.ExeName);

// гарантированно устанавливаем последний символ ‘\’ в конце строки

if ProgramPath[Length(ProgramPath)] ‘\’ then

ProgramPath := ProgramPath ‘\’;

// подготавливаем INI файл. Он должен иметь название lang.ini

// и должен находиться в каталоге программы

LangIniFile:=TIniFile.Create(ProgramPath ‘lang.ini’);

// читаем заголовок окна

Caption:=LangIniFile.ReadString(LangSection,name,Caption);

// перебираем все компоненты в этом окне

for i:=1 to ComponentCount do

begin

// если выбран из массива компонент Button, то изменяем текст на кнопке

if Components[i-1].ClassType = TButton then

(Components[i-1] as TButton).Caption := LangIniFile.ReadString(LangSection,

name Components[i-1].name, (Components[i-1] as TButton).Caption);

// Напомню описание функции ReadString:

// ====================================

// LangIniFile.ReadString( СЕКЦИЯ, ПАРАМЕТР, ЗНАЧЕНИЕ_ПО_УМОЛЧАНИЮ );

// 1. LangSection — передаваемый параметр в процедуру.

// В процедуру передается название секции для выбранного языка

// 2. Name Components[i-1].Name — Name — название формы,

// Components[i-1].Name — название компонента

// 3. (Components[i-1] as TButton).Caption — в случае неудачного чтения этого

// параметра из ini файла (нет такого параметра), то ничего меняться не будет

// аналогично для других типов:

if Components[i-1].ClassType = TLabel then

(Components[i-1] as TLabel).Caption := LangIniFile.ReadString(LangSection,

name Components[i-1].name, (Components[i-1] as TLabel).Caption);

if Components[i-1].ClassType = TEdit then

(Components[i-1] as TEdit).Text := LangIniFile.ReadString(LangSection,

name Components[i-1].name, (Components[i-1] as TEdit).Text);

// …

// …

// …

end;

LangIniFile.Free; // освобождаем ресурс

end;

end;

Обратите внимание, в программе два окна. В каждом модуле для каждого отдельного окна присутствует эта вышеописанная процедура.

Вместо строк // … вы можете добавлять другие типы компонентов, например, можете описать тип компонента TCheckBox, если таковой имеет место в вашей программе. В идеале описывайте по шаблону все или большинство типов компонентов, имеющиеся в наличие в delphi. Для этого вам понадобится несколько десятков строк программного кода, но зато вы гарантированно можете применять эту процедуру не только в одной вашей программе, не проверяя наличие всех используемых типов компонентов.

Напомню, аналогичную вышеприведенную процедуру, без изменений, вы должны вписать в каждый модуль вашей программы. При смене языка, например, в программе с тремя окнами (Form1, Form2, Form3) происходит следующим куском программного кода:

Form1.ChangeLang(‘RUSSIAN’);

Form2.ChangeLang(‘RUSSIAN’);

Form3.ChangeLang(‘RUSSIAN’);

Поскольку процедуру смена языка мы объявляем в разделе public, то доступ к этой процедуре мы можем получить из любого места программы. Как вы заметили из примера, для переключения на русский язык указана имя секции RUSSIAN.

Теперь рассмотрим содержание самого INI файла. Его вы должны создавать самостоятельно. Во-первых, нужно помнить правила орфографии windows ini-файла, во-вторых, названия параметров должны соответствовать имени формы плюс названия компонента, хранимое значение следует за знаком равенства. Для примера, ini-файл с двумя языками, с двумя окнами, на каждом окне находится по две кнопки.

; начало файла lang.ini

[RUSSIAN]

Form1Button1=Кнопка 1 на форме 1

Form1Button2=Кнопка 2 на форме 1

Form2Button1=Кнопка 1 на форме 2

Form2Button2=Кнопка 2 на форме 2

[ENGLISH]

Form1Button1=Button 1 on form 1

Form1Button2=Button 2 on form 1

Form2Button1=Button 1 on form 2

Form2Button2=Button 2 on form 2

; конец файла lang.ini

Кроме текста на визуальных элементах управления вашей программы, текст содержится еще в заголовке программы, в панели задач. Еще могут присутствовать всплывающие подсказки Hint. Полномасштабный перевод всех компонентов на несколько языков в ini файле может несколько затруднить сам процесс программирования. Но ради гибкости настройки интерфейса программы пожертвовуйте лишним временем. Ведь для изменения текста, добавления нового языка (возможно даже это делаете не вы) не нужно перекомпилировать проект. Это максимально упрощает языковую настройку. Можете вообще сделать ini-файл одноязычным. Перевести его можете после окончания проектирования, или вообще предоставьте это профессиональному переводчику.

Одним из слабых мест в такой программе есть необходимость помещать процедуру смены языка в каждый модуль. Если у вас в программе множество окон, это довольно сильно загромождает программный код, следовательно, увеличивает размер программы, следовательно, замедляет ее работу. Как же сделать так, чтобы весь процесс смены языка во всем приложении умещался с одной процедуре. Очень просто. А может и не просто. А в общем, через компонент Application. Он является по своей сути самой программой, значит, содержит в себе все компоненты форм (уже упоминалось в первых уроках, что сама форма и есть компонент). Таким образом:

// если в приложении есть компоненты форм (не консольное приложение)

if Application.ComponentCount 0 then

// перебираем все компоненты

for i := 1 to Application.ComponentCount do

// если выбранный компонент является подклассом окна, то

if Application.Components[i-1].ClassParent = TForm then

begin

// обработка переключения языка для этого окна

end;

Очень похоже на предыдущий пример…

Извините, что на первый взгляд эта тема урока кажется, немного сложноватой для начинающего, но тут важно разобраться в общих принципах, в нескольких свойствах, внимательно перечитать весь урок снова и все может стать понятным. Обязательно рассмотрите прилагаемый первый пример, прежде чем продолжать рассматривать этот урок далее.

Опять ищем слабые места в программе. Для начинающих программистов может быть новостью, что в одной программе не может быть двух и более окон с одинаковыми названиями. А как же дело обстоит с MDI приложениями. Дочернее окно проектируется в единственном варианте, а в внутри родительской формы, во время работы программы, оно может создаваться теоретически в неограниченном количестве. Имена же самой вновь создаваемой дочерней форме присваиваются системой автоматически. Следовательно, читать параметр из ini-файла по свойству Name не подойдет. Таким методом можно максимально прочитать язык для компонентов только для одного дочернего MDI-окна. Отсюда следует, что нужно читать данные согласно свойству ClassName, которое является уникальным для отдельного класса окна. Например, для окна Form1, являющегося главным MDI-окном такой класс TForm1. Для окна Form2, дочернего MDI-окна класс TForm2. Вот, вы наконец и узнали, что же это за такая туква Т, стоящая в начале названия компонента. Это надкласс, объединяющий однотипны

е компоненты (в том числе и окно программы) в единую группу, с одинаковыми вложенными свойствами. Это краткое описание, можно сказать, своими словами.

Эти все «наваяния» организованы во втором примере. Там смена языка происходит из одной процедуры абсолютно для всех окон. Тем самым мы уменьшили программный код за счет увеличения качества, увеличили количество вложенных циклов. Программа примера номер 2 является незаконченным каркасом MDI-приложения. Не удивляйтесь, почему программа не выполняет функции редактора.

Оъявление. Автор уроков для начинающих по delphi ищет темы, какие вам было бы интересно узнать. Свои предложения отсылайте мне, Semen’у, по адресу mailto:semen@krovatka.net?subject=предложение, указав в теме письма слово «предложение». Ваше предложение не должно быть очень сложным для программного решения, понятным для начинающего, тема не должна отклоняться от тематики ведения уроков (например, не рассматривается управление базами данных, SQL, internet и пр.). Материал, написанный по вашему предложению, ориентировочно должен быть дан в объеме одного урока. Предложение в текущий урок должно быть отправлено до пятницы.

По вашему дополнительному пожеланию ссылка на автора идеи будет помещена в начале урока. В таком случае обязательно делайте соответствующую пометку и указывайте свое имя с обратным адресом в письме с предложением.

{/codecitation}