Type Ta=arrayof something;
Var a:Ta;
Procedure Proc(a:Ta); - внутри
процедуры создаётся копия массива, внутри процедуры работа осуществляется только
с копией данных
Procedure Proc(var a:Ta); - внутри
процедуры код работает именно с переменной а и её содержимым Procedure Proc(const a:Ta); - внутри
процедуры запрещено изменять данные переменной а
Procedure Proc(out a:Ta); - при
входе в процедуру массив рассматривается как пустой, но после выполнения
процедуры можно получить значения
В Дельфи есть специальный класс для хранения массивов строк -
TStringList - очень рекомендую. Вот как вашу строку превратить в TStringList:
Объявление переменной var t:TStringList;
begin t:=TStringList.create; //создаём класс t.text:=stringReplace('Ваша строка для разделения',' ',#13#10,[rfReplaceAll]);//мы заменяем все пробелы на символы конца строки //теперь можно убедится что у вас строка разбина на элементы: showmessage(t[0]);
showmessage(t[1]);
showmessage(t[2]);
showmessage(t[3]); ...
//после работы надо уничтожить класс t.free;
Автор
Vit Взято с
Vingrad.ru http://forum.vingrad.ru
Вариант
2. Используем стандартные массивы:
var a:arrayofstring;//наш массив s:string;//строка которую мы будем разбивать begin s:='Windows Messages SysUtils Variants Classes Graphics Controls Forms'; Repeat//мы постепенно заполняем массив на каждом шаге цикла по 1 элементу setlength(a,length(a)+1);//увеличиваем размер массива на 1 if pos(' ',s)>0then//если есть пробел то надо взять слово до пробела begin a[length(a)-1]:=copy(s,1, pos(' ',s));//присвоение последнему элементу массива первого слова s:=copy(s,pos(' ',s)+1, length(s));//удаляем из строки первое слово end else//в строке осталось только одно слово begin a[length(a)-1]:=s;// присвоим последнее слово break;//выход из цикла end; Until False;//цикл бесконечный, выход изнутри //теперь проверяем что получили showmessage(a[0]);
showmessage(a[1]);
showmessage(a[2]);
После
использования массива не забудте освободить память a:=nil или setlength(a,0)
Self - это явное задание экземпляра класса в его методе.
Например для
твоей формы это указание на саму форму:
procedure TForm1.Button1Click(Sender: TObject);
begin showmessage(self.classname+#13#10+self.name); end;
Если
например это MDI форма то это будет указатель именно на тот экземпляр для
которого выполняется этот код. На практике Self обычно применяется при написании
своих классов, когда ты пишешь класс или компонент, то у тебя нет переменной с
экземпляром этого компонента, следовательно чтобы обратится к экземпляру
(который появится только в коде конечного пользователя, который будет
использовать компонент) класса нужна переменная - вот она и берётся за self.
Чтобы
понять, что такое self надо понять что такое метод класса. Метод класса - это
просто функция(процедура) который имеет дополнительный неявный параметр -
указатель на экземпляр класса. То есть:
procedure TMy.Proc(val:integer);
begin x:=val;
end;
После
компиляции это будет практически то же самое, что:
procedure Proc(self:TMy;val:integer);
begin self.x:=val; end;
То
есть на самом деле в методе Proc обращаясь к x мы на самом деле обращаемся к
self.x, просто переменная self опускается. В скомпилированном коде нет такого
понятия как классы - есть только код и память. Все методы классов превращаются в
обыкновенные функции, в которым качестве первого параметра передается указатель
на область памяти где лежит созданный пользователем экземпляр класса, который
они и используют для чтения или записи(а так же для вызова) того, что мы
называем членами класса.
var m1,m2:TMy; begin .....
m1.Proc(4); // -> Proc(m1,4) m2.Proc(4); // -> Proc(m2,4) end;
Ошибка "Access Violation" возникает, когда идёт обращение к памяти к которой
обращение запрещено. Это возможно во многих случаях, но наиболее типичные
ситуации я попытаюсь перечислить:
1) Обращение к не созданному
объекту.
var e:TEdit;
begin e.text:='Hello world!'; end;
В
данном случае объект e ещё не создан и идёт обращение к памяти, которая ещё не
выделена.
2) Обращение к уже разрушенному объекту:
var e:TEdit;
begin ...
e.free; ... e.text:='Hello world'; end;
Тут
есть хитрость, допустим вы хотите проверить есть ли объект и модернизируете код:
if e<>nilthen e.text:='Hello world!';
или
if assigned(e) then e.text:='Hello world!';
Особенно
часто приходится такое делать когда надо уничтожить объект:
if e<>nilthen e.free;
Так вот -
такой код может быть источником ошибки, так как метод Free автоматически не
устанавливает указатель в Nil. Обязательно после каждого Free используйте
установление указателя в nil:
e.free;
e:=nil;
3)
При выходе за границы динамического массива обычно генерится ошибка "Index
out of bound", но возможно и возникновение Access Violation, особенно когда не
стоят опции компилляции по проверки границ массивов. Эта ошибка может быть очень
сложна в отлаживании - дело в том что допустим у вас есть массив а длиной в 10
элементов, в пишете:
a[20]:=smething;
И
эта строка может пройти как и надо, без всяких проблем, но её выполнение
повредит какой-то другой код, причём каждый раз другой! Теперь самая безобидная
операция типа i:=10 может вдруг внезапно дать Access Violation.
3) На
форме на onCreate вызывается что-то с других форм - эти другие формы на
этот момент еще не созданы
4) На форме на onDestroy вызывается
что-то с других форм - эти другие формы на этот момент уже разрушены
Типа как в среде при нажатии Ctrl-Shift-G. Функция CoCreateGuid выводит
значение типа TGUID, я нигде не нашёл функции конвертации TGUID -> String.
Может кто знает такую функцию?
Тип String: по смещению -4 храниться длина строки по смещению -8
храниться счётчик ссылок на строку (когда он обнуляется строка уничтожается)
Сама строка располагается в памяти как есть - каждая буква занимает 1 байт.
При копировании строки: s1:=s2 -
реального копирования не происходит, увеличивается только счётчик ссылок, но
если после этого изменить одну из строк: s1:=s1+'a'; то
произойдёт физическое копирование содержимого строк, и теперь s1 и s2 будут
показывать на разные адреса памяти. PChar - длина строки определяется
от начала до #0 байта, по сути это чистой воды pointer, так что все действия по
отслеживанию распределения памяти лежат на программисте - сами заботьтесь о том
чтобы хватило места для распределения памяти и освобождении после использования.
Тоже одна буква = 1 байт Для хранения unicode (т.е. 2х байтовых символов)
используйте соответствующие символы с приставкой Wide... Автор
ответа:Vit
Примечание Fantasist'a:
Это верно только если s1 -
локальная переменная, или s1 и s2 - обе не локальные. Если s1 не
локальная(глобальная или член класса), а s2 - локальная происходит копирование.
А
как сделать чтобы "procedure Click" была не методом класса, а отдельно стоящей
функцией?
procedure Click(Self: TObject; Sender: TObject);
begin ...
end; var evhandler: TNotifyEvent; TMethod(evhandler).Code := @Click;
TMethod(evhandler).Data := nil;
Button1.OnClick := evhandler;
Без извращений можно так:
TDummy = class classprocedure Click(Sender: TObject);
end;
Button1.OnClick := TDummy.Click; Автор
ответа:Le
Taon Взято с
Vingrad.ru http://forum.vingrad.ru
По
идее, при вызове OnClick первым параметром будет запихнут указатель на экземпляр
того класса который в этом OnClick хранится . Я в низкоуровневой реализации не
силен, но кажись, так как параметры в процедурах в Delphi передаются через
регистры, то ничего страшного не произойдет.
procedure C(Self:pointer;Sender:TObject);
begin TButton(Sender).Caption:='ee'; end;
procedure TForm1.FormCreate(Sender: TObject);
begin @Button1.OnClick:=@c; end;
Self
тут у нас будет равен nil, а Sender как раз и получается Sender'ом.
1) Есть Class1, с методом Mtd. 2) Eсть Class2 наследованный от Class1, метод
Mtd перезаписан 3) В программе используется переменная типа Class2 Можно
ли из программы вызвать Mtd от Class1, Другими словами, можно ли вызвать
перезаписанный метод класса-предка?
Способ 1(только для не виртуальных
методов) var a:class2;
begin a:=class2.Create; class1(a).mtd; .... end; Автор
ответа:Fantasist Взято с
Vingrad.ru http://forum.vingrad.ru
Способ
со статическим приведением годится только для невиртуальных методов, имеющих
одно имя. Вызов же виртуальных методов от статического типа не зависит.
В твоём простейшем случае достаточно написать inherited Mtd; (ты его
можешь вызвать из любого метода TClass2, не только из Mtd). Трудности
возникнут, когда нужно вызвать метод "дедушки" или "прадедушки" и т.д. Один
из способов, описанных в литературе, - временная замена VMT объекта на
"дедушку" и обратно. Но если у дедушки такого метода не было - будет облом.
Я предпочитаю такой способ:
type TProc = procedureofobject; procedure TClassN.SomeMethod;
var Proc: TProc; begin TMethod(Proc).Code := @TClass1.Mtd; // Статический адрес TMethod(Proc).Data := Self; Proc(); end;
Автор
ответа:Le
Taon Взято с
Vingrad.ru http://forum.vingrad.ru
Может кто объяснит подробнее особенности применения директив вызовов процедур:
register, pascal, cdecl, stdcall, safecall. В чём отличия, когда и какие надо
применять, какие преимущества и недостатки?
Разница в способе передачи
параметров в функцию и возврата параметров из функции. stdcall - юзается
(вроде) а винапях. Передача аргументов справа налево. Стек очищает вызываемая
процедура. Возвращает разультат в EAX (помойму) pascal - юзалось в вин16апи.
Передача в аргументов слева направо. Стек очищает вызываемая. В паскале
результат возвращался в al, ax или в dx:ax. Как в дельфи - не помню, вероятно а
EAX. register - передача всего через регистры проца. Как именно - зависит от
компилера. cdecl - не помню. Вроде тоде, что и stdcall, только стек чистит
вызываюзая процедура
sdecl
- вызовы в стиле С (для обращения к длл использующим соглашения о вызовах в
стиле С). Параметры в сет с права на лево. Очистка - вызывающей процедурой.
Обеспечивают обслуживание переменного числа параметров.
Эти
директивы скорее относятся к способу(ам) реализации вызовов процедур и передачи
(примему от) параметров на конкретном машинном языке при компилляции с языков
высокого уровня. Так, например в DOS СИ использовали свои виды
реализаций(обычно называемые C-call), а Паскаль - свой. В win32 также
различаются реализации для этих языков, но постепенно происходит заимствование
фрагментов реализаций друг у друга и их симбиозы (stdcall). Если ты пишешь
только на одном языке и не подключаешь внешних библиотек, созданных другим
компиллятором (в другом формате), то тебе, в принципе, все равно, какая
реализация используется - компиллятор сам примет верное решение и согласует
вызовы подпрограмм в своем стиле. Исключение, пожалуй, составляет лишь опция
"registers" - по смыслу это означает приоритетное использование регистров
процессора для передачи(получения) данных процедуре. Как правило, это ускоряет
вызов процедуры и возврат из нее: может быть использования для повышения
быстродействия. Однако это обычно делают установкой глобального флага проекта в
момент создания Файнал Релиз, применяя это сразу ко всем подпрограммам.
Однако если тебе необходимо подключить внешнюю библиотеку (например,
написанный на СИ dll, вызывающий в свою очередь апи sql-сервера), то будет
необходимо учесть способ передачи параметров именно этой библиотеке. Или при
явном вызове win api из кода также нужно учесть способ их вызова (stdcall)...
Статья
P. Below http://www.swissdelphicenter.chна
Calling conventions influence two things: - how parameters are passed to
a function/procedure (=routines) - how the call stack is cleaned up when the
call returns Delphi routines can use the calling conventions pascal (the
Delphi 1 default), register (the default for Delphi 2-5), cdecl (the
default used by C/C++ compilers), stdcall (the default used by the Windows
API). There is a fifth one: safecall, which is only used in the context of
interface methods. A good explanation for what it entails can be found in
issue 51 (Nov. 99) of The Delphi Magazine, i will not go into it further
here. Lets go through the first four in detail, using a couple of test
functions with the same parameter list but different calling conventions.
For clearity we compile with stack frames on, so each routine will start
with the prologue push ebp mov ebp, esp The stack layouts given
below are for the mov line. Each test function is called with the same
parameter values so one can use the CPU windows stack pane to study the
resulting stack layout.
1. Pascal calling convention
Function Test1( i: Integer; b: Boolean; d: Double ): Integer;
Pascal; Pascal calling convention passes parameters on the stack and
pushes them from left to right in the parameter list. Each parameter
occupies a multiple of 4 bytes. The resulting stack layout is ebp + 20
value of i, 4 bytes ebp + 16 value of b, 4 bytes, only lowbyte significant
ebp + 08 value of d, 8 bytes ebp + 04 return address, 4 bytes ebp +
00 old ebp value The parameters are cleared off the stack by the called
function using a ret $10 instruction ($10 = 16 is the total size of
the parameters on stack).
2. Register calling convention Function Test2( i: Integer; b: Boolean; d: Double ): Integer; Register;
Register calling convention passes parameters in registers eax, edx, ecx
and on the stack and processes them from left to right in the parameter
list. There are rules to decide what goes into registers and what goes on
the stack, as detailed in the Object Pascal Language guide. The resulting
stack layout is ebp + 08 value of d, 8 bytes ebp + 04 return
address, 4 bytes ebp + 00 old ebp value The value of i is passed in eax,
the value of b in edx. The parameters are cleared off the stack by the
called function using a ret $8 instruction ($8 = 8 is the total size
of the parameters on stack).
3. cdecl calling convention Function Test3( i: Integer; b: Boolean; d: Double ): Integer; cdecl;
Cdecl calling convention passes parameters on the stack and pushes them
from right to left in the parameter list. Each parameter occupies a multiple
of 4 bytes. The resulting stack layout is ebp + 16 value of d, 8 bytes
ebp + 12 value of b, 4 bytes, only lowbyte significant ebp + 08 value of
i, 4 bytes ebp + 04 return address, 4 bytes ebp + 00 old ebp value
The parameters are cleared off the stack by the calling function, so the
function ends with a ret 0 and after the call instruction we find a
add esp, $10 instruction ($10 = 16 is the total size of the parameters
on stack).
4. Stdcall calling convention Function Test4(
i: Integer; b: Boolean; d: Double ): Integer; stdcall; Sdtcall calling
convention passes parameters on the stack and pushes them from right to left
in the parameter list. Each parameter occupies a multiple of 4 bytes. The
resulting stack layout is ebp + 16 value of d, 8 bytes ebp + 12
value of b, 4 bytes, only lowbyte significant ebp + 08 value of i, 4 bytes
ebp + 04 return address, 4 bytes ebp + 00 old ebp value The
parameters are cleared off the stack by the called function using a ret
$10 instruction ($10 = 16 is the total size of the parameters on stack).
When writing DLLs that are only be meant to be used from Delphi programs
you will usually use the register calling convention, since it is the most
efficient one. But this really ties the DLL to Delphi, no program compiled
in another language (with the exception of BC++ Builder perhaps) will be
able to use the DLL unless it uses assembler to call the functions, since
the Register calling convention (like MS VC _fastcall) is
compiler-specific. When writing DLLs that should be usable by other
programs regardless of language you use the stdcall calling convention
for exported routines. Any language that can call Windows API routines
will be able to call routines from such a DLL, as long as you stay away from
Delphi-specific data types, like String, Boolean, objects, real48.
Pascal calling convention is Win16 heritage, it was the default for the
Win16 API but is no longer used on Win32. A topic loosely tied to calling
conventions is name decoration for exported names in DLLs. Delphi (5 at
least) does not decorate names, regardless of calling convention used. The
name appears in the exported names table exactly as you cite it in the
exports clause of the DLL, case and all. Case is significant for exported
functions in Win32! Other compilers may decorate names. Unless told to do
otherwise a C compiler will prefix all cdecl functions with an underbar
and will decorate stdcall functions in the format _name@x, where x is
the total parameter size, e.g. _Test3@16. C++ is even worse, unless
functions are declared as extern "C" it will export names in a decorated
format that encodes parameter size and type, in a compiler-specific fashion.
For routines exported with Pascal calling convention the names may be all
uppercase, but as said above you will not usually encouter this convention
on Win32. Due to these naming issues it is often appropriate to sic
TDUMP on an unknown DLL you want to interface to, to figure out the
actual names of the exported functions. These can then be given in a
name clause for the external statement if they are decorated.
Demo DLL:
library DemoDLL;
uses Windows;
function Test1(i: Integer; b: Boolean; d: Double): Integer; pascal; begin Result := Round(i * Ord(b) * d); end;
function Test2(i: Integer; b: Boolean; d: Double): Integer; register; begin Result := Round(i * Ord(b) * d); end;
function Test3(i: Integer; b: Boolean; d: Double): Integer; cdecl; begin Result := Round(i * Ord(b) * d); end;
function Test4(i: Integer; b: Boolean; d: Double): Integer; stdcall; begin Result := Round(i * Ord(b) * d); end;
// Example call from test project: implementation {$R *.DFM} function Test1(i: Integer; b: Boolean; d: Double): Integer;pascal; external'DEMODLL.DLL'Index1; function Test2(i: Integer; b: Boolean; d: Double): Integer;register; external'DEMODLL.DLL'Index2; function Test3(i: Integer; b: Boolean; d: Double): Integer;cdecl; external'DEMODLL.DLL'Index3; function Test4(i: Integer; b: Boolean; d: Double): Integer;stdcall; external'DEMODLL.DLL'Index4;
procedure TForm1.Button1Click(Sender: TObject);
var i: Integer;
begin i := Test1(16, True, 1.0);
i := Test2(16, True, 1.0);
i := Test3(16, True, 1.0);
i := Test4(16, True, 1.0); end;
Set
breakpoints on the lines and step into the routines with the CPU window open
to see the stack layout.
Иногда возникают трудности интерпретации дробных чисел - что есть разделитель
точка или запятая?
В Дельфи есть системные переменные:
DECIMALSEPARATOR - десятичный разделитель который принят в системе
THOUSANDSEPARATOR - разделитель тысяч, который принят в системе
Для
USA регионального стандарта DECIMALSEPARATOR будет "." THOUSANDSEPARATOR
будет ","
Для России DECIMALSEPARATOR будет ","
THOUSANDSEPARATOR будет "." или " " (не помню уже)
Вот пример написания класса. Этот класс вычисляет сумму квадратов введенных
чисел. Этот класс написан мной только для примера, и я исходил из воображений
наглядности, а не оптимальности. Большая часть реализации не только не
оптимальна, но и бессмыслена, но показывает бОльшую часть простейших приемов
создания класса.
unit Unit2;
interface
Uses classes, Sysutils;
{Нам нужен процедурный тип для создания собственного события. Собственно - это описание процедуры которая должна будет исполнятся при каких-нибудь обстоятельствах}
Type TError = procedure(Sender:TObject; Error: string) ofobject;
{Описание нашего класса, мы его наследуем от TObject, потому ?то нам практи?ески не нужна
никакия функциональность предков} Type TStatistic=Class(TObject)
private{здесь описываются только внутренние переменные и процедуры - "для служебного пользования"} {Описание полей, т.е. переменных которые работают только внутри класса, "снаружи" они не
доступны.} FList:TStringList; FPrecision: byte; {Тоже переменная - для определения события} FonError: TError; {функция - будет использоваться только внутри класса, "снаружи" напрямую не доступна} function GetCount: integer;
public{Описанное здесь доступно для пользователя класса} {Конструктор - метод создания класса, имеет смысл его описывать только если он делает
?то-то специфи?еское - например нам надо будет создать переменную FList. В противном слу?ае
его описание можно опустить - будет работать конструктор родительского класса} Constructor Create;
{Деструктор - метод разрушения класса} Destructor Destroy; override; {Описание методов - собственно методы мало ?ем отли?аются от процедур} Procedure AddValue(Value:String); Procedure Clear;
Function Solve:real;
{Описание свойств. Обратите внимание само свойство не способно хранить никакую информацию, это
только указатель на внутренюю струкруру. Например для хранения свойства Precision используется
переменная FPrecision. А для ?тение свойства Count используется функция GetCount} Property Precision:byte read FPrecision write FPrecision;
Property Count:integer read GetCount;
{Описание событий. ?то такое событие? - Это указатель на процедуру. Сам класс реализации этой процедуры
не знает. Классу известно только заголовок процедуры, вы в коде программы будете писать реализацию
процедуры, а класс только в нужный момент передаст ей управление, используя указатель onError} Property onError:TError read FonError write FonError;
end;
implementation
{ TStatistic }
constructor TStatistic.Create;
begin inherited; {Вна?але надо вызвать конструктор класса-родителя} FList:=TStringList.create;{создаем структуры нашего класса} end;
destructor TStatistic.Destroy;
begin FList.Free;{Разрушаем структуры нашего класса} inherited;{в последнюю о?ередь вызываем деструктор клсса-родителя} end;
procedure TStatistic.AddValue(Value: String); begin FList.add(Value); {Примерно так мы реализуем метод} end;
procedure TStatistic.Clear;
begin FList.clear; end;
function TStatistic.GetCount: integer;
begin Result:=FList.count+1; end;
function TStatistic.Solve: real;
var i:integer;
begin result:=0; for i:=0to FList.count-1do begin try result:=result+(Sqr(strtofloat(FList[i]))); except {интересная конструкция. "on e:exception do" - мы "отлавливаем" ошибку как переменную "e".
Эта переменная имеет о?ень полезное свойство e.message - оно содержит описание ошибки. Далее
следует вызов события. Вна?але мы проверяем использует ли пользователь событие:
"if Assigned(FOnError) then", если использует то вызываем его процедуру: FOnError, с параметрами:
self - зарезервированная переменная - указатель на экземпляр нашего класса, e.message - описание
ошибки} on e:exception do if Assigned(FOnError) then FOnError(Self, e.message); end; end; end;
// функция служит для выяснения существования VMT у класса // возвращает True, если класс имеет VMT и False - если нет function IsVMTExist(Cls: TClass): Boolean;
// процедура служит для замены адреса метода в VMT класса со смещением // Offset(должно быть кратно 4) новым адресом, хранящимся в NewMet // примечание: перед вызовом этой процедуры проверяйте существование // VMT у класса функцией IsVMTExist procedure VirtMethodReplace(Cls: TClass; Offset: LongWord; NewMet: Pointer); overload;
// процедура служит для замены адреса метода, хранящегося в OldMet, // в VMT класса новым адресом, хранящимся в NewMet // примечание: перед вызовом этой процедуры проверяйте существование // VMT у класса функцией IsVMTExist procedure VirtMethodReplace(Cls: TClass; OldMet, NewMet: Pointer); overload;
// функция служит для замены адреса динамического метода класса с индексом, // хранящимся в Index, новым адресом, хранящимся в NewMet // возвращает True, если метод с данным индексом найден и False - если нет function DynMethodReplace(Cls: TClass; Index: Word; NewMet: Pointer): Boolean; overload;
// функция служит для замены адреса динамического метода класса, хранящегося // в OldMet, новым адресом, хранящимся в NewMet // возвращает True, если метод с данным адресом найден и False - если нет function DynMethodReplace(Cls: TClass; OldMet, NewMet: Pointer): Boolean; overload;
implementation
// функция служит для получения указателя на байт, следующий за адресом // последнего метода в VMT класса // возвращает nil в случае, если у класса нет VMT // функция является "внутренней" в модуле // (используется другими подпрограммами и не объявлена в секции interface) // , поэтому используйте её только если // Вы полностью уверены в своих действиях(она изменяет "рабочие" регистры // ECX и EDX) function GetVMTEnd(Cls: TClass): Pointer;
asm // Вход: Cls --> EAX // Выход: Result --> EAX
PUSH EDI MOV EDI, EAX PUSH ECX
PUSH EDX PUSH EAX CALL GetVMTEnd
POP EDX SUB EAX, EDX SHR EAX, 2 POP EDX POP ECX PUSH ECX
MOV ECX, EAX MOV EAX, EDX
POP EDX REPNE SCASD
JNE @@OldMet_not_found
MOV [EDI - 4], EDX
@@OldMet_not_found:
POP EDI
end;
function DynMethodReplace(Cls: TClass; Index: Word; NewMet: Pointer): Boolean; overload;
asm // Вход: Cls --> EAX, Index --> DX, NewMet --> ECX // Выход: Result --> AL
PUSH EDI PUSH ESI MOV ESI, ECX
XOR EAX, EDX
XOR EDX, EAX
XOR EAX, EDX
JMP @@start @@cycle: MOV EDX, [EDX]
@@start: MOV EDI, [EDX].vmtDynamicTable
TEST EDI, EDI JZ @@get_parent_dmt
MOVZX ECX, WORD PTR [EDI] PUSH ECX
ADD EDI, 2 REPNE SCASW JE @@Index_found
POP ECX @@get_parent_dmt:
MOV EDX, [EDX].vmtParent TEST EDX, EDX
JNZ @@cycle JMP @@Index_not_found
@@Index_found:
POP EAX SHL EAX, 1 SUB EAX, ECX MOV [EDI + EAX * 2 - 4], ESI
MOV AL, 1 JMP @@exit @@Index_not_found:
XOR AL, AL
@@exit: POP ESI POP EDI
end;
function DynMethodReplace(Cls: TClass; OldMet, NewMet: Pointer): Boolean; overload;
asm // Вход: Cls --> EAX, OldMet --> EDX, NewMet --> ECX // Выход: Result --> AL
PUSH EDI PUSH ESI MOV ESI, ECX
XOR EAX, EDX
XOR EDX, EAX
XOR EAX, EDX
JMP @@start @@cycle: MOV EDX, [EDX]
@@start: MOV EDI, [EDX].vmtDynamicTable
TEST EDI, EDI JZ @@get_parent_dmt
MOVZX ECX, WORD PTR [EDI] LEA EDI, EDI + 2 * ECX + 2 REPNE SCASD JE @@OldMet_found
@@get_parent_dmt: MOV EDX, [EDX].vmtParent
TEST EDX, EDX JNZ @@cycle
JMP @@OldMet_not_found
@@OldMet_found: MOV [EDI - 4], ESI
MOV AL, 1 JMP @@exit @@OldMet_not_found:
XOR AL, AL
@@exit: POP ESI POP EDI
BEEP , для дельфи , который работает, как в B.Pascal 7.0
Я
применяю следующий код, однако он работает только под Win9x/me (Под
WinNT/2000/XP вы можете использовать Beep(Tone, Duration) - задавать тон и
продолжительность звучания).
procedure Sound(Freq : Word);
var B : Byte;
begin if Freq > 18then begin Freq := Word(1193181div LongInt(Freq));
B := Byte(GetPort($61)); if (B and3) = 0then begin SetPort($61, Word(B or3));
SetPort($43, $B6); end;
SetPort($42, Freq);
SetPort($42, Freq shr8); end; end;
procedure NoSound;
var Value: Word;
begin Value := GetPort($61) and$FC;
SetPort($61, Value);
end;
procedure SetPort(address, Value:Word);
var bValue: byte;
begin bValue := trunc(Value and255); asm mov dx, address mov al, bValue out dx, al
end; end;
function GetPort(address:word):word;
var bValue: byte;
begin asm mov dx, address in al, dx
mov bValue, al end;
GetPort := bValue; end;
Взято с
Vingrad.ru http://forum.vingrad.ru
При создании визуальных контролов в runtime, важным моментом является назначение
родительских свойств и использование метода SetBounds, чтобы этот контрол стал
видимы.
Каким образом можно
использовать переменную типа 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 := 1to MaxMacroIndex do if CompareText(name, Macros[i].name) = 0thenbegin Macros[i].proc(param); break; end; end; Макропроцедуры необходимо объявить в секции Interface модуля или с клю?евым словом Far, например: procedure Proc1(n: Integer); far; begin ...
end; procedure Proc2(n: Integer); far; begin ...
end;
Взято с
Vingrad.ru http://forum.vingrad.ru
В Дельфи есть предопределенные переменные языковых установок и форматов:
SysUtils
The following are a set of variables used to define the
format for numeric or date/time strings:
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;
procedure SwapVars1(var u, v; Size: Integer);
var x: Pointer; begin GetMem(x, Size); try System.move(u, x^, Size); System.move(v, u, Size);
System.move(x^, v, Size); finally FreeMem(x); end; end;
procedure SwapVars2(var Source, Dest; Size: Integer);
// By Mike Heydon, mheydon@eoh.co.za begin asm push edi push esi mov esi,Source
mov edi,Dest mov ecx,Size cld @1:
mov al,[edi] xchg [esi],al inc si stosb
loop @1 pop esi pop edi end; end;
Paramcount - показывает сколько параметров передано Paramstr(0) - это имя с
путем твоей программы Paramstr(1) - имя первого параметра Paramstr(2) -
имя второго параметра и т.д.
Если ты запускаешь:
с:\myprog.exe
/a -b22 c:\dev
то Paramcount будет равен 3 Paramstr(0) будет равен
с:\myprog.exe Paramstr(1) будет равен /a Paramstr(2) будет равен -b22
Paramstr(3) будет равен c:\dev
Параметер это просто строка,
набор букв, выполнить ее нельзя - ты можешь только проверить на наличие строки и
если она присутствует, то выполнить какое либо действие, это действие ты должен
написать сам, никаких стандартных действий нет.
Например у тебя возможно
3 параметра:
Если параметер = "/v" то выдать сообщение, если параметер
"/c" то покрасить форму в синий цвет, если параметер "/f" - поменять заголовок
формы:
if paramstr(1)='/v'then showmessage('Parameter "/v" was found!'); if paramstr(1)='/c'then color:=clBlue; if paramstr(1)='/f'then caption:='Parameter "/f" was found'; Поставь
этот код на событие формы onActivate, откомпиллируй и попробуй запустить
программу с одним из 3х указанных параметров и ты увидишь что произойдет.
Основное предназначение этой статьи, заполнить пробелы
в оригинальной документации по Borland Delphi Developer, при этом весь
программный код, а так же теория, полность совместимы со всеми версиями Delphi.
Основное направление статьи, это познакомиться с использованием
ассемблера в Object Pascal. Однако, не будем пропускать и те аспекты
программирования, которые будут требовать пояснения для конкретных примеров,
приведённых в этой статье.
Использование Ассемблера в Борландовком
Delphi Перед тем, как начать, хотелось бы определиться с уровнем знаний,
необходимых для нормального усвоения данного материала. Необходимо быть знакомым
со встроенными средствами отладки в Delphi. Так же необходимо иметь
представление о таких терминах как тип реализации (instantiation), null pointer
и распределение памяти. Если в чём-то из вышеупомянутого Вы сомневаетесь, то
постарайтесь быть очень внимательны и осторожны при воплощении данного материала
на практике. Кроме того, будет обсуждаться только 32-битный код, так что
понадобится компилятор не ниже Delphi 2.0.
Зачем использовать Ассемблер?
На мой взгляд, Object Pascal, это инструмент, позволяющий генерировать
быстрый и эффективный код, однако использование ассемблера в некоторых случаях
позволяет решать некоторые задачи более эффективно. За всю работу с Delphi, я
пришёл к выводу, что использование низкоуровневого кода необходимо в двух
случая.
(1) Обработка большого количества данных. Nb. В данный случай не
входит ситуация, когда используется язык запроса данных.
(2) В
высокоскоростных подпрограммах работы с дисплеем. Nb. Имеется ввиду
использование простых процедур на чистом паскале, но никак не внешних библиотек
и DirectX.
В конце статьи мы рассмотрим примеры, которые явно отражают
значимость этих критериев, а так же не только когда и где использовать
ассемблерные вставки, но и как включать такой код в Delphi.
Что такое
Ассемблер? Надеюсь, что Все читатели этой статьи имеют как минимум
поверхностное представление о работе процессора. Грубо говоря, это калькулятор с
большим объёмом памяти. Память, это не более чем упорядоченная последовательнось
двоичных цифр. Каждая такая цифра является байтом. Каждый байт может содержать в
себе значение от 0 до 255, а так же имеет свой уникальный адрес, при помощи
которого процессор находит нужные значения в памяти. Процессор так же имеет
набор регистров (это можно расценить как глобальные переменные). Например
eax,ebx,ecx и edx, это универсальные 32-битные регистры. Это значит, что самое
большое число, которое мы можем записать в регистр eax, это 2 в степени 32 минус
1, или 4294967295.
Как мы уже выяснили, процессор манипулирует
значениями регистров. Машинный код операции прибавления 10 к значению регистра
eax будет выглядеть следующим образом 05/0a/00/00/00 Однако, такая
запись абсолютно не читабельна и, как следствие, не пригодна при отладке
программы. Так вот Ассемблер, это простое представление машинных команд в более
удобном виде. Теперь давайте посмотрим, как будет выглядеть прибавление 10 к eax
в ассемблерном представлении: add eax,10 {a := a + 10} А вот так
выглядит вычитаение значения ebx из eax sub eax,ebx {a := a - b } Чтобы
сохранить значние, можно просто поместить его в другой регистр mov eax,ecx
{a := c } или даже лучше, сохранить значение по определённому адресу в
памяти mov [1536],eax {сохраняет значение eax по адресу 1536} и конечно
же взять его от туда mov eax,[1536]
Однако, тут есть важный момент,
про который забывать не желательно. Так как регистр 32-битный(4 байта), то его
значение будет записано сразу в четыре ячейки памяти 1536, 1537, 1538 и 1539.
А теперь давайте посмотрим, как компилятор преобразует действия с
переменными в машинный код. Допустим у нас есть строка Count := 0; Для
компилятора это означает, что надо просто запомнить значение. Следовательно,
компилятор генерирует код, который сохраняет значение в памяти по определённому
адресу и следит, чтобы не произошло никаких накладок, и обзывает этот адрес как
'Count'. Вот как выглядит такой код
mov eax,0 mov Count,eax
Компилятор
не может использовать строку типа
mov Count,0 из-за того, что как
минимум один параметр инструкции должен являться регистром. Если посмотреть
на строку Count := Count + 1; то её ассемблерное представление будет
выглядеть как mov eax,Count add eax,1 mov Count,eax Для
переменных, тип которых отличается от целого, всё усложняется. Однако,
рассмотрим эту тему немного позже, а сейчас предлагаю закрепить теорию
практическими примерами.
Итак, рассмотрим первый пример. Сразу извинюсь
за тривиальность, но с чего-то надо начинать.
function Sum(X,Y:integer):integer;
begin Result := X+Y; end;
А
вот так будет выглядеть оперция сложения двух целых чисел на ассемблере:
function Sum(X,Y:integer):integer;
begin asm mov eax,X add eax,Y mov Result,eax end; end;
Этот
код прекрасно работает, однако он не даёт нам преимущества в скорости, а так же
потерялось восприятие кода. Но не стоит огорчаться, так как те немногие знания,
которые Вы почерпнули из этого материала, можно использовать с большей пользой.
Допустим, нам необходимо преобразовать явные значения Red,Green, и Blue в цвета
типа TColor, подходящие для использования в Delphi. Тип TColor описан как
24-битный True Colour хранящийся в формате целого числа, то есть четыре байта,
старший из которых равен нулю, а далее по порядку красный, зелёный, синий.
function GetColour(Red,Green,Blue:integer):TColor;
begin asm {ecx будет содержать значение TColor} mov ecx,0 {начинаем с красной компоненты} mov eax,Red {необходимо убедиться, что красный находится в диапазоне 0<=Red<=255} and eax,255 {сдвигаем значение красного в правильное положение} shl eax,16 {выравниваем значение TColor} xor ecx,eax
{проделываем тоже самое с зелёным} mov eax,Green and eax,255 shl eax,8 xor ecx,eax
{и тоже самое с синим} mov eax,Blue and eax,255 xor ecx,eax
mov Result, ecx end; end;
Заметьте,
что я использовал несколько бинарных операций. Эти операции также определены
непосредственно в Object Pascal.
procedure TForm1.FormCreate(Sender: TObject);
begin {This only works for classes registered using RegisterClass} RegisterClasses([TButton, TForm]); end;
procedure TForm1.Button1Click(Sender: TObject);
var CRef: TPersistentClass; PTI: PTypeInfo; AControl: TControl;
begin CRef := GetClass('TButton'); if CRef <> nilthen begin AControl := TControl(TControlClass(CRef).Create(Self)); with AControl do begin Parent := Self; Width := 50;
Height := 30; end;
Inc(Id); end else MessageDlg('No such class', mtWarning, [mbOk], 0); end;
// Converting method pointers into function pointers
// Often you need a function pointer for a callback function. But what, if you want to specify a method as // an callback? Converting a method pointer to a function pointer is not a trivial task; both types are // incomatible with each other. Although you have the possibility to convert like this "@TClass.SomeMethod", // this is more a hack than a solution, because it restricts the use of this method to some kind of a class // function, where you cannot access instance variables. If you fail to do so, you'll get a wonderful gpf. // But there is a better solution: run time code generation! Just allocate an executeable memory block, and // write 4 machine code instructions into it: 2 instructions loads the two pointers of the method pointer // (code & data) into the registers, one calls the method via the code pointer, and the last is just a return // Now you can use this pointer to the allocated memory as a plain funtion pointer, but in fact you are // calling a method for a specific instance of a Class.
C Data Type | Object Pascal | Description
----------------------------------------------
LPSTR PAnsiChar; String >pointer
LPCSTR PAnsiChar; String >pointer
DWORD Integer; Whole numbers
BOOL LongBool; Boolean values
PBOOL ^BOOL; Pointer to a Boolean value
Pbyte ^Byte; Pointer to a byte value
PINT ^Integer; Pointer to an integer value
Psingle ^Single; Pointer to a single (floating point) value
PWORD ^Word; Pointer to a 16-bit value
PDWORD ^DWORD; Pointer to a 32-bit value
LPDWORD PDWORD; Pointer to a 32-bit value
UCHAR Byte; 8-bit values (can represent characters)
PUCHAR ^Byte; Pointer to 8-bit values
SHORT Smallint; 16-bit whole numbers
UINT Integer; 32-bit whole numbers. Traditionally,
this was used to represent unsigned integers,
but Object Pascal does not have a true
unsigned integer data type.
PUINT ^UINT; Pointer to 32-bit whole numbers
ULONG Longint; 32-bit whole numbers. Traditionally,
this was used to represent unsigned integers,
but Object Pascal does not have a true
unsigned integer data type.
PULONG ^ULONG; Pointer to 32-bit whole numbers
PLongint ^Longint; Pointer to 32-bit values
PInteger ^Integer; Pointer to 32-bit values
PSmallInt ^Smallint; Pointer to 16-bit values
PDouble ^Double; Pointer to double (floating point) values
LCID DWORD; A local identifier
LANGID Word; A language identifier
THandle Integer; An object handle. Many Windows API functions return a value
of type THandle, which identobject ifies that object within
Windows'internal object tracking tables.
PHandle ^THandle; A pointer to a handle
WPARAM Longint; A 32-bit message parameter. Under earlier versions of Windows,
this was a 16-bit data type.
LPARAM Longint; A 32-bit message parameter
LRESULT Longint; A 32-bit function return value
HWND Integer; A handle to a window. All windowed controls, child windows,
main windows, etc., have a corresponding window handle that
identifies them within Windows'internal tracking tables.
HHOOK Integer; A handle to an installed Windows system hook
ATOM Word; An index into the local or global atom table for a string
HGLOBAL THandle; A handle identifying a globally allocated dynamic memory object.
Under 32-bit Windows, there is no distinction between globally
and locally allocated memory.
HLOCAL THandle; A handle identifying a locally allocated dynamic memory object.
Under 32-bit Windows, there is no distinction between globally
and locally allocated memory.
FARPROC Pointer; A pointer to a procedure, usually used as a parameter type in
functions that require a callback function
HGDIOBJ Integer; A handle to a GDI object. Pens, device contexts, brushes, etc.,
all have a handle of this type that identifies them within
Windows'internal tracking tables.
HBITMAP Integer; A handle to a Windows bitmap object
HBRUSH Integer; A handle to a Windows brush object
HDC Integer; A handle to a device context
HENHMETAFILE Integer; A handle to a Windows enhanced metafile object
HFONT Integer; A handle to a Windows logical font object
HICON Integer; A handle to a Windows icon object
HMENU Integer; A handle to a Windows menu object
HMETAFILE Integer; A handle to a Windows metafile object
HINST Integer; A handle to an instance object
HMODULE HINST; A handle to a module
HPALETTE Integer; A handle to a Windows color palette
HPEN Integer; A handle to a Windows pen object
HRGN Integer; A handle to a Windows region object
HRSRC Integer; A handle to a Windows resource object
HKL Integer; A handle to a keyboard layout
HFILE Integer; A handle to an open file
HCURSOR HICON; A handle to a Windows mouse cursor object
COLORREF DWORD; A Windows color reference value, containing values
for the red, green, and of ;bsp;blue components of a color
You may need to know at runtime what properties are available for a particular
component at runtime. The list can be obtained by a call to GetPropList. The
types, functions and procedures, including GetPropList, that allow access to
this property information reside in the VCL source file TYPINFO.PAS.
GetPropList Parameters
function GetPropList(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds; PropList: PPropList): Integer;
The
first parameter for GetPropList is of type PTypeInfo, and is part of the RTTI
(Run Time Type Information) available for any object. The record structure
defined:
The
TTypeInfo record can be accessed through the objects ClassInfo property. For
example, if you were getting the property list of a TButton, the call might
look, so far, like this:
GetPropList(Button1.ClassInfo, ....
The second parameter, of type TTypeKinds, is a set type that acts as
a filter for the kinds of properties to include in the list. There are a number
of valid entries that could be included in the set (see TYPEINFO.PAS), but
tkProperties covers the majority. Now our call to GetPropList would look like:
GetPropList(Button1.ClassInfo, tkProperties ....
The
last parameter, PPropList is an array of PPropInfo and is defined in
TYPEINFO.PAS:
PPropList = ^TPropList;
TPropList = array[0..16379] of PPropInfo;
Now
the call might read:
procedure TForm1.FormCreate(Sender: TObject);
var PropList: PPropList; begin PropList := AllocMem(SizeOf(PropList^));
GetPropList(TButton.ClassInfo, tkProperties + [tkMethod], PropList);
{...}
Getting
Additional Information from the TTypeInfo Record:
The example at the end
of this document lists not just the property name, but it's type. The name of
the property type resides in an additional set of structures. Let's take a
second look at the TPropInfo record. Notice that it contains a PPTypeInfo that
points ultimately to a TTypeInfo record. TTypeInfo contains the class name of
the property.
The
example below shows how to set up the call to GetPropList, and how to access the
array elements. TForm will be referenced in this example instead of TButton, but
you can substitute other values in the GetPropList call. The visible result will
be to fill the list with the property name and type of the TForm properties.
This project requires a TListBox. Enter the code below in the forms
OnCreate event handler.
uses TypInfo;
procedure TForm1.FormCreate(Sender: TObject);
var PropList: PPropList; i: integer; begin PropList := AllocMem(SizeOf(PropList^)); i := 0; try GetPropList(TForm.ClassInfo, tkProperties + [tkMethod], PropList);
while (PropList^[i] <> Nil) and (i < High(PropList^)) do begin ListBox1.Items.Add(PropList^[i].Name + ': ' + PropList^[i].PropType^.Name);
Inc(i); end; finally FreeMem(PropList); end; end;
function GetProperty(AControl: TPersistent; AProperty: String): PPropInfo;
var i: Integer; props: PPropList; typeData: PTypeData; begin Result := nil; if (AControl = nil) or (AControl.ClassInfo = nil) then Exit;
typeData := GetTypeData(AControl.ClassInfo); if (typeData = nil) or (typeData^.PropCount = 0) then Exit;
GetMem(props, typeData^.PropCount * SizeOf(Pointer)); try GetPropInfos(AControl.ClassInfo, props); for i := 0to typeData^.PropCount - 1do begin with Props^[i]^ do if (Name = AProperty) then result := Props^[i]; end; finally FreeMem(props); end; end;
procedure TForm1.Button1Click(Sender: TObject);
var propInfo: PPropInfo; begin PropInfo := GetProperty(Button1.Font, 'Name'); if PropInfo <> nilthen SetStrProp(Button1.Font, PropInfo, 'Arial'); end;
Взято
из http://www.lmc-mediaagentur.de/dpool
You can use RTTI to do this. Here is how to change a particular component:
procedure TForm1.BtnClick(Sender: TObject);
var p: PPropInfo; f: TFont; begin f := TFont.Create; {Setup the font properties} f.Name := 'Arial';
p := GetPropInfo(Sender.ClassInfo, 'Font'); if Assigned(p) then SetOrdProp(Sender, p, Integer(f)); f.Free; end;
To
get at all the forms loop through the Screen global variable. For each form loop
through its Components list calling the above procedure (or something close). If
you only create your components at design time that is it. If you create some at
runtime and the owner is not the form, then for each component loop through its
Components list recursively to get at all the owned components.
type TMyStream = class(TFileStream)
private FFred: integer; published property Fred: integer read FFred write FFred;
end; type TFrmPropertyList = class(TForm)
SpeedButton1: TSpeedButton; ListBox1: TListBox; procedure SpeedButton1Click(Sender: TObject);
private { Private declarations } public { Public declarations } end;
var FrmPropertyList: TFrmPropertyList;
implementation
{$R *.DFM}
uses TypInfo;
procedure ListProperties(AInstance: TPersistent; AList: TStrings);
var i: integer; pInfo: PTypeInfo; pType: PTypeData;
propList: PPropList; propCnt: integer; tmpStr: string; begin pInfo := AInstance.ClassInfo; if (pInfo = nil) or (pInfo^.Kind <> tkClass) then raise Exception.Create('Invalid type information');
pType := GetTypeData(pInfo); {Pointer to TTypeData} AList.Add('Class name: ' + pInfo^.Name); {If any properties, add them to the list} propCnt := pType^.PropCount; if propCnt > 0then begin AList.Add (EmptyStr); tmpStr := IntToStr(propCnt) + ' Propert'; if propCnt > 1then tmpStr := tmpStr + 'ies' else tmpStr := tmpStr + 'y';
AList.Add(tmpStr); FillChar(tmpStr[1], Length(tmpStr), '-');
AList.Add(tmpStr); {Get memory for the property list} GetMem(propList, sizeOf(PPropInfo) * propCnt); try {Fill in the property list} GetPropInfos(pInfo, propList); {Fill in info for each property} for i := 0to propCnt - 1do AList.Add(propList[i].Name + ': ' + propList[i].PropType^.Name);
finally FreeMem(propList, sizeOf(PPropInfo) * propCnt); end; end; end;
function GetPropertyList(AControl: TPersistent; AProperty: string): PPropInfo;
var i: integer; props: PPropList; typeData: PTypeData; begin Result := nil; if (AControl = nil) or (AControl.ClassInfo = nil) then Exit;
typeData := GetTypeData(AControl.ClassInfo); if (typeData = nil) or (typeData^.PropCount = 0) then Exit;
GetMem(props, typeData^.PropCount * SizeOf(Pointer)); try GetPropInfos(AControl.ClassInfo, props); for i := 0to typeData^.PropCount - 1do begin with Props^[i]^ do if (Name = AProperty) then result := Props^[i]; end; finally FreeMem(props); end; end;
procedure TFrmPropertyList.SpeedButton1Click(Sender: TObject);
var c: integer; begin ListProperties(self, ListBox1.Items); for c := 0to ComponentCount - 1do begin ListBox1.Items.Add(EmptyStr);
ListProperties(Components[c], ListBox1.Items); end; end;
type TMethodtableEntry = packedRecord len: Word; adr: Pointer; name: ShortString;
end; {Note: name occupies only the size required, so it is not a true shortstring! The actual
entry size is variable, so the method table is not an array of TMethodTableEntry!}
var pp: ^Pointer; pMethodTable: Pointer;
pMethodEntry: ^TMethodTableEntry; i, numEntries: Word; begin if aClass = nilthen Exit;
pp := Pointer(Integer( aClass ) + vmtMethodtable);
pMethodTable := pp^; lines.Add(format('Class %s: method table at %p', [aClass.Classname, pMethodTable ] ));
if pMethodtable <> nilthen begin {first word of the method table contains the number of entries} numEntries := PWord( pMethodTable )^; lines.Add(format(' %d published methods', [numEntries] ));
{make pointer to first method entry, it starts at the second word of the table} pMethodEntry := Pointer(Integer( pMethodTable ) + 2);
for i := 1to numEntries do begin with pMethodEntry^ do lines.Add(format( ' %d: len: %d, adr: %p, name: %s', [i, len, adr, name] ));
{make pointer to next method entry} pMethodEntry := Pointer(Integer( pMethodEntry ) + pMethodEntry^.len);
end; end;
EnumMethods( aClass.ClassParent, lines ); end;
procedure TForm2.Button1Click(Sender: TObject);
begin memo1.clear; EnumMethods( Classtype, memo1.lines ); end;
function GetComponentProperties(Instance: TPersistent; AList: TStrings): Integer;
var I, Count: Integer; PropInfo: PPropInfo; PropList: PPropList;
begin Result := 0;
Count := GetTypeData(Instance.ClassInfo)^.PropCount; if Count > 0then begin GetMem(PropList, Count * SizeOf(Pointer)); try GetPropInfos(Instance.ClassInfo, PropList); for I := 0to Count - 1do begin PropInfo := PropList^[I]; if PropInfo = nilthen Break; if IsStoredProp(Instance, PropInfo) then begin {
case PropInfo^.PropType^.Kind of tkInteger:
tkMethod: tkClass: ...
end; } end;
Result := AList.Add(PropInfo^.Name);
end; finally FreeMem(PropList, Count * SizeOf(Pointer)); end; end; end;
Tip by
Grega Loboda uses TypInfo
procedure ListProperties(AInstance: TPersistent; AList: TStrings);
var i: integer; pInfo: PTypeInfo; pType: PTypeData;
propList: PPropList; propCnt: integer; tmpStr: string; begin pInfo := AInstance.ClassInfo; if (pInfo = nil) or (pInfo^.Kind <> tkClass) then raise Exception.Create('Invalid type information');
pType := GetTypeData(pInfo); {Pointer to TTypeData} AList.Add('Class name: ' + pInfo^.Name); {If any properties, add them to the list} propCnt := pType^.PropCount; if propCnt > 0then begin AList.Add(EmptyStr); tmpStr := IntToStr(propCnt) + ' Propert'; if propCnt > 1then tmpStr := tmpStr + 'ies' else tmpStr := tmpStr + 'y';
AList.Add(tmpStr); FillChar(tmpStr[1], Length(tmpStr), '-');
AList.Add(tmpStr); {Get memory for the property list} GetMem(propList, sizeOf(PPropInfo) * propCnt); try {Fill in the property list} GetPropInfos(pInfo, propList); {Fill in info for each property} for i := 0to propCnt - 1do AList.Add(propList[i].Name+': '+propList[i].PropType^.Name);
finally FreeMem(propList, sizeOf(PPropInfo) * propCnt); end; end; end;
function GetPropertyList(AControl: TPersistent; AProperty: string): PPropInfo;
var i: integer; props: PPropList; typeData: PTypeData; begin Result := nil; if (AControl = nil) or (AControl.ClassInfo = nil) then Exit;
typeData := GetTypeData(AControl.ClassInfo); if (typeData = nil) or (typeData^.PropCount = 0) then Exit;
GetMem(props, typeData^.PropCount * SizeOf(Pointer)); try GetPropInfos(AControl.ClassInfo, props); for i := 0to typeData^.PropCount - 1do begin with Props^[i]^ do if (Name = AProperty) then result := Props^[i]; end; finally FreeMem(props); end; end;
function hasprop(comp: TComponent; const prop: String): Boolean;
var proplist: PPropList; numprops, i: Integer; begin result := false;
getmem(proplist, getTypeData(comp.classinfo)^.propcount * Sizeof(Pointer));
try NumProps := getproplist(comp.classInfo, tkProperties, proplist);
for i := 0to pred (NumProps) do begin if comparetext(proplist[i]^.Name, prop) = 0then begin result := true; break; end; end; finally freemem(proplist, getTypeData(comp.classinfo)^.propcount * Sizeof(Pointer));
end; end;
procedure setcomppropstring(comp: TComponent; const prop, s: String); var proplist: PPropList; numprops, i: Integer; begin getmem(proplist, getTypeData(comp.classinfo)^.propcount * Sizeof(Pointer));
try NumProps := getproplist(comp.classInfo, tkProperties, proplist);
for i := 0to pred (NumProps) do begin if (comparetext(proplist[i]^.Name, prop) = 0) and (comparetext(proplist[i]^.proptype^.name, 'string') = 0then begin setStrProp(comp, proplist[i], s); break; end; end; finally freemem(proplist, getTypeData(comp.classinfo)^.propcount * Sizeof(Pointer));
end; end;
If I am given a TPersistent object, and a method name, is there a way to
determine if the name is an event of TNotifyEvent type? For example, given a
TPersistent lMyObj and an event name, "OnDataChanged", how can I determine if
OnDataChanged is a TNotifyEvent?
function IsNotifyEvent(Sender: TObject; const Event: string): Boolean;
var PropInfo: PPropInfo; Method: TNotifyEvent; begin Result := False; PropInfo := GetPropInfo(Sender.ClassInfo, Event);
ifnot Assigned(PropInfo) then Exit;
if PropInfo.PropType^.Kind <> tkMethod then Exit;
Method := TNotifyEvent(GetMethodProp(Sender, PropInfo));
Result := Assigned(Method); end;
function GetFontProp( anObj: TObject): TFont;
var PInfo: PPropInfo; begin {Try to get a pointer to the property information for a property with the name 'Font'.
TObject.ClassInfo returns a pointer to the RTTI table, which we need to pass to GetPropInfo} PInfo := GetPropInfo( anObj.ClassInfo, 'font' );
Result := Nil; if PInfo <> Nilthen {found a property with this name, check if it has the correct type} if (PInfo^.Proptype^.Kind = tkClass) and GetTypeData(PInfo^.Proptype^)^.ClassType.InheritsFrom(TFont)
then Result := TFont(GetOrdProp( anObj, PInfo )); end;
Tip by
Peter Below
Does anyone know if there is an easy way to load the value of a component's
property directly from its resource without creating the component? Something
like:
if ReadPropertyValue('Form1.Button1', 'width') > 1000then ShowMessage('You are about to create a big button!'); function TForm1.ReadProp(r: TReader): string; begin result := ''; {Determine the value type of the property, read it with the appropriate method of TReader
and convert it to string. Not all value types are implemented here but you get the idea.} case r.NextValue of vaInt8, vaInt16, vaInt32: result := IntToStr(r.ReadInteger);
vaExtended: result := FloatToStr(r.ReadFloat);
vaString: result := r.ReadString; else r.SkipValue; {Not implemented} end; end;
procedure TForm1.ReadRes(PropPath: string; r: TReader);
var p: string; begin {Skip the class name} r.ReadStr; {Construct the property path} if PropPath = ''then p := r.ReadStr else p := PropPath + '.' + r.ReadStr;
{Read all properties and its values and fill them into the memo} whilenot r.EndOfList do Memo1.Lines.Add(p + '.' + r.ReadStr + ' = ' + ReadProp(r));
{Skip over the end of the list of the properties of this component} r.CheckValue(vaNull); {Recursively read the properties of all sub-components} whilenot r.EndOfList do begin ReadRes(p, r); r.CheckValue(vaNull); end; end;
I am building a routine that checks our forms for validity before deploying
them. I would like to use some kind of structure that tests if a component type
has access to a certain property, something like: " if (self.Controls[b] has
Tag) then ...". Can anyone offer suggestions? Here's an example of setting a
string property for a component if it exists and another for an integer
property:
procedure SetStringPropertyIfExists(AComp: TComponent; APropName: String;
AValue: String); var PropInfo: PPropInfo; TK: TTypeKind; begin PropInfo := GetPropInfo(AComp.ClassInfo, APropName); if PropInfo <> nilthen begin TK := PropInfo^.PropType^.Kind; if (TK = tkString) or (TK = tkLString) or (TK = tkWString) then SetStrProp(AComp, PropInfo, AValue); end; end;
procedure SetIntegerPropertyIfExists(AComp: TComponent; APropName: String;
AValue: Integer); var PropInfo: PPropInfo; begin PropInfo := GetPropInfo(AComp.ClassInfo, APropName); if PropInfo <> nilthen begin if PropInfo^.PropType^.Kind = tkInteger then SetOrdProp(AComp, PropInfo, AValue); end; end;
How can I assign all property values (or if it's not possible only published
property values, or some of them) of one class (TComponent) to another instance
of the same class? What I want to do is:
MyComponent1.{property1} := MyComponent2.{property1}; {...} MyComponent2.{propertyN} := MyComponent2.{propertyN};
Is
there a better and shorter way to do this? I tried this: MyComponent1 :=
MyComponent2; But it doesn't work. Why not? Can I point to the second component
?
Answer 1:
MyComponent2 and MyComponent1 are pointers to
your components, and this kind of assigment leads to MyComponent1 pointing to
MyComponent2. But it will not copy its property values.
A better way is
to override the assign method of your control, do all property assignment there
and call it when you need to copy component attributes. Here's example:
procedure TMyComponent.Assign(Source: TPersistent);
begin if Source is TMyComponent then begin property1 := TMyComponent(Source).property1; { ... } end else inherited Assign(Source);
end;
To
assign properties you'll need to set this line in the code:
MyComponent1.Assign(MyComponent2);
Tip
by Serge Gubenko
Answer 2:
procedure EqualClassProperties(AClass1, AClass2: TObject);
var PropList: PPropList; ClassTypeInfo: PTypeInfo;
ClassTypeData: PTypeData; i: integer; NumProps: Integer;
APersistent : TPersistent; begin if AClass1.ClassInfo <> AClass2.ClassInfo then exit;
ClassTypeInfo := AClass1.ClassInfo;
ClassTypeData := GetTypeData(ClassTypeInfo); if ClassTypeData.PropCount <> 0then begin GetMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);
try GetPropInfos(AClass1.ClassInfo, PropList); for i := 0to ClassTypeData.PropCount - 1do ifnot (PropList[i]^.PropType^.Kind = tkMethod) then {if Class1,2 is TControl/TWinControl on same form, its names must be unique} if PropList[i]^.Name <> 'Name'then if (PropList[i]^.PropType^.Kind = tkClass) then begin APersistent := TPersistent(GetObjectProp(AClass1, PropList[i]^.Name, TPersistent));
if APersistent <> nilthen APersistent.Assign(TPersistent(GetObjectProp(AClass2, PropList[i]^.Name, TPersistent)))
end else SetPropValue(AClass1, PropList[i]^.Name, GetPropValue(AClass2, PropList[i]^.Name));
finally FreeMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);
end; end; end;
Note
that this code skips object properties inherited other than TPersistent.
I need to get a list of strings (like a StringList) with the possible values for
a TBrushStyle property (bsSolid, bsClear, bsHorizontal, for example). I want to
build a ComboBox with this options. How can I set the property Items of my
ComboBox directly with all the values from the enumerated type TBrushStyle? My
ComboBox will be alike the Property Editor for this type.
You can
use runtime type information (RTTI) to do that. Below is an example:
uses {...}, TypInfo
procedure BrushStylesAsStrings(AList: TStrings);
var a: integer; pInfo: PTypeInfo; pEnum: PTypeData; begin AList.Clear; pInfo := PTypeInfo(TypeInfo(TBrushStyle));
pEnum := GetTypeData(pInfo); with pEnum^ do begin for a := MinValue to MaxValue do AList.Add(GetEnumName(pInfo, a)); end; end;
I would like to change the font color on all components on a form at runtime
(and the components owned by the components etc). I devised a recursive
algorithm using RTTI that accepts a TComponent as a parameter. It works to some
extent, but I still have to use 'if' statements to cast the object to a
particular descendant, resulting in about 30 lines of code to test for all of
the components I use. Also, some objects (TColumnTitle), are not descended from
TComponent, even though they have a font property.
This may do the trick
(with D6 and maybe D5): uses TypInfo;
{ ... } var i: integer; aFont: TFont; begin for i := 0to aComponent.ComponentCount - 1do begin aFont := TFont(GetOrdProp(aComponent.Components[i], 'Font'));
if assigned(aFont) then aFont.Color := clWhite; end; end;
With D4:
{ ... } var i: integer; aFont: TFont; pi: PPropInfo; begin for i := 0to aComponent.ComponentCount - 1do begin pi := GetPropInfo(aComponent.Components[i].ClassInfo, 'Font');
if assigned(pi) then TFont(GetOrdProp(aComponent.Components[i],pi)).Color := clWhite;
end; end;
директивы условной компиляции {$C+} и {$C-} -
директивы проверки утверждений {$I+} и {$I-} - директивы контроля
ввода/вывода {$M} и {$S} - директивы, определяющие размер стека
{$M+} и {$M-} - директивы информации времени выполнения о типах
{$Q+} и {$Q-} - директивы проверки переполнения целочисленных операций
{$R} - директива связывания ресурсов
{$R+} и {$R-} - директивы
проверки диапазона
{$APPTYPE CONSOLE} - директива создания консольного
приложения
1) Директивы компилятора, разрешающие или запрещающие
проверку утверждений.
По умолчанию {$C+} или {$ASSERTIONS ON}
Область действия локальная
Описание
Директивы
компилятора $C разрешают или запрещают проверку утверждений. Они влияют на
работу процедуры Assert,используемой при отладке программ. По умолчанию
действует директива {$C+} и процедура Assert генерирует исключение
EAssertionFailed, если проверяемое утверждение ложно.
Так как эти
проверки используются только в процессе отладки программы, то перед ее
окончательной компиляцией следует указать директиву {$C-}. При этом работа
процедур Assert будет блокировано и генерация исключений EassertionFailed
производиться не будет.
Директивы действуют на весь файл исходного кода
независимо от того, в каком месте файла они расположены.
2) Директивы компилятора, включающие и выключающие
контроль файлового ввода-вывода.
По умолчанию {$I+} или {$IOCHECKS ON}
Область действия локальная
Описание
Директивы
компилятора $I включают или выключают автоматический контроль результата вызова
процедур ввода-вывода Object Pascal. Если действует директива {$I+}, то при
возвращении процедурой ввода-вывода ненулевого значения генерируется
исключение EInOutError и в его свойство errorcode заносится код ошибки.
Таким образом, при действующей директиве {$I+} операции ввода-вывода
располагаются в блоке try...except, имеющем обработчик исключения EInOutError.
Если такого блока нет, то обработка производится методом
TApplication.HandleException.
Если действует директива {$I-}, то
исключение не генерируется. В этом случае проверить, была ли ошибка, или ее не
было, можно, обратившись к функции IOResult. Эта функция очищает ошибку и
возвращает ее код, который затем можно анализировать. Типичное применение
директивы {$I-} и функции IOResult демонстрирует следующий пример:
{$I-}
AssignFile(F,s); Rewrite(F);
{$I+} i:=IOResult; if
i<>0 then
case i of 2: .......... 3:
.......... end;
В этом примере на время открытия файла
отключается проверка ошибок ввода вывода, затем она опять включается, переменной
i присваивается значение, возвращаемое функцией IOResult и, если это значение не
равно нулю (есть ошибка), то предпринимаются какие-то действия в зависимости от
кода ошибки. Подобный стиль программирования был типичен до введения в Object
Pascal механизма обработки исключений. Однако сейчас, по-видимому, подобный
стиль устарел и применение директив $I потеряло былое значение.
3)
Директивы компилятора, определяющие размер стека
По умолчанию {$M
16384,1048576}
Область действия глобальная
Описание
Локальные переменные в процедурах и функциях размещаются в стеке
приложения. При каждом вызове процедуры или функции ее локальные переменные
помещаются в стек. При выходе из процедуры или функции эти локальные процедуры
удаляются из стека. Директивы компилятора $M задают параметры стека
приложения: его минимальный и максимальный размеры. Приложение всегда
гарантированно имеет размер стека, равный его минимальной величине. Если при
запуске приложения Windows обнаруживает, что не может выделить этот
минимальный объем памяти, то выдается сообщение об этой ошибке.
Если во
время работы выясняется, что минимального размера стека не хватает, то размер
увеличивается на 4 K, но не более, чем до установленного директивой
максимального размера. Если увеличение размера стека невозможно из-за нехватки
памяти или из-за достижения его максимальной величины, генерируется исключение
EStackOverflow. Минимальный размер стека по умолчанию равен 16384 (16K). Этот
размер может изменяться параметром minstacksize директивы {$M} или
параметром number директивы {$MINSTACKSIZE}.
Максимальный размер стека
по умолчанию равен 1,048,576 (1M). Этот размер может изменяться параметром
maxstacksize директивы {$M} или параметром number директивы {$MAXSTACKSIZE
number}. Значение минимального размера стека может задаваться целым числом в
диапазоне между1024 и 2147483647. Значение максимального размера стека должно
быть не менее минимального размера и не более 2147483647. Директивы задания
размера стека могут включаться только в программу и не должны использоваться в
библиотеках и модулях.
В Delphi 1 имеется процедура компилятора {$S},
осуществляющая переключение контроля переполнения стека. Теперь этот процесс
полностью автоматизирован и директива {$S} оставлена только для обратной
совместимости.
4) Директивы компилятора, включающие и выключающие
генерацию информации времени выполнения о типах (runtime type information -
RTTI).
По умолчанию {$M-} или {$ TYPEINFO OFF}
Область действия
локальная
Описание
Директивы компилятора $I включают или
выключают генерацию информации времени выполнения о типах (runtime type
information - RTTI). Если класс объявляется в состоянии {$M+} или является
производным от класса объявленного в этом состоянии, то компилятор генерирует
RTTI о его полях, методах и свойствах, объявленных в разделе published. В
противном случае раздел published в классе не допускается. Класс
TPersistent, являющийся предшественником большинства классов Delphi и все
классов компонентов, объявлен в модуле Classes в состоянии {$M+}. Так что для
всех классов, производных от него, заботиться о директиве {$M+}не приходится.
5) Директивы компилятора, включающие и выключающие
проверку переполнения при целочисленных операциях
По умолчанию {$Q-} или
{$OVERFLOWCHECKS OFF}
Область действия локальная
Описание
Директивы компилятора $Q включают или выключают проверку переполнения
при целочисленных операциях. Под переполнением понимается получение результата,
который не может сохраняться в регистре компьютера. При включенной директиве
{$Q+} проверяется переполнение при целочисленных операциях +, -, *, Abs, Sqr,
Succ, Pred, Inc и Dec. После каждой из этих операций размещается код,
осуществляющий соответствующую проверку. Если обнаружено переполнение, то
генерируется исключение EIntOverflow. Если это исключение не может быть
обработано, выполнение программы завершается.
Директивы $Q проверяют
только результат арифметических операций. Обычно они используются совместно с
директивами {$R}, проверяющими диапазон значений при присваивании. Директива
{$Q+} замедляет выполнение программы и увеличивает ее размер. Поэтому обычно она
используется только во время отладки программы. Однако, надо отдавать себе
отчет, что отключение этой директивы приведет к появлению ошибочных результатов
расчета в случаях, если переполнение действительно произойдет во время
выполнении программы. Причем сообщений о подобных ошибках не будет.
6) Директива компилятора, связывающая с выполняемым
модулем файлы ресурсов
Область действия локальная
Описание
Директива компилятора {$R} указывает файлы ресурсов (.DFM, .RES),
которые должны быть включены в выполняемый модуль или в библиотеку. Указанный
файл должен быть файлом ресурсов Windows. По умолчанию расширение файлов
ресурсов - .RES. В процессе компоновки компилированной программы или библиотеки
файлы, указанные в директивах {$R}, копируются в выполняемый модуль.
Компоновщик Delphi ищет эти файлы сначала в том каталоге, в котором расположен
модуль, содержащий директиву {$R}, а затем в каталогах, указанных при выполнении
команды главного меню Project | Options на странице Directories/Conditionals
диалогового окна в опции Search path или в опции /R командной строки DCC32.
При генерации кода модуля, содержащего форму, Delphi автоматически
включает в файл .pas директиву {$R *.DFM}, обеспечивающую компоновку файлов
ресурсов форм. Эту директиву нельзя удалять из текста модуля, так как в
противном случае загрузочный модуль не будет создан и сгенерируется исключение
EResNotFound.
7) Директивы компилятора, включающие и
выключающие проверку диапазона целочисленных значений и индексов
По
умолчанию {$R-} или {$RANGECHECKS OFF}
Область действия локальная
Описание
Директивы компилятора $R включают или выключают
проверку диапазона целочисленных значений и индексов. Если включена директива
{$R+}, то все индексы массивов и строк и все присваивания скалярным переменным и
переменным с ограниченным диапазоном значений проверяются на соответствие
значения допустимому диапазону. Если требования диапазона нарушены или
присваиваемое значение слишком велико, генерируется исключение ERangeError. Если
оно не может быть перехвачено, выполнение программы завершается.
Проверка диапазона длинных строк типа Long strings не производится.
Директива {$R+} замедляет работу приложения и увеличивает его размер.
Поэтому она обычно используется только во время отладки.
8)
Директива компилятора, связывающая с выполняемым модулем файлы ресурсов
Область действия локальная
Описание
Директива
компилятора {$R} указывает файлы ресурсов (.DFM, .RES), которые должны быть
включены в выполняемый модуль или в библиотеку. Указанный файл должен быть
файлом ресурсов Windows. По умолчанию расширение файлов ресурсов - .RES. В
процессе компоновки компилированной программы или библиотеки файлы, указанные в
директивах {$R}, копируются в выполняемый модуль. Компоновщик Delphi ищет эти
файлы сначала в том каталоге, в котором расположен модуль, содержащий директиву
{$R}, а затем в каталогах, указанных при выполнении команды главного меню
Project | Options на странице Directories/Conditionals диалогового окна в опции
Search path или в опции /R командной строки DCC32.
При генерации кода
модуля, содержащего форму, Delphi автоматически включает в файл .pas директиву
{$R *.DFM}, обеспечивающую компоновку файлов ресурсов форм. Эту директиву нельзя
удалять из текста модуля, так как в противном случае загрузочный модуль не будет
создан и сгенерируется исключение EResNotFound.
Взято из FAQ: http://blackman.km.ru/myfaq/cont4.phtml Если
вы присвоили свойству имя 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 := 0to ???? dobegin TableName := ????[I]; Proc(TableName); end; end; Затем
зарегистрируйте данный редактор свойства следующим образом: RegisterPropertyEditor(TypeInfo(string), TcsNotebook, 'TableName', TTableNameProperty);
F1, Ctrl-F1 - контекстная помощь F3 - продолжить поиск F4 - выполнить
программу до положения курсора F5 - поставить Break Point F7 -
трассировать с заходом в процедуры F8 - трассировать без захода в процедуры
F9 - запустить программу F10, Ctrl-F10 - активизировать главное меню
F11 - открыть/закрыть Object Inspector F12 - переход между формой и
кодом Ctrl-F2 - прервать выполнение программы Ctrl-F3 - посмотреть стек
Ctrl-F4 - закрыть текущий модуль Ctrl-F5 - Watch List Ctrl-F7 -
Evalute/Modify (просмотр значений переменных и их изменение) Ctrl-F9 -
Компиллировать проэкт Ctrl-F11 - Открыть проэкт Ctrl-F12 - Список
модулей проэкта Shift-F7 - Трассировка заходя в каждую процедуру и
перескакивание в каждое возникающее событие Shift-F10 - всплывающее меню
Shift-F11 - добавить модуль к проэкту Shift-F12 - список форм проэкта
для быстрой навигации Alt-F4, Ctrl-Shift-F4 - закрыть проэкт и все файлы.
Alt-F6 - палитра выравнивания Alt-Ctrl-F11 - Мененджер проэктов
Alt-Shift-F4 - Закрыть все окна, но проэкт не закрывать Ctrl-Shift-0..9
- Поставить метку 0..9 Ctrl-0..9 - Перейти на метку 0..9 Alt-0 - Список
окон Ctrl-Enter - Открыть файл с именем стова на котором курсор стоит
Ctrl+клик мышкой на слове - перейти на определение этого слова
Alt+функции выделения текста (мышкой или клавиатурой) - выделение
вертикального блока Ctrl-Shift-Up/Down - переход от объявления процедуры к
ее реализации Ctrl-Shift-C - закончить метод (если он описан - создать
шаблон для реализации, если есть реализация - объявить метод)
Ctrl-Shift-E - открыть эксплорер кода Ctrl-Shift-R - начать/завершить
запись макро Ctrl-Shift-P - выполнить записанное макро Ctrl-Shift-T -
добавить в To Do лист Ctrl-Shift-U - уменьшить отступ выделенного блока
Ctrl-Shift-I - Увеличить отступ выделенного блока Ctrl-Shift-S - Grep
Search Ctrl-Shift-G - Вставить GUID Ctrl-Shift-B - Посмотреть иерархию
классов Ctrl-Alt-W - Watch List Ctrl-Alt-R - Grep Result Ctrl-Alt-T
- Список потоков проэкта Ctrl-Alt-A - Вставить дату Ctrl-Alt-S - Вызовы
стэка Ctrl-Alt-H - Шаблон для документации модуля Ctrl-Alt-L - Локальные
переменные Ctrl-Alt-V - История событий Ctrl-Alt-B - Список Break Points
Ctrl-Alt-M - Модули Ctrl-N - Вставить пустую сторку, курсор остается на
текущей строке Ctrl-M, Enter - Вставить пустую сторку, курсор переходит на
следующую строку Ctrl-E - Поиск по мерре введения символов (Incremental
Search) Ctrl-R - Search and Replace Ctrl-A - Выделить весь текст (только
дельфи 6) Ctrl-T - Удалить от курсора до конца слова Ctrl-Y - Удалить
строку Ctrl-O + O - Вставить все текущие опции компилляции по позиции
курсора Ctrl-P - префикс, после которого можно вставить любой ASCII код
Ctrl-S - сохранить текущий файл Ctrl-F - открыть диалог поиска
Ctrl-J - лист шаблонов Ctrl-K + С - копирование блока без буфера обмена
Ctrl-Z - отмена Ctrl-X - вырезать Ctrl-С - копировать Ctrl-V -
вставить Ctrl-B - список буфферов
Tab Selects the next component Shift+Tab
Selects the previous component Arrow Keys Selects the nearest component in
the direction pressed Ctrl+Arrow Keys Moves the selected component one pixel
at a time Shift+Arrow Keys Moves the selected component one pixel at a time
Ctrl+Shift+Arrow Keys Moves the selected component one grid at a time (when
Snap to Grid is enabled) Del Deletes the selected component Esc Selects
the containing group (usually the form or group box)
F11 Toggles control
between the Object Inspector and the last active form or unit F12 Toggles
between the form and its associated unit Ctrl+F12 Displays the View Unit
dialog box Shift+F12 Displays the View Form dialog box
Project
Manager keyboard shortcuts
Arrow Keys Selects forms and units Alt+A
Adds a form or unit to the project Alt+R Removes a form or unit from the
project Alt+U Views the selected unit Alt+F Views the selected form
Alt+O Displays the Project Options dialog box Alt+D Updates the current
project Enter Views the selected unit Shift+Enter Views the selected
form Ins Adds a file to the project Del Removes a file from the project
Object Inspector keyboard shortcuts
Ctrl+I Opens the Object
Selector Up and Down Arrow Keys Selects properties or event handlers
Left and Right Arrow Keys Edits the value in the value or event column
Tab Toggles between the property and value columns in the Object Inspector
Tab+ Jumps directly to the first property beginning with the letter
Ctrl+Tab Toggles between the properties and events tabs in the Object
Inspector Page Up Moves up one screen of properties
Page Down Moves
down one screen of properties Alt+F10 Toggles expand and contract
Alt+Down Opens a drop-down list for a property. Ctrl+Down Opens the
object list drop-down. Ctrl+Enter Selects the ellipsis button (if available)
in a selected property.
Package editor
Enter Lets you view
the selected unit's source code. Ins Adds a unit to the current folder
(Contains or Requires). Del Removes the selected item from the package.
Ctrl+B Compiles the current package. If changes to the package are required,
a dialog box appears that lists the changes that will be made to the package
before it is compiled. Ctrl+I Installs the current package as a design time
package. If changes to the package are required, a dialog box appears that lists
the changes that will be made to the package before it is compiled.
CPU
Window
Shift+Left Arrow Move left one pane. Shift+Right Arrow Move
right one pane Shift+Up Arrow Move up one pane. Shift+Down Arrow Move
down one pane.
Иногда надо выполнить разный код в зависимости от версии Дельфи, особенно
актуально это при разработки компонентов и модулей, которые используются в
разных приложениях.
В Дельфи предопределены специальные константы
компилляции для этого:
var item: TMenuItem; begin {get reference to delphi's mainmenu. You can handle it like a common TMainMenu} with (BorlandIDEServices as INTAServices).GetMainMenu do begin item := TMenuItem.Create(nil);
item.Caption := 'A Mewn caption';
Items.Add(item); end; end;
Взято с
сайта http://www.swissdelphicenter.ch/en/tipsindex.php
{
This unit can be compiled into a package and will then appear in the delphi
Help menu. } unit SDCSimpleExpert;
interface
uses ToolsApi;
type TSDCSimpleExpert = class(TNotifierObject, IOTAMenuWizard, IOTAWizard)
public function GetIDString: string;
function GetName: string;
function GetState: TWizardState;
procedure Execute;
function GetMenuText: string; end;
procedureRegister;
implementation
uses Dialogs;
procedureRegister; begin {register expert} RegisterPackageWizard(TSDCSimpleExpert.Create); end;
{ TSDCSimpleExpert }
procedure TSDCSimpleExpert.Execute;
begin {code to execute when menu item is clicked} ShowMessage('Hello SwissDelphiCenter Simple Expert.'); end;
function TSDCSimpleExpert.GetIDString: string; begin {unique expert identifier} Result := 'SwissDelphiCenter.SimpleExpert'; end;
function TSDCSimpleExpert.GetMenuText: string; begin {caption of menu item in help menu} Result := 'SwissDelphiCenter Simple Expert'; end;
function TSDCSimpleExpert.GetName: string; begin {name of the expert} Result := 'SwissDelphiCenter Simple Expert'; end;
function TSDCSimpleExpert.GetState: TWizardState;
begin Result := [wsEnabled]; end;
Для этого достаточно текущее разрешение экрана и в соответствии с ним изменить
дескриптор иконки приложения. Естевственно, что Вам прийдётся создать в ресурсах
новые иконки.
Поместите следующий код в файл проекта (.DPR) Вашего
приложения: