Советы по Delphi

         

Поиск текста в текстовом файле


Кто-нибудь знает быстрый способ поиска строки в текстовом файле?

    unit BMSearch;

(* -------------------------------------------------------------------
Поиск строки методом Boyer-Moore.
Это - один из самых быстрых алгоритмов поиска строки. See a description in:
R. Boyer и S. Moore. Быстрый алгоритм поиска строки. Communications of the ACM 20, 1977, страницы 762-772 ------------------------------------------------------------------- *)

interface



type

{$ifdef WINDOWS}
size_t = Word; {$else}
size_t = LongInt; {$endif}

type
TTranslationTable = array[char] of char;  { таблица перевода }
TSearchBM = class(TObject) private FTranslate  : TTranslationTable;     { таблица перевода } FJumpTable  : array[char] of Byte;   { таблица переходов } FShift_1    : integer; FPattern    : pchar; FPatternLen : size_t;
public procedure Prepare( Pattern: pchar; PatternLen: size_t; IgnoreCase: Boolean ); procedure PrepareStr( const Pattern: string; IgnoreCase: Boolean );
function  Search( Text: pchar; TextLen: size_t ): pchar; function  Pos( const S: string ): integer; end;

implementation

uses
  SysUtils;

(* -------------------------------------------------------------------
Игнорируем регистр таблицы перевода ------------------------------------------------------------------- *)

procedure CreateTranslationTable( var T: TTranslationTable; IgnoreCase: Boolean );
var
c: char; begin
for
c := #0 to #255 do T[c] := c;
if not IgnoreCase then exit;
for c := 'a' to 'z' do T[c] := UpCase(c);
{ Связываем все нижние символы с их эквивалентом верхнего регистра }
T['Б'] := 'A'; T['А'] := 'A'; T['Д'] := 'A'; T['В'] := 'A';
T['б'] := 'A'; T['а'] := 'A'; T['д'] := 'A'; T['в'] := 'A';
T['Й'] := 'E'; T['И'] := 'E'; T['Л'] := 'E'; T['К'] := 'E';
T['й'] := 'E'; T['и'] := 'E'; T['л'] := 'E'; T['к'] := 'E';
T['Н'] := 'I'; T['М'] := 'I'; T['П'] := 'I'; T['О'] := 'I';
T['н'] := 'I'; T['м'] := 'I'; T['п'] := 'I'; T['о'] := 'I';
T['У'] := 'O'; T['Т'] := 'O'; T['Ц'] := 'O'; T['Ф'] := 'O';
T['у'] := 'O'; T['т'] := 'O'; T['ц'] := 'O'; T['ф'] := 'O';
T['Ъ'] := 'U'; T['Щ'] := 'U'; T['Ь'] := 'U'; T['Ы'] := 'U';
T['ъ'] := 'U'; T['щ'] := 'U'; T['ь'] := 'U'; T['ы'] := 'U';
T['с'] := 'С'; end;

(* -------------------------------------------------------------------
Подготовка таблицы переходов ------------------------------------------------------------------- *)

procedure TSearchBM.Prepare( Pattern: pchar; PatternLen: size_t;
IgnoreCase: Boolean ); var
i: integer; c, lastc: char; begin
FPattern := Pattern; FPatternLen := PatternLen;
if FPatternLen < 1 then FPatternLen := strlen(FPattern);
{ Данный алгоритм базируется на наборе из 256 символов }
if FPatternLen > 256 then exit;

{ 1. Подготовка таблицы перевода }
CreateTranslationTable( FTranslate, IgnoreCase);

{ 2. Подготовка таблицы переходов }
for c := #0 to #255 do FJumpTable[c] := FPatternLen;
for i := FPatternLen - 1 downto 0 do begin c := FTranslate[FPattern[i]]; if FJumpTable[c] >= FPatternLen - 1 then FJumpTable[c] := FPatternLen - 1 - i; end;
FShift_1 := FPatternLen - 1; lastc := FTranslate[Pattern[FPatternLen - 1]];
for i := FPatternLen - 2 downto 0 do if FTranslate[FPattern[i]] = lastc  then begin FShift_1 := FPatternLen - 1 - i; break; end;
if FShift_1 = 0 then FShift_1 := 1; end;

procedure TSearchBM.PrepareStr( const Pattern: string; IgnoreCase: Boolean );
var
str: pchar; begin
if
Pattern <> '' then begin {$ifdef Windows}
str := @Pattern[1]; {$else}
str := pchar(Pattern); {$endif}

Prepare( str, Length(Pattern), IgnoreCase); end; end;

{ Поиск последнего символа & просмотр справа налево }

function TSearchBM.Search( Text: pchar; TextLen: size_t ): pchar;
var
shift, m1, j: integer; jumps: size_t; begin
result := nil; if FPatternLen > 256 then exit;
if TextLen < 1 then TextLen := strlen(Text);

m1 := FPatternLen - 1; shift := 0; jumps := 0;
{ Поиск последнего символа }
while jumps <= TextLen do begin Inc( Text, shift); shift := FJumpTable[FTranslate[Text^]]; while shift <> 0 do begin Inc( jumps, shift); if jumps > TextLen then exit;
Inc( Text, shift); shift := FJumpTable[FTranslate[Text^]]; end;
{ Сравниваем справа налево FPatternLen - 1 символов }
if jumps >= m1 then begin j := 0; while FTranslate[FPattern[m1 - j]] = FTranslate[(Text - j)^] do begin Inc(j); if j = FPatternLen then begin result := Text - m1; exit; end; end; end;
shift := FShift_1; Inc( jumps, shift); end; end;

function TSearchBM.Pos( const S: string ): integer;
var
str, p: pchar; begin
result := 0; if S <> '' then begin {$ifdef Windows}
str := @S[1]; {$else}
str := pchar(S); {$endif}

p := Search( str, Length(S)); if p <> nil then result := 1 + p - str; end; end;

end.

[000305]



Поиск загрузочного диска


Есть какая-либо функция или вызов API для поиска загрузочного диска?

Я нашел это в регистрах:

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup

Значение "BootDir" и есть искомая величина. [000285]



Получение размера файла


Вот мой код, которым я пользуюсь для определения информации о группе файлов:

    var
Fhnd2 : File ; sPath : String; tpath : string; SearchRec: TSearchRec; tempsearch : string; tempfiles : Integer; tempbytes : LongInt; wBytes : Word; sTemp : String ; iLen : Integer ; szString: Array[0..128] Of Char; ec : integer;
BEGIN

{* Выбираем системный каталог *} MailManLogS('Запуск MailMan'); sTemp := ParamStr(0) ; iLen := Length(sTemp) ; WHILE sTemp[iLen] <> '\' DO DEC (iLen) ; StrPCopy(szString, sTemp) ; szString[iLen] := #0 ; SysDir := StrPas(szString) ;
tempbytes := 0; tempfiles := 0; Files2bProc := 0; Bytes2bProc := 0; MailManLogS('Калькулируем файлы для обработки'); {* Подсчитываем, сколько файлов и байт должны быть обработаны *} tempsearch := SysDir + 'spool\witchcrf\d\*.*' ; ec := FindFirst(tempsearch, faSysFile, SearchRec); While ec = 0 do begin if ((SearchRec.Name <> '.') and (SearchRec.Name <> '..')) then begin tempfiles := tempfiles + 1; ---->     tempbytes := tempbytes + SearchRec.Size;       <------
TotalInBytes.Text := IntToStr(tempbytes); TotalInFiles.Text := IntToStr(tempfiles); MailManLogS('Файл-' + SearchRec.Name + '     Размер-' + IntToStr(SearchRec.Size));
end; ec := FindNext(SearchRec); end;
MailManLogS('Всего файлов = ' + IntToStr(tempfiles) + '        Байт = ' + IntToStr(tempbytes));
end;

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

Я все это делал с использованием FindFirst. Функция возвращает запись, имеющую тип TSearchRec. Данная запись содержит переменную Size, которая содержит размер файла в байтах. Это может быть не так красиво, но это работает:

    function GetFileSize(FileName: string): Longint;
var
SearchRec: TSearchRec; begin
if FindFirst(FileName, faAnyFile, SearchRec) = 0 then Result:=SearchRec.Size else Result:=-1;       {возвращаем ошибку, это может быть число меньше нуля} end;

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

    Function FileGetSize1(Filename : String) : LongInt;
var
F : File; OldFileAttr : Integer; begin
if FileExists(Filename) then begin OldFileAttr := FileGetAttr(Filename); FileSetAttr(Filename,OldFileAttr and (faReadOnly xor $FFFF)); try AssignFile(F, Filename); Reset(F,1); Result := FileSize(F); CloseFile(F); finally FileSetAttr(Filename, OldFileAttr); end; end else Result := 0; end;

Function FileGetSize2(Filename : String) : LongInt;
var
FileHandle : Integer; begin
if FileExists(Filename) then begin FileName := FileName + chr(0); FileHandle := _lopen(@FileName[1], 0); Result := _llseek(FileHandle, 0, 2); _lclose(FileHandle); end else Result := 0; end;

[Eric Nielsen, htrsoft@midwest.net]

Я не стал возиться с AssignFile.

    Function FileSizeInBytes(YourFile : String) : LongInt;
Var
F : Integer; Begin
F:=FileOpen(YourFile,0);  { режим ReadOnly } FilesizeInBytes := FileSeek(F,0,2); FileClose(F) End;

Примечание: Проверка ошибок отсутствует !!! [001688]



Проблема получения времени создания файла


Попробуйте следующую функцию, которая не требует вызова FindFirst:

    function GetFileDate(TheFileName: string): string; var FHandle: integer; begin FHandle := FileOpen(TheFileName, 0); result := DateTimeToStr(FileDateToDateTime(FileGetDate(FHandle))); FileClose(FHandle); end;

Одно маленькое предупреждение: время, возвращаемое Win32-функцией, отсчитывается от Гринвича, поэтому вам необходимо привести полученный результат к локальному времени. Чтобы быть уверенным, проверьте документацию. (Я уверен, что FindNextFile делает это правильно). [000087]



Процедура форматирования


В Shell32.dll спрятана WinAPI функция SHFormatDrive, вызывающая стандартный диалог форматирования сменного накопителя. Я уже встречал этот вопрос в конференции borland.public.delphi.winapi.

    {раздел реализации}
..
..
const
SHFMT_ID_DEFAULT        = $FFFF; // Опции форматирования SHFMT_OPT_QUICKFORMAT   = $0000; SHFMT_OPT_FULL          = $0001; SHFMT_OPT_SYSONLY       = $0002; // Коды ошибок SHFMT_ERROR             = $FFFFFFFF; SHFMT_CANCEL            = $FFFFFFFE; SHFMT_NOFORMAT          = $FFFFFFFD;
function SHFormatDrive(Handle: HWND; Drive, ID, Options: Word): LongInt;
stdcall; external 'shell32.dll' name 'SHFormatDrive'
procedure TForm1.btnFormatDiskClick(Sender: TObject);
var
retCode: LongInt; begin
retCode:=       SHFormatDrive(Handle, 0, SHFMT_ID_DEFAULT, SHFMT_OPT_QUICKFORMAT); if retCode < 0 then ShowMessage('Не могу отформатировать накопитель'); end;

end.

Как удалить все файлы из директории?

Попробуй это:

    procedure TfrmMain.DelDir(DirName: string);
var
SearchRec: TSearchRec; GotOne: integer; begin
GotOne:= FindFirst(DirName + '\*.*', faAnyFile, SearchRec); while GotOne = 0 do begin if ((SearchRec.Attr and faDirectory) = 0) then DeleteFile(DirName + '\' + SearchRec.Name) else if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then DelDir(DirName + '\' + SearchRec.Name); GotOne:= FindNext(SearchRec); end; FindClose(SearchRec); end;

Если впоследствии Вы захотите директорию удалить, попробуйте сделать так:

    //--------
DelDir('C:\WASTE'); {-I} RmDir('C:\WASTE'); {+I} if IOResult <> 0 then raise Exception.Create('Ошибка удаления каталога'); //-------

Автор рекурсивного кода David Ullrich. [000079]



Путь/Имя папки 'My Computer'


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

Операционная система windows 32 основывается на оболочке, которая использует виртуальные папки, такие, как 'my computer' (Мой компьютер), 'desktop' (Рабочий Стол) и 'recycle bin' (Корзина). Некоторые из них являются частью физической файловой системы. Другими словами, они имеют соответствующий реальный каталог в файловой системе. Это относится, например, к системным папкам 'desktop' и 'recycle bin'. Данные каталоги могут быть использованы как InitialDir в TOpenDialog, но сначала вы должны получить их физическое месторасположение, которое может различаться на других компьютерах. Чтобы узнать их реальное месторасположение на локальном диске, вы должны воспользоваться некоторыми специальными вызовами API (смотри пример ниже). Другие папки, типа 'my computer' и 'printers' не являются частью файловой системы, они чисто виртуальные. Обращаю ваше внимание на то, что такие папки можно использовать в TOpenDialog, но никак не в InitialDir.

Виртуальные папки (я немного упрощаю) имеют тип SHITEMID (идентификатор элемента). Получить к ним доступ можно используя pointers to item identifiers list (PIDL, указатель на элемент списка идентификаторов). Для того, чтобы получить PIDL специальной папки, вы должны использовать функцию SHGetSpecialFolder. Физическое месторасположение соответствующей директории можно получить, передавая PIDL в качестве входного параметра функции GetPathFromIDList. Если папка является частью файловой системы, функция возвращает путь к ней в виде строки (которая впоследствии может использоваться как InitialDir). Но если вы хотите использовать OpenDialog только с виртуальными папками (например, с 'my computer'), то в принципе вы должны использовать PIDL как InitialDir, но это работать не будет. Я думаю дело в том, что TOpenDialog использует PIDLs только для просмотра, а для InitialDir требуются только реальные (физические) каталоги.

Вот пример, показывающий как получить путь к 'recent documents' (последние документы) и использовать его в качестве InitialDir:

    procedure TForm1.Button1Click(Sender: TObject);
var
PIDL: Pointer;
Path: LPSTR;
const
CSIDL_RECENT = $0008; begin
Path := StrAlloc(MAX_PATH);
SHGetSpecialFolderLocation(Handle, CSIDL_RECENT, @PIDL);
if SHGetPathFromIDList(PIDL, Path) then // возвращает False если папка
не является частью файловой системы
begin OpenDialog1.InitialDir := Path; OpenDialog1.Execute; end; StrDispose(Path);
end;

Я думаю вам необходимо создать класс-оболочку для этих вызовов API. Они располагаются в shell32.dll. Наилучший совет, который я могу дать при изучении этого вопроса - копнуть поглубже файл ShlObj.h. Я также не программирую в C, но почерпнул оттуда немало ценной информации.

Вот некоторые константы, которые вам могут понадобиться:

    CSIDL_DESKTOP            = $0000; CSIDL_PROGRAMS           = $0002; CSIDL_CONTROLS           = $0003; CSIDL_PRINTERS           = $0004; CSIDL_PERSONAL           = $0005; CSIDL_STARTUP            = $0007; CSIDL_RECENT             = $0008; CSIDL_SENDTO             = $0009; CSIDL_BITBUCKET          = $000a; CSIDL_STARTMENU          = $000b; CSIDL_DESKTOPDIRECTORY   = $0010; CSIDL_DRIVES             = $0011;  // Мой компьютер CSIDL_NETWORK            = $0012; CSIDL_NETHOOD            = $0013; CSIDL_FONTS              = $0014; CSIDL_TEMPLATES          = $0015;

[000280]



Разбиение и сборка файла


Не так сложно, вот как это может выглядеть:

    inf:   file; outf:  file; size:  longint; outsize: longint; amt:    word; amtRead: word;
assignfile (inf, 'входной файл'); reset (inf, 1); size := fileSize (inf); repeat showMessage ('Вставьте дискету в дисковод "A"')  { или "B", а лучше позвольте их определять } assignFile (outf, 'A: выходной файл'); rewrite (outf, 1); outsize := diskFree (1);  { или 2, если это дисковод "B" } while (outsize > 0) and (size > 0) do begin amt := sizeof(buf); if amt > outsize then amt := outsize; blockRead (inf, buf, amt, amtRead); blockWrite (outf, buf, amtRead); dec (outSize, amtRead); dec (size, amtRead); end; closeFile (outf); until size <= 0; closeFile (inf);

Писалось все "от руки", поэтому синтаксис может быть с ошибками. Правильным было бы добавление кода, который позволит пользователю определить используемый дисковод ("A" или "B"), задание именной схемы для восстановления информации, если один из дисков испортится и пр.

Сборка происходит аналогично: открываем на диске выходной файл, просим пользователя вставить дискетту, blockRead/blockWrite с дискеты на жесткий диск, просим пользователя вставить другую дискету, пока куски файла не считаются полностью. [001684]



Readln для более чем 255 символов


Как мне воспользоваться функцией readln(), если файл содержит строки с более чем 255 символами?

ReadLn акцептует массив символов array [0..something] of Char и использует его в качестве буфера для чтения символов, замыкая цепочку терминирующим нулем. Единственное ограничение: компилятор должен иметь возможность вычисления размера буфера во время компиляции, что делает невозможным объявление переменой типа PChar и ее распределение во время выполнения программы.

Обходной путь:

    Type {используем самое большое количество символов в строке, с которым вы можете иметь дело} TLine = Array [0..1024] of Char;
PLine = ^TLine;
Var pBuf: PLine; ... New( pBuf );
... ReadLn( F, pBuf^ );

Для передачи pBuf функциям, которым требуется параметр типа Pchar, используйте приведение типа подобно PChar( pBuf ).

Примечание: вы, конечно, можете использовать объявление переменной типа TLine или непосредственно массив символов, но я предпочитаю распределять из кучи нечто большее, чем 4 байта...

[000838]



Рекурсивное удаление файлов и подкаталогов


    {
Здесь я привожу немного сокращенный код, который я создавал для Borland Pascal 5.5 под DOS (оригинальный код не делал rmDir, поэтому вы можете поэкспериментировать с этим, передав указатель на каталог функции rmDir в конце этого кода). Я подозреваю, что Delphi-версия может быть или идентичной, или иметь некоторые различии в написании имен функций (рекомендую ознакомиться с электронной документацией по Delphi, с темой, где описаны функции для работы с файлами). Данный код не предусматривает проверку атрибутов файлов, которые могут быть установлены для предотвращения удаления файла. (В Pascal 5.5 вам необходимо между парой {$I-} {$I+} поместить функцию, которая вызывает проблему, не знаю, делаете ли вы это в Delphi.) }

procedure removeTree (DirName: string);
var
FileSearch:  SearchRec; begin
{ для начала пробегаемся, и удаляем все файлы } chDir (DirName); FindFirst ('*.*', Directory, FileSearch); while (DosError = 0) do begin if (FileSearch.name <> '.') AND (FileSearch.name <> '..') AND ( (FileSearch.attr AND Directory) <> 0) then begin if DirName[length(DirName)] = '\' then removeTree (DirName+FileSearch.Name) else removeTree (DirName+'\'+FileSearch.Name); ChDir (DirName); end; FindNext (FileSearch) end;
{ затем пробегаемся, и удаляем все каталоги } FindFirst ('*.*', AnyFile, FileSearch); while (DosError = 0) do begin if (FileSearch.name <> '.') AND (FileSearch.name <> '..') then Remove (workdir); end; FindNext (FileSearch) end; rmDir (DirName) end;

[001705]



Слияние двух бинарных файлов


Самым простым способом является открытие первого, перемещение в его конец, и копирование с этого места второго файла.

    Var
f1, f2 : File; xfer   : Word; buf    : PChar; Begin
AssignFile(f1, name1); Reset(f1); Seek(f1, Filesize(f1)); AssignFile(f2, name2); Reset(f2); GetMem(buf, 65000); Repeat BlockRead(f1, buf^, 65000, xfer); BlockWrite(f2, buf^, xfer); Until xfer < 65000; CloseFile(f1); CloseFile(f2); End;

[001675]



Сохранение в файле пятисот символов из массива


Следующий код может помочь вам начать.

    Type
TCharArray = Array[500] of Char;
Procedure WriteToFile(Var aArray : TCharArray; sFileName : String); {Примечание:
Объявление массива как параметр Var позволяет передавать только ссылку на массив,
а не копировать его целиком в стек, если же вам нужна безопасная работа с массивом,}
то вам не следует передавать его как Var-параметр.}

Var
nArrayIndex : Word;
fFileHandle : TextFile;
Begin
AssignFile(fFileHandle, sFileName);
Rewrite(fFileHandle);

For nArrayIndex := 1 to 500 Do
Begin

Write(fFileHandle, aArray[nArrayIndex]); End;

CloseFile(fFileHandle);
End; {end Procedure, WriteToFile()}

[001707]



Удаление непустого каталога


Пришло письмо от читателя:

Здравствуйте

Вот, понадобилась мне процедурка удаления непустого каталога. Нашел, не помню где. Включил в програму, работала, пока не стал использовать длинные имена каталогов (без пробелов, кстати). NT стала операцию выполнять, но криво (возбуждала исключение). Окончательно:

    procedure TForm1.deletedirectory(dir:string);
var
sh:SHFILEOPSTRUCT; st:string; sr:tsearchrec; pst:pchar; begin
if
findfirst(dir,faDirectory,sr)=0 then begin
//added by me dir:=longtoshortfilename(dir); //original code sh.Wnd:= Form1.handle; sh.wFunc:= FO_DELETE; Pst:=StrAlloc(Length(dir{sr.Name})+1); StrPLCopy(Pst,dir{sr.Name},Length(dir{sr.Name})+1); sh.pFrom:=pst; sh.pTo:= Nil; sh.fFlags:= FOF_NOCONFIRMATION or FOF_SILENT; sh.hNameMappings:= Nil; sh.lpszProgressTitle:= Nil;
SHFileOperation(sh);
StrDispose(Pst); end;
findclose(sr); end;

С уважением, Александр Рабцевич [000744]



Управление атрибутом файла date/time


"Могу ли я написать функцию, которая устанавливает дату одного файла, равную дате другого файла?"

Не проблема. Используйте следующую функцию, использующую в качестве параметров две строки с полными путями/именами файлов DOS. Файл, дату которого вы хотите установить идет вторым параметром, файл, чью дату вы хотите использовать - первым.

    procedure CopyFileDate(const Source, Dest: String);
var
SourceHand, DestHand: word; begin
SourceHand := FileOpen(Source, fmOutput);       { открываем исходный файл } DestHand := FileOpen(Dest, fmInput);            { открываем целевой файл } FileSetDate(DestHand, FileGetDate(SourceHand)); { получаем/устанавливаем дату } FileClose(SourceHand);                          { закрываем исходный файл } FileClose(DestHand);                            { закрываем целевой файл } end;

[000534]



Управление каталогами и файлами


    unit win95;
{
Копирование, перемещение и удаление файлов и каталогов наподобие Проводника (Explorer) в Windows 95. Дата  : 06/04/97 Последнее обновление: 03/08/97
Просьба сообщать о всех найденных ошибках и недочетах на мой адрес электронной почты. Приветствуются пожелания и предложения по улучшению функциональности!!!
ОБНОВЛЕНИЯ: (18/04/97) Множество небольших поправок после множества ваших писем. Спасибо всем.
(31/08/97) Две новых процедуры: Win95AddToRecent и Win95ClearRecentDocs. }
interface
Uses Classes, ShellApi, ShlObj, Registry, Windows;
type
Str10  = String[10];
Const
fpRootKey ='\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'; fpDesktop   : Str10 = 'DESKTOP'; fpFavorites : Str10 = 'FAVORITES'; fpFonts     : Str10 = 'FONTS'; fpPersonal  : Str10 = 'PERSONAL'; fpPrograms  : Str10 = 'PROGRAMS'; fpRecent    : Str10 = 'RECENT'; fpSendTo    : Str10 = 'SENDTO'; fpStartMenu : Str10 = 'START MENU'; fpStartup   : Str10 = 'STARTUP'; fpTemplates : Str10 = 'TEMPLATES';

{Пути к системным папкам}
function GetFolderPath(Const FolderName: Str10): String;

{Функции для работы с файлами}
procedure Win95AddToRecentDocs(Const Filename: string);
procedure Win95ClearRecentDocs;
{Для манипулирования несколькими файлами разделите их имена символом "#0"}
function Win95Copy(Owner: Integer;  FromFile,ToFile: String; RenameOnCollision, Confirm: boolean): Boolean;
function Win95Move(Owner: Integer;  FromFile,ToFile: String; RenameOnCollision, Confirm: boolean): Boolean;
{Если SendToRecycleBin = True, то файлы будут отправлены в Корзину (RecycleBin),
в противном случае они будут стерты} function Win95Erase(Owner: Integer;  WichFiles: String; SendToRecycleBin, Confirm: Boolean): Boolean;

implementation

function GetFolderPath(Const FolderName: Str10): String;
begin
with
TRegistry.Create do Try RootKey:=HKEY_CURRENT_USER; OpenKey(fpRootKey,False); Result:=ReadString(FolderName); finally Free; end; end;

procedure Win95AddToRecentDocs(Const Filename: string);
begin
SHAddToRecentDocs(SHARD_PATH, @Filename[1]); end;

procedure Win95ClearRecentDocs;
begin
SHAddToRecentDocs(SHARD_PATH, nil); end;

function Win95Copy(Owner: Integer; FromFile,ToFile: String; RenameOnCollision, Confirm: boolean): Boolean;
const
Aborted: Boolean = False; var
Struct : TSHFileOpStructA; begin
While
pos(';',FromFile)>0 do FromFile[pos(';',FromFile)]:=#0; While pos(';',ToFile)>0 do ToFile[pos(';',ToFile)]:=#0; FromFile:=FromFile+#0#0; ToFile:=ToFile+#0#0; with Struct do begin wnd         :=Owner; wFunc       :=FO_Copy; pFrom       :=PChar(FromFile); pTo         :=PChar(ToFile); fFlags:=FOF_ALLOWUNDO or FOF_FILESONLY; If RenameOnCollision then fFLags:=fFlags or FOF_RENAMEONCOLLISION; If not Confirm then fFLags:=fFlags or FOF_NOCONFIRMATION; fAnyOperationsAborted:=Aborted; hNameMappings:=nil; lpszProgressTitle:=nil; end; result:=(SHFileOperationA(Struct)=0) and (not Aborted); end;

function Win95Move(Owner: Integer;  FromFile,ToFile: String; RenameOnCollision, Confirm: boolean): Boolean;
const
Aborted: Boolean = False; var
Struct : TSHFileOpStructA; begin

While
pos(';',FromFile)>0 do FromFile[pos(';',FromFile)]:=#0; While pos(';',ToFile)>0 do ToFile[pos(';',ToFile)]:=#0;
FromFile:=FromFile+#0#0; ToFile:=ToFile+#0#0; with Struct do begin wnd         :=Owner; wFunc       :=FO_Move; pFrom       :=PChar(FromFile); pTo         :=PChar(ToFile); fFlags:=FOF_ALLOWUNDO or FOF_FILESONLY; If RenameOnCollision then fFLags:=fFlags or FOF_RENAMEONCOLLISION; If Confirm then fFLags:=fFlags or FOF_NOCONFIRMATION; fAnyOperationsAborted:=Aborted; hNameMappings:=nil; lpszProgressTitle:=nil; end; result:=(SHFileOperationA(Struct)=0) and (not Aborted); end;

function Win95Erase(Owner: Integer;  WichFiles: String;  SendToRecycleBin, Confirm: Boolean): Boolean;
const
Aborted: Boolean = False; var
Struct : TSHFileOpStructA; begin
While
pos(';',WichFiles)>0 do WichFiles[pos(';',WichFiles)]:=#0; WichFiles:=WichFiles+#0#0; with Struct do begin wnd         :=Owner; wFunc       :=FO_Delete; pFrom       :=PChar(WichFiles); pTo         :=nil; If not Confirm then fFlags:=FOF_NOCONFIRMATION; If SendToRecycleBin then fFLags:=fFlags or FOF_ALLOWUNDO or FOF_FILESONLY else fFlags:=fFlags or 0 or FOF_FILESONLY; fAnyOperationsAborted:=Aborted; hNameMappings:=nil; lpszProgressTitle:=nil; end; result:=(SHFileOperationA(Struct)=0) and (not Aborted); end;

end.

[000268]



Установка режима бинарного файла


Попробуйте вызвать приведенную ниже процедуру немедленно после перезаписи выходного файла и перед началом записи в него:

    procedure SetBinaryMode (var F: file);  assembler;
asm mov ax,$4400 les di,F mov bx,word ptr es:[di] int $21 or dl,$20 xor dh,dh mov ax,$4401 int $21 end;

-Steve [000603]



Установка времени и даты файла


...вы были на правильном пути, когда заговорили о функции SetFTime. Ниже несколько строчек кода, которые изменяют значения даты & времени файла:

    var
f: file; begin
Assign(f, DirInfo.Name); Reset(f); SetFTime(f, Time); Close(f); end;

[001710]



Установка времени компиляции программы в диалоговом окне "О программе".


Я подразумаваю, что проблема состоит в получении времени компиляции?

    Var
F : Integer; S : String; Begin
F:=FileOpen(ExpandFileName(Application.ExeName), 0); S:=TimeToStr(FileDateToDateTime(FileGetDate(F))); FileClose(F); End;

Взгляните на описание функции DateTime... в файле помощи. Вероятно, существует лучший способ получения времени без использования функции FileOpen.

Также можно использовать время файла (File Time) в качестве номера версии, так, время 6:02 обозначало бы версию 6.02, и устанавливать его чем-то типа Touch. [001712]



Вопросы разделяемого доступа к файлу


А вы пробовали использовать блок try ... except?

Недавно я решал аналогичную задачу.

Код на основе приведенного ниже скелета отлично справлятеся с поставленной задачей...

    try
{здесь размещается код работы с открытым файлом} ... except
{здесь располагается код обработки исключений} {что-то типа MessageDlg('Не могу открыть файл', mtError, [mbOk], 0) или что-либо получше :) } ... end;

Для EXE & COM файлов может использоваться так называемый сетевой Shareable-атрибут, который позволяет нескольким пользователям выполнять один файл. Для текстовых файлов это не работает.

Единственный метод должен проверять наличие атрибута DOS "READ-ONLY" (только для чтения). Множество DOS & Windows программ устанавливают этот флаг после того, как они откроют файл, чтобы таким образом предотвратить к нему доступ из других программ. В качестве альтернативы, можно проверять атрибут файла NETWARE "READ-ONLY". Проверку можно организовать с помощью компонентов NETWARE API, обеспечивающими работу с сетевыми функциями. Если ваша программа находит одно из этих двух условий истинным, следует подождать некоторое время, и проверить его снова, прежде чем пытаться получить доступ к файлу. [001682]



Восстанавление длинных имен файлов по известным коротким


boris советует:

    //---------------------------------------------------------------------
// Восстанавливает длинные имена файлов по известным коротким (8.3)
// В качестве аргумента принимает полный или неполный (в т.ч. относительный)
// путь к файлу, например 'C:\WINDOWS\РАБОЧИ~1\ИТАКДА~1.LNK' или
// '..\..\COMMON~1\BORLAN~1\BDE\BDEREA~1.TXT'. Понимает сетевые имена.
// Возвращает полный(!) путь типа 'C:\Windows\Рабочий стол\и так далее.lnk',
// 'C:\Program Files\Common Files\Borland Shared\BDE\bdereadme.txt',
// '\\Computer\resource\Folder with long name\File with long name.ext'
//---------------------------------------------------------------------

Function RestoreLongName(fn: string): string;
function LookupLongName(const filename: string): string; var sr: TSearchRec; begin if FindFirst(filename, faAnyFile, sr)=0 then Result:=sr.Name else Result:=ExtractFileName(filename); SysUtils.FindClose(sr); end; function GetNextFN: string; var i: integer; begin Result:=''; if Pos('\\', fn)=1 then begin Result:='\\'; fn:=Copy(fn, 3, length(fn)-2); i:=Pos('\', fn); if i<>0 then begin Result:=Result+Copy(fn,1,i); fn:=Copy(fn, i+1, length(fn)-i); end; end; i:=Pos('\', fn); if i<>0 then begin Result:=Result+Copy(fn,1,i-1); fn:=Copy(fn, i+1, length(fn)-i); end else begin Result:=Result+fn; fn:=''; end; end; Var name: string; Begin
fn:=ExpandFileName(fn); Result:=GetNextFN; Repeat name:=GetNextFN; Result:=Result+'\'+LookupLongName(Result+'\'+name); Until length(fn)=0; End;

[001034]



Запись и чтение из файла массива записей


Это не очень Delphi-подобно (тем не менее, работа происходит с действительно паскалевскими записями), но вы можете писать и читать записи из/в файл, используя паскалевские процедуры для работы с файлами:

    type
TMyRec = record ; Field1 : integer ; Field2 : string ; end ;
TMyRecArray = array [0..9] of TMyRec ;
var
MyArray : TMyRecArray ; MyRec : TMyRec ; RecFile : file of TMyRec ;
begin

{...здесь должен быть расположен код инициализации MyArray...}
AssignFile( RecFile, 'MYREC.FIL' ) ; ReWrite( RecFile ) ; for i := 0 to 9 do begin Write( RecFile, MyRec[i] ) ; end ; CloseFile( RecFile ) ;

Также, вы можете использовать Read() для чтения записи из вашего файла, и Seek() для перемещения на его конкретную запись (начиная с 0). Для получения дополнительной информации обратитесь к разделу "I/O Routines" электронной справки по Delphi.

Если вы хотите делать это с Data Aware компонентами (компонентами для работы с базами данных), вы должны создать базу данных, где база данных "records" должна отражать структуру ваших паскалевских записей, при этом необходимо создать механизмы трансляции данных из одной среды в другую. Я не готов сейчас сказать вам, как это можно сделать, но, во всяком случае, всю функциональность можно инкапсулировать в отдельном специализированном компоненте. [001721]



THeader как сплиттер


Компонент THeader может работать в качестве сплиттера. Для этого вы должны связать события OnSize с другими элементами управления, расположенными на форме. Если у вас есть исходный код компонента, вы можете это проверить. У него есть все необходимые для этого характеристики типа событий на перемещение мыши и т.п.

[000399]



Использование файла помощи


Вот код для трех стандартных пунктов меню "Help":

    procedure TForm1.Contents1Click(Sender: TObject); begin Application.HelpCommand(HELP_CONTENTS, 0); end;
procedure TForm1.SearchforHelpOn1Click(Sender: TObject); begin Application.HelpCommand(HELP_PARTIALKEY, 0); end;
procedure TForm1.HowtoUseHelp1Click(Sender: TObject); begin Application.HelpCommand(HELP_HELPONHELP, 0); end;

- Neil [000770]



Как мне привязать файлы помощи в Delphi 3?


Вот как это делаю я:

Сначала создайте файл помощи. Откройте меню "Project/Options...", щелкните на закладке "Application" и введите путь к файлу помощи в строке "Help File". Или же вы можете сделать это непосредственно во время выполнения приложения, указав соответственное значение свойству Application.HelpFile. Затем вам необходимо присвоить значения свойству "HelpContext" у необходимых элементов управления. В нашем случае необходимо задать значение свойству "HelpContext" у кнопки "Help", обычно расположенной на вспомогательных окнах или диалогах. Наконец, в обработчике события нажатия на кнопку вызовите метод Application.HelpContext. Для нашей кнопки "Help" обработчик события OnClick мог бы выглядеть примерно так:

    procedure TForm1.btnHelpClick(Sender: TObject); begin Application.HelpContext(TButton(Sender).HelpContext); end;

Это все!

Вы также можете вызывать другие методы Application для вывода файлов помощи, такие, как Application.HelpCommand и Application.HelpJump.

[000128]



Как сделать так, чтобы в приложении вызывался хелп с окошечком для поиска раздела?


Nomadic советует:

    unit {$IFDEF WIN32} Windows {$ELSE} WinProcs {$ENDIF};

function WinHelp(Wnd: HWnd; HelpFile: PChar; Command: Word; Data: LongInt): Bool;

Здесь цитата из WinAPI Help: HELP_CONTEXTPOPUP An unsigned long integer containing the context number for a topic. Displays in a pop-up window a particular Help topic identified by a context number that has been defined in the [MAP] section of the .HPJ file. То же самое, что делает макрос "Search()" для WinHelp-а.

    procedure TForm1.HelpSearchFor;
var
S: String; begin
S := ''; Application.HelpFile := 'C:\MYAPPPATH\MYHELP.HLP'; Application.HelpCommand( HELP_PARTIALKEY, LongInt( @S ) ); end;

[001206]



Как заставить Help-файлы нормально отображать русский текст под Windows 3.x?


Nomadic советует:

Удалось вылечить дописыванием в файл пpоекта в гpафу Options стpочки FORCEFONT=Arial Cyr, пpичем HC31 pугается что нет такого шpифта, но зато хелп потом ноpмально показывается пpактически под любой pуссифициpованной виндой.

Пpовеpял с [Win31+CyrWin], [Win311Rus], [Win95PE], [Win95Rus].

На NT не пpовеpял.

Пpичем шpифты в тексте ноpмально пеpеключаются и будут не только Arial.

Вот кусок котоpый надо вставить в HPJ файл пеpед компиляцией - [OPTIONS] FORCEFONT=Arial Cyr [001208]



Не могу открыть файл помощи...


Я создал файл помощи для моего приложения и назвал его KidsHelp.hlp

При запуске в системе, в которой файл был создан, программа находит его без проблем. Данная машина имеет конфигурацию Pentium 120 с установленной Windows 95. При запуске программы на второй системе, с Windows 3.1, при выборе пункта меню "Using Help" программа не может открыть файл. Я создал файл помощи с помощью программы "HC31.exe". В самом проекте я не указывал полный путь к файлу помощи, я указал только его имя.

Для решения этой проблемы я делаю две вещи: Всегда располагаю файл помощи в том же каталоге, что и приложение Назначаю файл помощи в обработчике события главной формы OnCreate таким образом:

    Application.HelpFile := ChangeFileExt(Application.ExeName, '.HLP');

- Neil Rubenking [001101]



Оглавление файлов помощи (Help Files Contents)


Используйте HELP_FINDER, если "текущая закладка" не является закладкой 'Index' или 'Find'. HELP_FINDER открывает окно Help Topics, но не меняет закладку с оглавлением (Contents), если текущая закладка - 'Index' или 'Find'.

Попробуйте следующий код:

    Function L1InvokeHelpMacro(const i_strMacro: String; const i_bForceFile:
Boolean): Boolean;
Begin
if i_bForceFile then Application.HelpCommand(HELP_FORCEFILE, 0);
Result:=Application.HelpCommand(HELP_COMMAND, Longint(PChar(i_strMacro))); //Приведение типа PChar здесь необязательно.
End;

Ищем ассоциированный файл помощи, открываем его (если не открыт) и переходим на закладку 'Index':

    L1InvokeHelpMacro('Search()', True);

Ищем ассоциированный файл помощи, открываем его (если не открыт) и переходим на закладку 'Contents':

    L1InvokeHelpMacro('Contents()', True);

Ищем ассоциированный файл помощи, открываем его (если не открыт) и переходим на закладку 'Find' (только для WinHelp 4):

    L1InvokeHelpMacro('Find()', True);

[000127]



Показ диалога "Help Search" I


    Application.HelpCommand(HELP_PARTIALKEY, 0);

Если данная команда не находит идентификатор #0 файла помощи (естественно, мы его и задаем), то она выводит диалог "Help Search". [000524]



Показ диалога "Help Search" II


Следующий код демонстрирует способ вывода диалога WinHelp "Search" для электронной справки вашего приложения. Для этого следует послать системе электронной справки Windows (WinHelp) команду Help_PartialKey, что можно сделать с помощью метода объекта TApplication HelpCommand. Параметр для этой команды должен иметь тип PChar (можно привести к longint) и содержать строку, которую вам необходимо найти. Пример ниже использует для вызова диалога "Search" пустую строку, которую освобождает после его закрытия.

    procedure TForm1.SearchHelp;
var
P: PChar; begin
Application.HelpFile := 'c:\delphi\bin\delphi.hlp'; P := StrNew(''); Application.HelpCommand(Help_PartialKey, longint(P)); StrDispose(P); end;

[000590]



Закрытие файла помощи


При закрытии моего приложения окно помощи (если оно в это время открыто) автоматически не закрывается! Как мне сделать так, чтобы оно также закрывалось автоматически?

Попробуйте так:

    Application.HelpCommand(HELP_QUIT, 0);

- Nick Hodges [000879]



Delphi


Сайты, посвященные DelphiАнглоязычные

N Сервер Описание
1 http://sunsite.icm.edu.pl/delphi
Delphi Super Page
Самая большая в мире коллекция компонентов для всех продуктов фирмы Borland.



Download


Рассылка

B
ероятно вы хотели бы иметь у себя самую последнюю версия "Советов по Delphi"? Нет ничего проще. Выберите подходящий способ, отправьте мне письмо и не забудьте указать Ваш email, ФИО и город проживания (исключительно для статистики). Вам будет посылаться раз в месяц небольшой файл (около 150Кб), который позволит установить справочную систему через Интернет.

Вам необходимо:

Подписаться на ежемесячную рассылку? (посылается небольшой файл setup.exe, производящий инсталляцию с сервера).

Прекратить подписку?

O
пределитесь
и нажмите на соответствующую ссылку.




Home


Домашние страницыРусскоязычные

N Сервер Описание
1 http://www.ph.usmga.ru/lexa
Delphi Russian Suite
Сайт, посвященный программированию на Дельфи. Автор страницы Алексей Спицын. Можно скачать документацию по Дельфи (в том числе и на русском языке). Есть список ссылок на страницы в интернете, посвященные Дельфи, а также на адреса электронных версий компьютерных журналов.



анный раздел содержит справочную информацию.


BODY {font-family:verdana,helvetica,georgia,sans-serif; color:#000; font-size=10pt} A:link, A:visited {text-decoration: none; color: #0000FF;} A:hover {text-decoration: underline; color: #FF0000;} HR {width=10%; color=red; text-align: left} Индексы
Д
анный раздел содержит справочную информацию. Как и сами "Советы", он разбит на темы и перечисляет содержащиеся в них сами советы и их количество. По причине большого количества советов, плохих телефонных линий в России, проблематичности большинства пользователей сидеть в Интернете без оглядки на часы, да и просто ввиду непредназначенности Интернета для публикования справочных систем, "Советы по Delphi" не предусматривают online-версии и существуют в виде скомпиллированного файла с удобной системой инсталляции.
Д
анная страничка позволяет ознакомится с содержанием "Советов". Для себя Вы можете выяснить вопрос о необходимости справочной системы на вашем компьютере и, может быть, найдете именно то, что так долго искали. Вся нумерация относительна и применяется автором лишь для удобства компоновки. Индексы содержат полное оглавление всех советов и обновляются по мере выхода новых версий. Новые советы отмечены красным цветом.
[ Алгоритмы ][ Преобразования ]

[000001] HEX -> Integer [000002] Преобразование десятичного числа в шестнадцатиричное [000003] Преобразование ASCII в шестнадцатиричное представление [000004] Преобразование двоичного числа в десятичное [000005] Преобразование ICO в BMP [000006] Unix-строки (чтение и запись Unix-файлов) [000007] Преобразование BMP в JPEG в Delphi 3 [000008] Декомпилляция звукового файла формата Wave и получение звуковых данных

[ Алгоритмы ][ Даты ]

[000009] Вычисление даты Пасхи [000010] Дни недели [000011] Формат даты [000012] Функция DateSer

[ Алгоритмы ][ Разное ]

[000013] Ханойская башня [000014] Аглоритм (уравнение) для определения восхода/захода солнца и луны (BASIC) [000015] Автоматический формат даты в компоненте Edit

[ Win API ][ Переменные среды ]

[000016] Получение переменных DOS [000017] Изменение системного времени из Delphi

[ Win API ][ Завершение работы Windows ]

[000018] События, происходящие в приложениях Delphi при завершении работы Windows [000019] Завершение работы Windows

[ Win API ][ Режим энергосбережения (Power saving) ]

[000020] Управление монитором

[ Win API ][ Разное ]

[000021] Извлечение из EXE-файла иконки и рисование ее в TImage [000022] Как не допустить запуск второй копии программы? [000023] Получение имени модуля [000024] Каким образом, программным путем, можно узнать о завершении запущенной программы?

[ Паскаль ][ Массивы ]

[000025] Массив в Delphi [000026] Динамические массивы

[ Базы данных ][ Создание ]

[000027] Создание db-файла во время работы приложения

[ Базы данных ][ Доступ ]

[000028] Очень медленный доступ к таблице при первом обращении

[ Базы данных ][ Поиск ]

[000029] Поиск величины при вводе [000030] Быстрый поиск в базах данных

[ Базы данных ][ Калькуляция ]

[000031] Хитрость OnCalcFields

[ Базы данных ][ dBASE ]

[000063] Таблицы dBASE: Структура .DBF-файла

[ Базы данных ][ Разное ]

[000032] Сканирование версии структуры базы данных [000033] Перемещение таблиц [000034] Прокрутка таблицы: хитрость PeekMessage()

[ BDE ][ Псевдонимы ]

[000035] Задание псевдонима программным путем [000036] Информация о псевдонимах BDE

[ Мультимедиа ][ Аудио-компакт ]

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

[ Аппаратное обеспечение ][ CD-ROM ]

[000038] Открытие и закрытие привода CD-ROM

[ Операционная система ][ Буфер обмена ]

[000039] Просмотр буфера обмена [000040] Копирование в буфер обмена [000041] Форма как графический объект

[ Компоненты ][ BitBtn ]

[000042] Смена иконки BitBtn во время работы приложения

[ Компоненты ][ DBGrid ]

[000064] Использование опции MultiSelect в DBGRID

[ Компоненты ][ Edit ]

[000043] Массив Edit-компонентов

[ Компоненты ][ Label ]

[000044] 3D-рамка для текстовых компонентов

[ Компоненты ][ ScrollBox ]

[000045] Синхронизация двух компонентов Scrollbox

[ Компоненты ][ Splitter ]

[000046] Конструирование Splitter

[ Компоненты ][ StatusBar ]

[000047] Обработчик события OwnerDraw в компоненте StatusBar

[ Компоненты ][ StringGrid ]

[000048] Установка атрибутов -=Только для чтения=- у столбцов компонента StringGrid [000049] Помещение изображения в ячейку StringGrid [000050] Сохранение и чтение Tstringgrid

[ Компоненты ][ TabbedNotebook ]

[000051] Добавление элементов управления в TTabbedNotebook и TNotebook [000052] Недоступная закладка в компоненте Tabbednotebook

[ Компоненты ][ Table ]

[000053] Создание компонента TTable без формы

[ Компоненты ][ TreeView ]

[000054] Ускорение работы TreeView

[ Компоненты ][ Разное ]

[000055] Массив компонентов... [000056] Получение индекса компонента в списке родителя [000057] Создание компонента во время работы приложения [000058] Дублирование компонентов и их потомков во время выполнения приложения

[ События ][ Создание ]

[000059] Создание события во время выполнения приложения

[ События ][ Задержка выполнения ]

[000060] Задержка выполнения OnChange (Delphi 2)

[ Миграция ][ Delphi3 ]

[000061] Получение констант с определением ошибки функцией LoadStr

[ Ошибки ][ Delphi2 ]

[000062] Ошибка в руководстве "Getting Started" на странице 42


Inprise


Разработчик DelphiРусскоязычные

N Сервер Описание
1 http://www.inprise.ru
Российское представительство Inprise
Фирма - разработчик Delphi



Inprisea


Разработчик DelphiАнглоязычные

N Сервер Описание
1 http://www.inprise.com
Inprise
Фирма - разработчик Delphi



Intro


BODY {font-family:verdana,helvetica,georgia,sans-serif; color:#000; font-size=10pt} A:link, A:visited {text-decoration: none; color: #0000FF;} A:hover {text-decoration: underline; color: #FF0000;} HR {width=10%; color=red; text-align: left} Что такое "Советы по Delphi"?

"
Советы по Delphi" - коллекция ответов на нетрадиционные вопросы программирования на Delphi, нестандартных решений, хитростей и интересных идей. Для практической пользы дела приведены конкретные примеры кода, позволяющие донести идею или полностью ответить на заданный вопрос.

A
втором предусматривается попытка на периодичность издания, подробности получения новых версий смотрите на страничке "Получение". При составлении "Советов" не ставилась цель включить ВСЕ материалы, отбирались лишь самые интересные. Источником "Советов" служили многочисленные западные источники (FAQ), кропотливо отобранные и переведенные на русский язык.

У
читывая плачевное состояние наших линий, "Советы" практически не содержат графики. Весь приведенный код отформатирован таким образом, чтобы вы могли скопировать его прямо со странички в свое приложение. По этой же причине отсутствует online-версия "Советов". Тем не менее, оглавление советов приведены на страничке "Индексы" (дабы не оказался кот в мешке).

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

Ш
лите примеры, советы, полезности, статьи и давайте ссылки на свои и не свои сайты. От вас самих зависит наполняемость советов. Авторы! Дайте вторую жизнь вашим произведениям! Присылайте статьи и переводы!

Предупреждение

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

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

Объявление

A
втор ищет возможность размещения домашней странички "Советов" с возможностью размещения на ней самих "Советов". Желателен канал не меньше 64К и возможность работы по FTP. Страничка небольшая, но "Советы" - растущий файл, который трудно разместить, а тем более скачать с бесплатных серверов. Буду благодарен за предоставленную возможность.



License


Лицензионное соглашение

И
спользование Вами любой версии Советов по Delphi указывает на то, что Вы принимаете все условия данного лицензионного соглашения:

Предоставление лицензии

B
алентин Озеров предоставил Вам ограниченную лицензию на "Советы по Delphi". Она НЕ ВКЛЮЧАЕТ В СЕБЯ лицензию на изменение, транслирование, перепроектирование, декомпилирование, дизассемблирование (за исключением случаев, когда соответствующие законы специально запрещают такое ограничение), или создание других программных продуктов, основанных на этом.

Особые условия

Ограничение на обратное конструирование, декомпиляцию и дизассемблирование.

В
ы не имеете права предпринимать обратное конструирование, декомпиляцию или дизассемблирование "Советов по Delphi" за исключением и только в той степени, в которой такие действия явно разрешены действующими законами в изъятие из данного положения.

T
ак как все программное обеспечение бесплатно, компания Mechanical Result и автор не несут ответственности за любые последствия использования данного программного обеспечения на вашем компьютере. Советы по Delphi распространяется по принципу "КАК ЕСТЬ", т.е. без гарантий любого вида, включая отсутствие гарантии на соответствие указанным задачам и отсутствие сбоев. Весь риск относительно качества и эффективности программного обеспечения лежит на Вас. Если использование данной программы приведет к проблемам, Вы принимаете на себя всю стоимость любого обслуживания и ремонта.

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

Распространение

В
ы свободны в распространении Советов по Delphi на любом общественном (открытом) Web/FTP/Gopher сервере без обязательного получения разрешения автора, пока это распространение проводится АБСОЛЮТНО БЕСПЛАТНО. Если Вы хотите издать данный продукт на или совместно с любым носителем (например, на компакт диске с журналом или книгой, на любой коллекции программного обеспечения, совместно с другим программным обеспечением или любым другим дистрибутивным пакетом), требуется разрешение автора. Обычно разрешение может быть получено без проблем - свободно, за исключением некоторых особых случаев.

Помните

В
алентин Озеров - единственый человек, который может дать разрешение на коммерческое использование и распространение справочной системы Советы по Delphi.

E
ЕСЛИ ВЫ НЕ ПОНИМАЕТЕ КАКОЙ-ЛИБО ЧАСТИ ДАННОГО ЛИЦЕНЗИОННОГО СОГЛАШЕНИЯ ИЛИ ВСЕГО СОГЛАШЕНИЯ В ЦЕЛОМ, СВЯЖИТЕСЬ СО МНОЙ ПО ЭЛЕКТРОННОЙ ПОЧТЕ ИЛИ ЛЮБЫМ ДРУГИМ СПОСОБОМ, ЧТОБЫ Я МОГ ОБЪЯСНИТЬ ИХ ВАМ.



Price



Цены

Услуга Стоимость
Справочная система "Советы по Delphi" Бесплатно
Размещение монопольного банера на титульной странице в течение месяца На правах обмена
Прием советов Бесплатно



Warez


Пиратские сайтыРусскоязычные

N Сервер Описание
1 http://delphi4.da.ru
KiraSoft Warez Page
Delphi distr, DB Aware, Communication, General, Reports, Experts, Misc, Tools, Links
2 www.audit.kharkov.com/barry/Delphi
Barry's Delphi Page
VCL's, VCL packs, Help Design, FAQ's, IB DataBase, Utilities, Forum



Warezo


Пиратские сайтыДругие

N Сервер Описание
1 http://www.netease.com/~dce
Delphi Components Crack (with Dr.Dai)
Как там написано, "Following components ONLY for Delphi 3.0 ", остальное понять трудно, как и идентифицировать родной язык автора сайта, хотя по скриншотам разобраться что и где лежит вполне можно



% Ресурсов, в режиме редактирования


Если у вас открыты все формы (показаны или минимизированы), а в редакторе кода открыты все модули, ресурсы очень быстро исчерпываются. Попробуйте закрыть все формы и модули, и открыть только те, которыми вы будете пользоваться. В противном случае при компиляции вы можете завесить Delphi и саму машину. [001432]



Активизация и использование в IDE окна CPU


Предупреждение: Окно CPU еще до конца не оттестировано и может иногда приводить к ошибкам. Если у вас есть проблемы с отладчиком, или при запуске вашей программы вы не можете им воспользоваться, окно CPU может помочь решить ваши проблемы. Обычно его не требуется включать, если только у вас не "особый случай".

В Delphi 2 эта характеристика встроена, но по умолчанию выключена, называется это окно CPU window, или DisassemblyView. Она легка в использовании, может быть полезной в отладке и сравнении кода при его оптимизации.

Для активизации этой характеристики, запустите REGEDIT и отредактируйте регистры описанным ниже образом. Найдите ключ HKEY_CURRENT_USER\Software\Borland\Delphi\2.0\Debugging. Создайте по этому пути строковый ключ с именем "ENABLECPU". Значение нового ключа должно быть строкой "1". Это все. Теперь в Delphi IDE появился новый пункт меню View|CPUWindow. При его активизации выводится новое окно.

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

Создайте 2 одинаковых обработчика события. В каждом обработчике события разместите приведенный ниже код. Установите точку прерывания на первой строчке каждого обработчика. Запустите приложение и активизируйте события. Сравните ассемблерный код обоих методов. Один короче? В этом случае он будет исполняться быстрее.

Достойными для такого рода анализа могут быть участки кода, многократно выполняемые в процессе работы программы, или критические ко времени выполнения.

Хорошим примером, где различный код выполняет одну и ту же работу, но делает это с разной скоростью, является использование конструкции "with object do". Исходный код с многократным использованием конструкции "with object do" будет длиннее, но ассемблерный код короче. Вспомните, сколько раз вы устанавливали свойства для динамически создаваемых объектов? Код:

    with TObject.create do
begin

property1 := ; property2 := ; property3 := ; end;

будет выполняться быстрее, чем

    MyObj := TObject.create;
MyObj.Property1 := ;
MyObj.Property2 := ;
MyObj.Property3 := ;

[001434]



Дата компилляции


Очень удобно, когда в диалоговом окне "О программе" можно узнать номер версии программы. Было бы удобным также использование даты и времени компиляции.

Я догадываюсь, что нечто подобное вы уже пробовали сделать, но поковырявшись некоторое время с этой проблемой, я пришел к выводу, что корректней всего производить вывод данной информации при запуске программы из коммандной строки, для чего я создал файл "today.inc". Пример работы вы можете увидеть у большинства DOS-программ (вначале выводится информация о номере версии и дате компиляции).

Для этого я создал файл "today.inc", где я храню необходимую информацию о времени компиляции приложения:

    const
_day   : string[10] = 'Понедельник'; _date  : word = 12; _month : word = 8; _year  : word = 1996;

Затем просто вставьте строчку {$I c:\today.inc} в верхную части вашего кода.

Досадно, что компилятор не поддерживает директивы {$DATE}!

[000293]

Попробуй сделать


Nomadic отвечает:

A: (AlPe): Попробуй сделать в [HKLM\Software\Microsoft\Windows NT\CurrentVersion\FontMapper] DEFAULT=0xcc (204) вместо 0x00 (Именно DEFAULT, а не (Default) :-)
получше маленько будет... [001488]


Где находится опция "Break on Exception" в Delphi 4?


Олегом Кулабухов отвечает:

Я тоже сначала искал. Нашел таки.

Tools->Debugger Options->Language Exceptions->Stop on Delphi Exceptions [001904]



IDE: шрифт по умолчанию


Если вы хотите использовать шрифт, оличный от шрифта Delphi "по умолчанию", добавьте строку типа:

DefaultFont=Arial, 9

в секцию FormDesign файла Delphi.ini. [000456]



Имитация Delphi IDE


Во-первых, необходимо проверить, загружена ли Delphi - используйте FindWindow для поиска окна с именем класса TAppBuilder. Если оно загружено, FindWindow вернет вам дескриптор. Если нет, выполняйте WinExec и снова используйте FindWindow для получения дескриптора.

Для получения доступа к главному меню Delphi используйте GetMenu. "Пробегитесь" по дереву меню до тех пор, пока не найдете желаемый пункт. После получения ID пункта меню, вы можете посылать сообщение WM_COMMAND, дающее тот же эффект, что и нажатие на этот пункт. Ниже приведен пример вышесказанного. Для его создания, расположите на новой форме панель, выровняйте ее по верху (свойство Align = alTop) и разместите на ней кнопку. Расположите на форме компонент outline и установите его свойство Align в alClient. Поместите на форме компонент OpenDialog и настройте его на открытие файлов с расширением .EXE. Затем используйте код, приведенный ниже. Данный пример запустит определенную вами программу и отобразит структуру ее меню в outline. Располагая идентификаторами пунктов (IDs) меню чужой программы (в нашем случае Delphi) в компоненте outline, вы запросто можете управлять этой программой. Или, что еще интереснее, вы можете "пробежаться" по меню, используя те же методы, и найти нужный ID во время выполнения программы. Кто знает, может новая версия Delphi будет содержать другие пункты (ID) меню.

    ... private { Private declarations } InstHandle : Word; WndHandle : hWnd; function EnumFunc(H : HWnd) : Word; ... implementation USES ShellApi;
{$R *.DFM}

function TForm1.EnumFunc(H : HWnd) : Word;
BEGIN
IF GetWindowWord(H, GWW_HINSTANCE) = InstHandle THEN BEGIN WndHandle := H; Result := 0; END ELSE Result := 1; END;

procedure TForm1.Button1Click(Sender: TObject);
VAR
Cmd : ARRAY[0..255] OF Char;
procedure AddChildMenus(Loc, Han : Integer); VAR MText : ARRAY[0..255] OF Char; N : Integer; ID : Word; NuLoc : Integer; BEGIN FOR N := 0 TO GetMenuItemCount(Han)-1 DO BEGIN Id := GetMenuItemID(Han, N); GetMenuString(Han, N, MText, 255, MF_BYPOSITION); IF ID = $FFFF THEN BEGIN NuLoc := Outline1.AddChild(Loc, StrPas(MText)); AddChildMenus(NuLoc, GetSubMenu(Han, N)); END ELSE Outline1.AddChild(Loc, StrPas(MText) + ' {' + IntToStr(Id) +'}'); END; END; begin
WITH OpenDialog1 DO IF Execute THEN BEGIN StrPCopy(Cmd, Filename); InstHandle := WinExec(Cmd, SW_SHOW); WndHandle := 0; IF InstHandle >= 32 THEN EnumWindows(@TForm1.EnumFunc, LongInt(Self)); IF WndHandle <> 0 THEN BEGIN SetWindowText(WndHandle, 'Title changed by KNOWEXEC'); Outline1.Clear; Outline1.Add(0, 'menu'); AddChildMenus(1, GetMenu(WndHandle)); END; END; end;

[000650]



Использование Tools Interface


...я все еще ищу *крутой* способ отрисовки содержимого окна редактирования IDE, и уже добрался до списка дескрипторов окон. Я так понял, что для этого нужно использовать инструментальный интерфейс (Tools Interface), только c помощью него, да? Ну и как этим чудом воспользоваться?

Приведенный ниже код может использоваться для включения заголовка исходного кода, представляющего собой шапку с информацией об авторских правах, авторе, версии и пр. при добавлении нового модуля или формы к вашему проекту. TIAddInNotifier - класс, реализованный в ToolIntf и позволяющий "захватывать" такие события, как открытие файлов, их закрытие, открытие и закрытие проекта и др. Я перекрыл процедуру FileNotification для захвата событий AddedToProject и RemovedFromProject. В обработчике события AddedToProject вы можете получить доступ к новому модулю проекта, особенно это касается процедуры InsertHeader. Я создал наследника класса TIEditorInterface, расположенного в файле EditIntf.pas, и создал собственную процедуру InsertHeader.

VCSNotifier создается в другом модуле и здесь не показан. Приведенный ниже код является частью моей программы, осуществляющей контроль версий dll. При создании код "живет" до тех пор, пока работает Delphi. При получении кода AddedToProject, я проверяю наличие файла (должен быть новым), и что он является .pas-файлом. Затем я создаю VCSEditorInterface, мой унаследованный интерфейс, и использую мою процедуру InsertHeader.

В самой процедуре InsertHeader я создаю экземпляр TIEditReader для чтения нового модуля и TIEditWriter для его изменения.

    unit VCSNtfy;

interface

uses
SysUtils, Dialogs, Controls, ToolIntf, EditIntf;

type
TIVCSNotifier = class( TIAddInNotifier ) public procedure FileNotification(NotifyCode: TFileNotification; const FileName: string; var Cancel: Boolean ); override; end;

TIVCSEditorInterface = class( TIEditorInterface ) public procedure InsertHeader; end;
var
VCSNotifier : TIVCSNotifier; VCSModuleInterface : TIModuleInterface; VCSEditorInterface : TIVCSEditorInterface;
implementation

uses
FITIntf, FITStr, Classes;

{ *************************   Начало VCSNotifier  **************************** }

procedure  TIVCSNotifier.FileNotification( NotifyCode: TFileNotification; const
FileName: string; var Cancel : Boolean ); var
TmpFileName : string;
begin
case
NotifyCode of fnRemovedFromProject: VCSProject.Remove( LowerCase( ExtractFileName( FileName ))); fnAddedToProject: begin if ( not FileExists( FileName ) ) and ( ExtractFileExt( FileName ) = '.pas' ) then begin { новый файл с исходным кодом } VCSModuleInterface := ToolServices.GetModuleInterface( FileName ); if VCSModuleInterface <> nil then begin VCSEditorInterface := TIVCSEditorInterface( VCSModuleInterface.GetEditorInterface ); VCSEditorInterface.InsertHeader; VCSEditorInterface.Free; end; VCSModuleInterface.Free; end;
TmpFileName := LowerCase( ExtractFileName( FileName )); if VCSProject.RecycleExists( TmpFileName ) then begin if MessageDlg( 'Вы хотите извлечь текущие ' + ' записи из таблицы Recycle' + #13 + #10 + '           ' + VCSProject.ProjectName + '/' + TmpFileName + '?', mtConfirmation, [mbYes,mbNo], 0 ) = mrYes then begin VCSProject.Recycle( TmpFileName ); end; end; end; end; end;

{ *************************    Конец TIVCSNotifier   *************************** }

{ *********************   Начало TIVCSEditorInterface  ************************ }

procedure TIVCSEditorInterface.InsertHeader;
var
Module, TmpFileName, UnitName, InsertText, Tmp : string; Reader : TIEditReader; Writer : TIEditWriter; APos : Integer; F : TextFile; begin

TmpFileName := ExtractFileName( FileName ); UnitName := SwapStr( TmpFileName, '.pas', '' );
SetLength( Module, 255); Reader := CreateReader; try Reader.GetText( 0, PChar( Module ), Length( Module )); finally Reader.Free; end;
APos := Pos( 'unit ' + UnitName, Module ); if APos > 0 then begin try InsertText := ''; AssignFile( F, VCSConfig.HeaderFileLocation ); Reset( F ); while not EOF(F) do begin Readln( F, Tmp ); InsertText := InsertText + #13 + #10 + Tmp; end; CloseFile(F);
InsertText := InsertText + #13 + #10;
Writer := CreateWriter; try Writer.CopyTo( APos - 1 ); Writer.Insert( PChar( InsertText )); finally Writer.Free; end; except On E : EStreamError do MessageDlg( 'Не могу создать шапку', mtInformation, [mbOK], 0 ); end; end;
end;
{ *********************   Конец TIVCSModuleInterface  ************************** }
end.

- Jim Poe [001061]



Как быстро открыть файл в IDE?


Олегом Кулабухов отвечает:

Просто наведите курсор на имя файла в редакторе и нажмите Ctrl-Enter. Это действует на все *.pas и *.h, включая находящиеся в uses и include. [001876]