Programming: Delphi
Советы программистов (Часть 1)
- Часть 2
- Часть 3
- Часть 4
- Преобразование арабских чисел в римские
- Преобразование в EBCDIC
- Добавление лидирующих символов
- Получение элемента даты
- Использование DateTime в DBGrid
- Управление битами
- Вращение изображения
- Защита программ перекрытием кода
- Пример защиты типа SHAREWARE
- Перекодировка текста из DOS в Windows и наоборот
- Сумма прописью
- Правильное округление дробных чисел
- Паскалевский эквивалент StrTok
- Как получить указатели всех процессов, запущенных в системе
- Список запущенных приложений
- Как запустить другую программу
- Как предотвратить запуск копии приложения
- Контроль завершения приложения
- Управление завершением работы Windows
- Директивы компилятора, способные увеличить скорость
- Создание редактора свойства
- Вызов процедуры, имя которой содержится в переменной
- Передача функции как параметра
- Переменная в качестве имени процедуры
- Массивы размером более 64К
Преобразование арабских чисел в римские
Как преобразовать арабские числа в римские?
Функция получает в качестве параметра любую десятичную величину и возвращает результат в виде строки, содержащей римские цифры.
function TForm1.DecToRoman(Decimal: Integer): String;
const
Romans: array[1..13] of String = ('I', 'IV', 'V', 'IX', 'X', 'XL', 'L',
'XC', 'C', 'CD', 'D', 'CM', 'M');
Arabics: array[1..13] of Integer = (1, 4, 5, 9, 10, 40, 50,
90, 100, 400, 500, 900, 1000);
var
i: Integer;
begin
result := '';
for i := 13 downto 1 do
while (Decimal >= Arabics[i]) do begin
Decimal := Decimal - Arabics[i];
result := result + Romans[i];
end;
end;
Преобразование в EBCDIC
Как перекодировать строку?
Функция конвертирует любую строку. Можете доработать ее, для того чтобы она могла преобразовывать другие типы данных. Но если вам нужны дополнительные преобразования и обработка данных, то стоит задуматься о приобретении специализированного программного обеспечения...
const
a2e: array [0..255] of byte =
(000, 001, 002, 003, 055, 045, 046, 047, 022, 005, 037, 011, 012, 013, 014, 159,
016, 017, 018, 019, 182, 181, 050, 038, 024, 025, 063, 039, 028, 029, 030, 031,
064, 090, 127, 123, 091, 108, 080, 125, 077, 093, 092, 078, 107, 096, 075, 097,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 122, 094, 076, 126, 110, 111,
124, 193, 194, 195, 196, 197, 198, 199, 200, 201, 209, 210, 211, 212, 213, 214,
215, 216, 217, 226, 227, 228, 229, 230, 231, 232, 233, 173, 224, 189, 095, 109,
121, 129, 130, 131, 132, 133, 134, 135, 136, 137, 145, 146, 147, 148, 149, 150,
151, 152, 153, 162, 163, 164, 165, 166, 167, 168, 169, 192, 106, 208, 161, 007,
104, 220, 081, 066, 067, 068, 071, 072, 082, 083, 084, 087, 086, 088, 099, 103,
113, 156, 158, 203, 204, 205, 219, 221, 224, 236, 252, 176, 177, 178, 062, 180,
069, 085, 206, 222, 073, 105, 154, 155, 171, 015, 186, 184, 183, 170, 138, 139,
060, 061, 098, 079, 100, 101, 102, 032, 033, 034, 112, 035, 114, 115, 116, 190,
118, 119, 120, 128, 036, 021, 140, 141, 142, 065, 006, 023, 040, 041, 157, 042,
043, 044, 009, 010, 172, 074, 174, 175, 027, 048, 049, 250, 026, 051, 052, 053,
054, 089, 008, 056, 188, 057, 160, 191, 202, 058, 254, 059, 004, 207, 218, 020,
225, 143, 070, 117, 253, 235, 238, 237, 144, 239, 179, 251, 185, 234, 187, 255);
procedure StringA2E(var StringToConvert: String);
var
Loop: integer;
begin
for Loop := 1 to Length(StringToConvert) do
StringToConvert[Loop] := Char(a2e[Ord(StringToConvert[Loop])]);
end;
Добавление лидирующих символов
Как в начало строки вставить символ? Количество вставляемых символов может быть различным.
Если необходимо в начало строки вставить определенный символ, например, преобразовать «1010» в «0001010», воспользуйтесь следующей функцией:
function PadL(s_InStr: string; i_Wide: integer; c_Chr: char): string;
begin
while Length(s_InStr) < i_Wide do s_InStr := c_Chr + s_InStr;
Result := s_InStr;
end;
Получение элемента даты
Как из даты выделить нужный элемент?
Используйте универсальную функцию возврата значения элемента даты (год, месяц, день, квартал):
function RetDate(inDate: TDateTime; inTip: integer): integer;
var
xYear, xMonth, xDay: word;
begin
Result := 0;
DecodeDate(inDate, xYear, xMonth, xDay);
case inTip of
1: Result := xYear; // год
2: Result := xMonth; // месяц
3: Result := xDay; // день
4: if xMonth < 4 then Result := 1 // квартал
else if xMonth < 7 then Result := 2
else if xMonth < 10 then Result := 3
else Result := 4;
end;
end;
[Галимарзанов Фанис]
Использование DateTime в DBGrid
При отображении TDateTimeField в DBGrid с форматированием hh:mm (для показа только времени), любая попытка изменения времени приводит (при передаче данных) к ошибке примерно такого содержания: «'07:00' is not a valid DateTime». Как переслать данные в виде: «Trunc(oldDateTimevalue) + StrToTime(displaytext)»?
Следующий обработчик событий – TDateTimeField.OnSetText – не слишком элегантен, но он работает.
Предположим, что имеется маска редактирования, допускающая формат hh:mm или hh:mm:ss. Тогда процедура будет иметь следующий вид:
procedure TForm1.Table1Date1SetText(Sender: TField; const Text: String);
var
d: TDateTime;
t: string;
begin
t := Text;
with Sender as TDateTimeField do begin
if IsNull then d := SysUtils.Date
else d := AsDateTime;
AsDateTime := StrToDateTime(Copy(DateToStr(d), 1, 10) + ' ' + t);
end;
end;
Примечание
Функция Copy как раз и формирует постоянную дату (в формате dd/mm/yyyy), которая автоматически вводится в поле, t – вводимое время.
[News Group]
Управление битами
Как получить доступ к битам переменной и управлять их значением?
Решение 1
unit Bitwise;
interface
function IsBitSet(const val: longint; const TheBit: byte): boolean;
function BitOn(const val: longint; const TheBit: byte): LongInt;
function BitOff(const val: longint; const TheBit: byte): LongInt;
function BitToggle(const val: longint; const TheBit: byte): LongInt;
implementation
function IsBitSet(const val: longint; const TheBit: byte): boolean;
begin
result := (val and (1 shl TheBit)) <> 0;
end;
function BitOn(const val: longint; const TheBit: byte): LongInt;
begin
result := val or (1 shl TheBit);
end;
function BitOff(const val: longint; const TheBit: byte): LongInt;
begin
result := val and ((1 shl TheBit) xor $FFFFFFFF);
end;
function BitToggle(const val: longint; const TheBit: byte): LongInt;
begin
result := val xor (1 shl TheBit);
end;
end.
Решение 2
SetWord – слово, которое необходимо установить. BitNum – номер бита, который необходимо выставить согласно определениям в секции const (Bit0, Bit1 и др.). GetBitStat возвращает значение True, если бит установлен и False – в противном случае.
const
Bit0 = 1;
Bit1 = 2;
Bit2 = 4;
Bit3 = 8;
Bit4 = 16;
Bit5 = 32;
Bit6 = 64;
Bit7 = 128;
Bit8 = 256;
Bit9 = 512;
Bit10 = 1024;
Bit11 = 2048;
Bit12 = 4096;
Bit13 = 8192;
Bit14 = 16384;
Bit15 = 32768;
procedure SetBit(SetWord, BitNum: Word);
begin
SetWord := SetWord Or BitNum; { Устанавливаем бит }
end;
procedure ClearBit(SetWord, BitNum: Word);
begin
SetWord := SetWord Or BitNum; { Устанавливаем бит }
SetWord := SetWord Xor BitNum; { Переключаем бит }
end;
procedure ToggleBit(SetWord, BitNum: Word);
begin
SetWord := SetWord Xor BitNum; { Переключаем бит }
end;
function GetBitStat(SetWord, BitNum: Word): Boolean;
begin
GetBitStat := SetWord and BitNum = BitNum; { Если бит установлен }
end;
Вращение изображения
С помощью предлагаемого программного кода реализуется быстрый и примитивный способ вращения изображения. По крайней мере, это тоже выход из положения, поскольку Windows этого делать не умеет.
procedure RotateRight(BitMap: TImage);
var
FirstC, LastC, c, r: integer;
procedure FixPixels(c, r: integer);
var
SavePix, SavePix2: tColor;
i, NewC, NewR: integer;
begin
SavePix := Bitmap.Canvas.Pixels[c, r];
for i := 1 to 4 do begin
Newc := BitMap.Height - r + 1;
Newr := c;
SavePix2 := BitMap.Canvas.Pixels[Newc, Newr];
Bitmap.Canvas.Pixels[Newc, Newr] := SavePix;
SavePix := SavePix2;
c := NewC;
r := NewR;
end;
end;
begin
if BitMap.Width <> BitMap.Height then exit;
BitMap.Visible := False;
with Bitmap.Canvas do begin
FirstС := 0;
LastС := BitMap.Width;
for r := 0 to BitMap.Height div 2 do begin
for c := FirstС to LastС do FixPixels(c, r);
Inc(FirstC);
Dec(LastC);
end;
end;
BitMap.Visible := True;
end;
[News Group]
Примечание
Вращение происходит на 90 градусов вправо за одно выполнение процедуры. Не забудьте добавить компонент TImage на форму, загрузить изображение и передать TImage в качестве параметра в процедуру вращения.
Защита программ перекрытием кода
Не секрет, что совершенной защиты не существует. Тем не менее, хорошая защита должна обеспечить такой уровень, чтобы на ее вскрытие нужно было затратить усилия сравнимые, с самостоятельным написанием программы. Разумеется, она должна быть многоуровневой и перекрывающейся (уровни должны работать независимо). Не забывайте, что хорошие взломщики неплохо знают Ассемблер, и высокоуровневые ухищрения от них не спасают. Следовательно, для построения высококлассной защиты с использованием Ассемблера, необходимо владеть последним в совершенстве. Не думайте, что вам это не подходит, т. к. слишком сложно или уже не модно. Хороший программист не пренебрегает ассемблером и высшей математикой.
Один из методов – это перекрывающийся код. Он может показаться немного сложным для большинства из нас, но, зная несколько HEX значений инструкций процессора, вы тоже сможете создать небольшой по размеру перекрывающийся код. Перекрывающийся код можно сделать сколь угодно многоуровневым, а здесь я покажу лишь, в каком направлении надо «копать».
temp_string := 'Den is Com';
asm
mov ax, $05EB
@as: jmp @as-2
end;
ShowMessage('Сообщение');
На первый взгляд, это может озадачить, но на самом деле все очень просто. Первая инструкция заносит значение в AX. Вторая выполняет переход на значение операнда команды MOV. Код '05EB' переводится как 'JMP $+5' (помните, что слова хранятся в обратном порядке). Этот переход минует JMP и передает выполнение дальше. Вероятно, этого не будет достаточно для защиты, но технику ее создания демонстрирует.
Присваивание temp_string := 'Den is Com' существенной роли не играет, но может применяться при отладке программы, т. к. хорошо просматривается при использовании дизассемблера и отладчика. Возможно, ваши первые попытки будут приводить к частому зависанию компьютера, но не отчаивайтесь – защита того стоит. Попробуйте разработать свой способ сравнения строк (чаше всего ловятся именно эти инструкции), замаскируйте инструкции зависания компьютера и т.д.
[Den is Com]
Пример защиты типа SHAREWARE
В качестве примера приведен небольшой участок программного кода, позволяющий быстро создать защиту для программ SHAREWARE, которая, не влияет на функциональность самой программы, но настоятельно «просит» ее зарегистрировать и закрывает при каждом повторном запуске.
Технология данного метода заключается в том, что пользователь может запустить программу только один раз за текущий сеанс Windows.
Используйте обработчик события FormShow:
procedure TForm1.FormShow(Sender : TObject);
var
atom: integer;
CRLF: string;
begin
if GlobalFindAtom('THIS_IS_SOME_OBSCUREE_TEXT') = 0 then
atom := GlobalAddAtom('THIS_IS_SOME_OBSCUREE_TEXT')
else begin
CRLF := #10 + #13;
ShowMessage('Данная версия предусматривает только один запуск'
+ 'в текущем сеансе Windows.' + CRLF
+ 'Для повторного запуска необходимо перезапустить Windows, или,'
+ CRLF + 'что лучше, - ' + CRLF + 'ЗАРЕГИСТРИРУЙТЕСЬ !');
Close;
end;
end;
Преимущество данного метода в том, что пользователю доступны все возможности программы, но только до момента ее закрытия, или перезапуска системы. Вся хитрость заключается в сохранении некоторой строки в системных глобальных переменных («атомах») и последующей проверке ее в таблице «атомов» системы.
Перекодировка текста из DOS в Windows и наоборот
Как с помощью Delphi перекодировать текстовый файл из DOS в Windows и наоборот?
Решение 1
Src – строка для перекодировки, Str – перекодированная строка
procedure TForm1.WinToDos;
var
Src, Str: PChar;
begin
GetMem(Str, Length(Memo1.Lines.Text)); // Выделяем место под строку-приемник
Src := Memo1.Lines.GetText; // Берем текст из TMemo как PChar
CharToOem(Src, Str); // Перекодировка текста
Memo2.Lines.SetText(Str); // Передаем перекодированный текст
FreeMem(Str); // Освобождаем память
end;
procedure TForm1.DosToWin; // С точностью до вызова функции API
var // повторяем код
Src, Str: PChar;
begin
GetMem(Str, Length(Memo1.Lines.Text));
Src := Memo1.Lines.GetText;
OemToChar(Src, Str);
Memo2.Lines.SetText(Str);
FreeMem(Str);
end;
Решение 2
Используйте CharToOEM, OEMToChar, CharToOEMBuff, OEMToCharBuff.
[Nomadic]
Сумма прописью
Очень часто в финансовых приложениях сумму нужно писать прописью. Как сумму, представленную цифрой, преобразовать в строку прописью?
function TextSum(S: double): string;
function Conv999(M: longint; fm: integer): string;
const
c1to9m: array [1..9] of string[6] =
('один', 'два', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь','девять');
c1to9f: array [1..9] of string[6] = ('одна', 'две', 'три', 'четыре', 'пять',
'шесть', 'семь', 'восемь', 'девять');
c11to19: array [1..9] of string[12] =
('одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать',
'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать');
c10to90: array [1..9] of string[11] =
('десять', 'двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят',
'семьдесят', 'восемьдесят', 'девяносто');
c100to900: array [1..9] of string[9] =
('сто', 'двести', 'триста', 'четыреста', 'пятьсот',
'шестьсот', 'семьсот', 'восемьсот', 'девятьсот');
var
s: String;
i: Longint;
begin
s := '';
i := M div 100;
if I <> 0 then s := c100to900[i] + ' ';
M := M mod 100;
i := M div 10;
if (M > 10) and (M < 20) then
s := s + c11to19[M - 10] + ' '
else begin
if I <> 0 then s := s + c10to90[i] + ' ';
M := M mod 10;
if M <> 0 then
if fm = 0 then s := s + c1to9f[M] + ' '
else s := s + c1to9m[M] + ' ';
end;
Conv999 := s;
end;
var
i: Longint;
j: Longint;
r: Real;
t: String;
begin
t := '';
j := Trunc(S / 1000000000.0);
r := j;
r := S - r*1000000000.0;
i := Trunc(r);
if j <> 0 then begin
t := t + Conv999(j, 1) + 'миллиард';
j := j mod 100;
if (j > 10) and (j < 20) then t := t + 'ов '
else
case j mod 10 of
0: t := t + 'ов ';
1: t := t + ' ';
2..4: t := t + 'а ';
5..9: t := t + 'ов ';
end;
end;
j := i div 1000000;
if j <> 0 then begin
t := t + Conv999(j, 1) + 'миллион';
j := j mod 100;
if (j > 10) and (j < 20) then t := t + 'ов '
else
case j mod 10 of
0: t := t + 'ов ';
1: t := t + ' ';
2..4: t := t + 'а ';
5..9: t := t + 'ов ';
end;
end;
i := i mod 1000000;
j := i div 1000;
if j <> 0 then begin
t := t + Conv999(j, 0) + 'тысяч';
j := j mod 100;
if (j > 10) and (j < 20) then t := t + ' '
else
case j mod 10 of
0: t := t + ' ';
1: t := t + 'а ';
2..4: t := t + 'и ';
5..9: t := t + ' ';
end;
end;
i := i mod 1000;
j := i;
if j <> 0 then t := t + Conv999(j, 1);
t := t + 'руб. ';
i := Round(Frac(S)*100.0);
t := t + IntToStr(i) + ' коп.';
TextSum := t;
end;
[Александр]
Правильное округление дробных чисел
Как научить Delphi правильно округлять дробные числа?
function RoundEx(X: Double; Precision: Integer): Double;
{ Precision : 1 - до целых, 10 - до десятых, 100 - до сотых... }
var
ScaledFractPart, Temp: Double;
begin
ScaledFractPart := Frac(X) * Precision;
Temp := Frac(ScaledFractPart);
ScaledFractPart := Int(ScaledFractPart);
if Temp >= 0.5 then ScaledFractPart := ScaledFractPart + 1;
if Temp <= -0.5 then ScaledFractPart := ScaledFractPart - 1;
RoundEx := Int(X) + ScaledFractPart / Precision;
end;
[Nomadic]
Паскалевский эквивалент StrTok
Решение 1
function NextToken(P: PChar; Divider: PChar): PChar;
const
next: PChar = nil ;
begin
if P = nil then P := next;
if P <> nil then begin
next := StrPos(P, Divider);
if next <> nil then begin
next^ := #0;
next := @next[StrLen(Divider)];
end;
end;
NextToken := P;
end;
[News Group]
Решение 2
function StrTok(Phrase: PChar; Delimeter: PChar): PChar;
const
tokenPtr: PChar = nil;
workPtr: PChar = nil;
var
delimPtr: PChar;
begin
if (Phrase <> nil) then workPtr := Phrase
else workPtr := tokenPtr;
if workPtr = nil then begin
Result := nil;
Exit;
end;
delimPtr := StrPos(workPtr, Delimeter);
if (delimPtr <> nil) then begin
delimPtr^ := Chr(0);
tokenPtr := delimPtr + 1
end
else
tokenPtr := nil;
Result := workPtr;
end;
[News Group]
Как получить указатели всех процессов, запущенных в системе
Под Windows (Win32) это возможно с использованием вспомогательных информационных функций:
Вызывается функция:
hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
Process32First() – получение информации о первом процессе в списке;
Далее в цикле Process32Next() – получение информации о следующем процессе в списке.
unit KernlUtl;
interface
uses
TlHelp32, Windows, Classes, SysUtils;
procedure GetProcessList(List: TStrings);
procedure GetModuleList(List: TStrings);
function GetProcessHandle(ProcessID: DWORD): THandle;
procedure GetParentProcessInfo(var ID: DWORD; var Path: String);
const
PROCESS_TERMINATE = $0001;
PROCESS_CREATE_THREAD = $0002;
PROCESS_VM_OPERATION = $0008;
PROCESS_VM_READ = $0010;
PROCESS_VM_WRITE = $0020;
PROCESS_DUP_HANDLE = $0040;
PROCESS_CREATE_PROCESS = $0080;
PROCESS_SET_QUOTA = $0100;
PROCESS_SET_INFORMATION = $0200;
PROCESS_QUERY_INFORMATION = $0400;
PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or $0FFF;
implementation
procedure GetProcessList(List: TStrings);
var
I: Integer;
hSnapshoot: THandle;
pe32: TProcessEntry32;
begin
List.Clear;
hSnapshoot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshoot = -1) then Exit;
pe32.dwSize := SizeOf(TProcessEntry32);
if (Process32First(hSnapshoot, pe32)) then repeat
I := List.Add(Format('%x, %x: %s', [pe32.th32ProcessID,
pe32.th32ParentProcessID, pe32.szExeFile]));
List.Objects[I] := Pointer(pe32.th32ProcessID);
until not Process32Next(hSnapshoot, pe32);
CloseHandle (hSnapshoot);
end;
procedure GetModuleList(List: TStrings);
var
I: Integer;
hSnapshoot: THandle;
me32: TModuleEntry32;
begin
List.Clear;
hSnapshoot := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
if (hSnapshoot = -1) then Exit;
me32.dwSize := SizeOf(TModuleEntry32);
if (Module32First(hSnapshoot, me32)) then repeat
I := List.Add(me32.szModule);
List.Objects[I] := Pointer(me32.th32ModuleID);
until not Module32Next(hSnapshoot, me32);
CloseHandle (hSnapshoot);
end;
procedure GetParentProcessInfo(var ID: DWORD; var Path: String);
var
ProcessID: DWORD;
hSnapshoot: THandle;
pe32: TProcessEntry32;
begin
ProcessID := GetCurrentProcessId;
ID := 0;
Path := '';
hSnapshoot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshoot = -1) then Exit;
pe32.dwSize := SizeOf(TProcessEntry32);
if (Process32First(hSnapshoot, pe32)) then
repeat
if pe32.th32ProcessID = ProcessID then begin
ID := pe32.th32ParentProcessID;
Break;
end;
until not Process32Next(hSnapshoot, pe32);
if ID <> -1 then
if (Process32First(hSnapshoot, pe32)) then repeat
if pe32.th32ProcessID = ID then begin
Path := pe32.szExeFile;
Break;
end;
until not Process32Next(hSnapshoot, pe32);
CloseHandle (hSnapshoot);
end;
function GetProcessHandle(ProcessID: DWORD): THandle;
begin
Result := OpenProcess(PROCESS_ALL_ACCESS, True, ProcessID);
end;
end.
[Nomadic]
Список запущенных приложений
Решение
procedure TForm1.Button1Click(Sender: TObject);
var
Wnd: hWnd;
buff: array [0..127] of char;
begin
ListBox1.Clear;
Wnd := GetWindow(Handle, gw_HWndFirst);
while Wnd <> 0 do begin // Не показываем:
if (Wnd <> Application.Handle) // Собственное окно
and IsWindowVisible(Wnd) // Невидимые окна
and (GetWindow(Wnd, gw_Owner) = 0) // Дочерние окна
and (GetWindowText(Wnd, buff, sizeof(buff)) <> 0) then begin
GetWindowText(Wnd, buff, sizeof(buff));
ListBox1.Items.Add(StrPas(buff));
end;
Wnd := GetWindow(Wnd, gw_hWndNext);
end;
ListBox1.ItemIndex := 0;
end;
[Nikolaev Igor]
Как запустить другую программу
Для примера посмотрите в Delphi модуль FMXUTILS.PAS:
function ExecuteFile(const FileName, Params, DefaultDir: string;
ShowCmd: Integer): THandle;
var
zFileName, zParams, zDir: array[0..79] of Char;
begin
Result := ShellExecute(Application.MainForm.Handle, nil,
StrPCopy(zFileName, FileName), StrPCopy(zParams, Params),
StrPCopy(zDir, DefaultDir), ShowCmd);
end;
Пример вызова:
ExecuteFile('Notepad.exe', '', 'c:\windows', SW_SHOWNORMAL);
Примечание
Чтобы этот код заработал, необходимо добавить в uses модуль ShellAPI.
Как предотвратить запуск копии приложения
Можно использовать переменную Atom, полная информация о которой содержится в справочном руководстве по Delphi.
program Project1;
uses
Windows, Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
const
AtStr = 'MyProgram';
function CheckThis: boolean;
var
Atom: THandle;
begin
Atom := GlobalFindAtom(AtStr);
Result := Atom <> 0;
if not Result then GlobalAddAtom(AtStr);
end;
begin
if not CheckThis then begin // Запуск программмы
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
GlobalDeleteAtom(GlobalFindAtom(AtStr));
end else MessageBox(0, 'Нельзя запустить две копии программы', 'Error', 0);
end.
[Чумак Михаил]
Контроль завершения приложения
Решение
function WinExecAndWait32(FileName: String; Visibility: integer): DWORD;
var
zAppName: array[0..512] of char;
zCurDir: array[0..255] of char;
WorkDir: String;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
Pd: pointer;
begin
StrPCopy(zAppName, FileName);
GetDir(0, WorkDir);
StrPCopy(zCurDir, WorkDir);
FillChar(StartupInfo, Sizeof(StartupInfo), #0);
StartupInfo.cb := Sizeof(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := Visibility;
if not CreateProcess(nil, zAppName, nil, nil, false, CREATE_NEW_CONSOLE
or NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo) then Result := 0
else begin
WaitforSingleObject(ProcessInfo.hProcess, INFINITE);
GetExitCodeProcess(ProcessInfo.hProcess, Result);
end;
end;
В качестве дополнения внесем резонное исправление – вместо:WaitforSingleObject(ProcessInfo.hProcess, INFINITE);
лучше написать:
while WaitforSingleObject(ProcessInfo.hProcess, 200) = WAIT_TIMEOUT do
TForm1.Repaint;
Смысл замены: в первом варианте главное окно ждёт завершения вызванного сообщения, не обрабатывая при этом никаких событий. Вследствие этого, главное окно не перерисовывается, что выглядит далеко не лучшим образом. Последний вариант исправляет этот недостаток.
[Trubachev Pavel]
Управление завершением работы Windows
Как в одном компоненте реализовать выключение компьютера, его перезагрузку, завершение сеанса работы пользователя, функцию Eject CD, выключение питания монитора и т. д.?
Предлагаем рассмотреть следующий пример:
procedure TForm1.Button1Click(Sender: TObject);
begin
PowerControl1.Action := actCDEject; // Или... actLogOFF, actShutDown...
PowerControl1.Execute;
end;
unit PowerControl;
interface
uses
Windows, Messages, SysUtils, Classes, Controls, Forms, Graphics, MMSystem;
type
TAction = (actLogOFF, actShutDown, actReBoot, actForce,
actPowerOFF, actForceIfHung, actMonitorOFF,
actMonitorON, actCDEject, actCDUnEject);
TPowerControl = class(TComponent)
private
FAction: TAction;
Procedure SetAction(Value: TAction);
public
function Execute: Boolean;
published
property Action: TAction read FAction write SetAction;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('K2', [TPowerControl]);
end;
procedure TPowerControl.SetAction(Value: TAction);
begin
FAction := Value;
end;
function TPowerControl.Execute: Boolean;
begin
with (Owner as TForm) do
case FAction of
actLogOff: ExitWindowsEx(EWX_LOGOFF,1);
actShutDown: ExitWindowsEx(EWX_SHUTDOWN,1);
actReBoot: ExitWindowsEx(EWX_REBOOT,1);
actForce: ExitWindowsEx(EWX_FORCE,1);
actPowerOff: ExitWindowsEx(EWX_POWEROFF,1);
actForceIfHung: ExitWindowsEx(EWX_FORCEIFHUNG,1);
actMonitorOFF: SendMessage(Application.Handle, WM_SYSCOMMAND,
SC_MONITORPOWER, 0);
actMonitorON: SendMessage(Application.Handle, WM_SYSCOMMAND,
SC_MONITORPOWER, -1);
actCDEject: mciSendstring('SET CDAUDIO DOOR OPEN WAIT',
nil, 0, Handle);
actCDUnEject: mciSendstring('SET CDAUDIO DOOR CLOSED WAIT',
nil, 0, Handle);
end; {Case}
Result := True;
end;
end.
[Nikolaev Igor]
Директивы компилятора, способные увеличить скорость
Скорость исполнения прилоежния может упасть из-за применения динамических массивов. Поэтому целесообразно обратить внимание на ключи компилятора. После отладки кода установите эти три наиболее важных ключа:
{$R-} {Range checking off - проверка диапазона}
{$S-} {Stack checking off – проверка стека}
{$A+} {Word align data – 'выравнивание слов'}
Создание редактора свойства
Если вы присвоили свойству имя TableName, то полный цикл создания редактора свойств включает следующие шаги:
Опишите класс редактора свойства:
type
TTableNameProperty = class(TStringProperty)
function GetAttributes: TPropertyAttributes; override;
procedure GetValues(Proc: TGetStrProc); override;
end;
implementation
{ TTableNameProperty }
function TTableNameProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paValueList];
end;
procedure TTableNameProperty.GetValues(Proc: TGetStrProc);
var
TableName: String;
I: Integer;
begin
{ здесь вы должны добавить свой код, чтобы с помощью цикла обойти имена всех
таблиц, включенных в список }
for I := 0 to ???? do begin
TableName := ????[I];
Proc(TableName);
end;
end;
Затем зарегистрируйте данный редактор свойства следующим образом:
RegisterPropertyEditor(TypeInfo(string), TcsNotebook, 'TableName',
TTableNameProperty);
[News Group]
Вызов процедуры, имя которой содержится в переменной
Как вызвать процедуру, имя которой хранится в таблице, списке, и т. п.?
Можно создать переменную типа StringList, как показано ниже:
StringList.Create;
StringList.AddObject('Proc1', @Proc1);
StringList.AddObject('Proc2', @Proc2);
Затем реализовать это в программе:
var
myFunc: procedure;
begin
if Stringlist.IndexOf(S) = -1 then
MessageDlg('Не понял процедуру ' + S, mtError, [mbOk], 0)
else begin
@myFunc := Stringlist.Objects[Stringlist.IndexOf(S)];
myFunc;
end;
end;
[News Group]
Передача функции как параметра
В этом случае лучшим решением будет использование процедурного типа. Допустим, что DllFunction() на входе хочет получить определенную функцию. Поясним это на примере:
type
TMyFuncType = function: integer;
var
MyFunc: TMyFuncType;
function foo: integer;
begin
result := 1;
end;
begin
MyFunc := foo;
DllFunction(longint(MyFunc));
Можно это сделать и так:
DllFunction(longint(@foo));
Тем не менее, нельзя гарантировать, что память при вызовах DLL (как в этом примере) распределяется оптимальным образом.. Для корректной работы необходимо объявить foo с директивой far, т. е. экспортировать ее в модуле.
Также, в зависимости от того, как написана DllFunction(), можно в вызове подразумевать приведение типа:
function DllFunction(p: TMyFuncType): Integer; far; external 'mydll';
В этом случае не нужна переменная MyFunc или оператор «@».
В Delphi/Pascal можно передавать функции как параметры. Но для того чтобы этим воспользоваться, необходимо установить для компилятора тип.
Проверьте следующий код:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
type
IntFunc = function: integer;
function DllFunction(iFunc: IntFunc): integer; far;
begin
DllFunction := iFunc; // Обратите внимание, что это вызов функции
end;
function iFoo: integer; far;
begin
iFoo := 1;
end;
procedure TestIFunc;
var
i: integer;
begin
i := DllFunction(iFoo);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TestIFunc;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Close;
end;
end.
Возможны два способа. Первый заключается в использовании следующего кода:
i := longint(@foo);
Такой способ подойдет, если вы хотите применить для передачи longint. Другой вариант, которым можно воспользоваться – исключить работу с longint и вызывать функцию DLL следующим образом:
DLLfunction(@foo);
Имейте в виду, что если собираетесь вызывать foo из DLL, то необходимо предусмотреть вопросы совместимости. Для получения дополнительной информации почитайте описание функции MakeProcInstance.
Переменная в качестве имени процедуры
Каким образом можно использовать переменную типа String в качестве имени процедуры?
Если все процедуры, которые вы собираетесь вызывать, имеют список с одними и теми же параметрами (или все без параметров), то это не трудно. Для этого необходимы:
процедурный тип, соответствующий вашей процедуре, например:
type
TMacroProc = procedure(param: Integer);
массив, сопоставляющий имена процедур их адресам во время выполнения приложения:
type
TMacroName = string[32];
TMacroLink = record
name: TMacroName;
proc: TMacroProc;
end;
TMacroList = array [1..MaxMacroIndex] of TMacroLink;
const
Macros: TMacroList = (
(name: 'Proc1'; proc: Proc1),
(name: 'Proc2'; proc: Proc2),
...
);
интерпретатор функций, типа:
procedure CallMacro(name: String; param: Integer);
var
i: Integer;
begin
for i := 1 to MaxMacroIndex do
if CompareText(name, Macros[i].name) = 0 then begin
Macros[i].proc(param);
break;
end;
end;
Макропроцедуры необходимо объявить в секции Interface модуля или с ключевым словом Far, например:
procedure Proc1(n: Integer); far;
begin
...
end;
procedure Proc2(n: Integer); far;
begin
...
end;
[News Group]
Массивы размером более 64К
Не существует способа непосредственного доступа к массиву размером свыше 65520 элементов. Или вы пользуетесь для распределения памяти GlobalAlloc или TMemoryStream и создаете специализированный класс для доступа к элементам массива, или вы делаете это непосредственно вручную. Добраться до следующих сегментов GlobalAlloc объекта можно, строя указатели с помощью SelectorInc. Самый простой способ заключается в применении TMemoryStream.
type
Tmyarr = class
buffer: TMemoryStream;
elsize: LongInt;
constructor Create(esize, number: Word);
destructor Free;
procedure SetElement(index: Word; p: Pointer);
procedure GetElement(index: Word; p: Pointer);
end;
implementation
constructor Tmyarr.Create(esize, number: Word);
var
size: LongInt;
begin
Inherited Create;
buffer := TMemoryStream.Create;
elsize := esize;
size := esize * number;
buffer.SetSize(size);
end;
destructor Tmyarr.Free;
begin
if Self <> Nil then begin
buffer.Free;
Destroy;
end;
end;
procedure Tmyarr.GetElement(index: Word; p: Pointer);
begin
buffer.Seek(elsize * index, 0);
buffer.Read(p^, elsize);
end;
procedure Tmyarr.SetElement(index: Word; p: Pointer);
begin
buffer.Seek(elsize * index, 0);
buffer.Write(p^, elsize);
end;
[News Goup]
Часть 2
При перепечатке любого материала
с сайта, видимая ссылка на источник www.warayg.narod.ru
и все имена, ссылки авторов обязательны.
© 2005
|