|
||||||||||||||||||
|
PascalОбъектыПроблема циклических ссылокУ меня имеется объект A и объект B, и им обоим нужно вызывать методы друг друга…
Создание множества экземпляров
list:=Tlist.create; For i:= 1 to 1000 do begin SSObject:=TSSObject.create; {поместите куда-нибудь ссылку на созданный объект - например, в Tlist} list.add(SSObject); end; ПараметрыПередача функции как параметра
В нашем случае лучшим решением будет использование процедурного типа. Допустим, что 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 SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } 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. Вы можете сделать две вещи. Во-первых, если вы хотите использовать для передачи longint, напишите следующий код: i := longint(@foo) Другая вещь, которую вы можете сделать — исключить работу с longint и вызывать функцию dll следующим образом: DLLfunction(@foo); Имейте в виду, что если вы собираетесь вызывать foo из DLL, то необходимо предусмотреть вопросы совместимости, для получения дополнительной информации почитайте описание функции MakeProcInstance. Проблема передачи записи
Может это не то, что вы ищете, но идея такая: Определите базовый класс с именем, скажем, allrecs: tAllrecs = class function getVal(field: integer): string; virtual; end; Затем создаем классы для каждой записи: recA = class(tAllrecs) this: Integer; that: String; the_other: Integer; function getVal(field: integer): string; virtual; end; Затем для каждой функции класса определите возвращаемый результат: function recA.getVal(field: integer); string; begin case field of 1: getVal := intToStr(this); 2: getVal := that; 3: getVal := intToStr(the_other); end; end; Затем вы можете определить function myFunc(rec: tAllrecs; field: integer); begin label2.caption := allrecs.getVal(field); end; затем вы можете вызвать myFunc с любым классом, производным от tAllrecs, например: myFunc(recA, 2); myFunc(recB, 29); (getVal предпочтительно должна быть процедурой (а не функцией) с тремя var-параметрами, возвращающими имя, тип и значение.) Все это работает, т.к. данный пример я взял из моего рабочего проекта. [Sid Gudes, cougar@roadrunner.com] Если вы хотите за один раз передавать целую запись, установите на входе ваших функций/процедур тип 'array of const' (убедитесь в правильном приведенни типов). Это идентично 'array of TVarRec'. Для получения дополнительной информации о системных константах, определяемых для TVarRec, смотри электронную справку по Delphi. УказателиУказатель на функцию I
Это то, что я нашел при создании простой машины состояний: Ниже приведен простой пример для Borland Delphi, использующий указатели функций для управления программным потоком. Просто создайте простую форму с единственной кнопкой и скопируйте код из Unit1 во вновь созданный модуль. Добавьте к проекту Unit2 и скомпилируйте проект. Дайте мне знать, если у вас возникнут какие-либо проблемы. interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; CurrProc : LongInt; MyVal : LongInt; implementation uses Unit2; {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); var NewProc : LongInt; MyString : string; begin CurrProc := 2; { начальная точка в таблице методов } MyVal := 0; { вспомогательная переменная } NewProc := 0; { возвращаемое значение для следующего индекса в таблице методов } while CurrProc < 6 do begin { выполняем текущий индекс в таблице методов и получаем следующую процедуру } NewProc := ProcTable[CurrProc](MyVal); { просто показываем значения NewProc и CurrProc } FmtStr(MyString, 'NewProc [%d] CurrProc [%d]', [NewProc, CurrProc]); MessageDlg(MyString, mtInformation, [mbOK], 0); { присваиваем текущую процедуру возвращаемой процедуре } CurrProc := NewProc; end; end; end. { Это простой пример, определяющий массив указателей на функции } interface type { определяем Procs как функцию } Procs = function(var ProcNum : LongInt): LongInt; var { объявляем массив указателей на функции } ProcTable : Array [1..5] of Procs; { определения интерфейсов функций } function Proc1(var MyVal : LongInt) : LongInt; far; function Proc2(var MyVal : LongInt) : LongInt; far; function Proc3(var MyVal : LongInt) : LongInt; far; function Proc4(var MyVal : LongInt) : LongInt; far; function Proc5(var MyVal : LongInt) : LongInt; far; implementation uses Dialogs; function Proc1(var MyVal : LongInt) : LongInt; begin MessageDlg('Процедура 1', mtInformation, [mbOK], 0); Proc1 := 6; end; function Proc2(var MyVal : LongInt) : LongInt; begin MessageDlg('Процедура 2', mtInformation, [mbOK], 0); Proc2 := 3; end; function Proc3(var MyVal : LongInt) : LongInt; begin MessageDlg('Процедура 3', mtInformation, [mbOK], 0); Proc3 := 4; end; function Proc4(var MyVal : LongInt) : LongInt; begin MessageDlg('Процедура 4', mtInformation, [mbOK], 0); Proc4 := 5; end; function Proc5(var MyVal : LongInt) : LongInt; begin MessageDlg('Процедура 5', mtInformation, [mbOK], 0); Proc5 := 1; end; initialization { инициализируем содержание массива указателей на функции } @ProcTable[1] := @Proc1; @ProcTable[2] := @Proc2; @ProcTable[3] := @Proc3; @ProcTable[4] := @Proc4; @ProcTable[5] := @Proc5; end. Я думаю это можно сделать приблизительно так: объявите в каждой форме процедуры, обрабатывающие нажатие кнопки, типа процедуры CutButtonPressed(Sender:TObject) of Object; затем просто назначьте события кнопок OnClick этим процедурам при наступлении событий форм OnActivate. Этот способ соответствует концепции ОО-программирования, но если вам не нравится это, то вы все еще можете воспользоваться указателями функций, которая предоставляет Delphi. Объявите базовый класс формы с объявлениями абстрактных функций для каждой функции, которую вы хотите вызывать из вашего toolbar. Затем наследуйте каждую вашу форму от базового класса формы и создайте определения этих функций. Пример: (Здесь может встретиться пара синтаксических ошибок — я не компилил это) type TBaseForm = class(TForm) public procedure Method1; virtual; abstract; end; type TDerivedForm1= class(TBaseForm) public procedure Method1; override; end; TDerivedForm2= class(TBaseForm) public procedure Method1; override; end; procedure TDerivedForm1.Method1; begin … end; procedure TDerivedForm2.Method1; begin … end; {Для вызова функции из вашего toolbar, получите активную в настоящий момент форму и вызовите Method1} procedure OnButtonClick; var AForm: TBaseForm; begin AForm := ActiveForm as TBaseForm; AForm.Method1; end Указатель на функцию II
Что лично я использую, чтобы вызвать какую-то функцию из DLL: 1. Объявите тип: type TYourDLLFunc = function(Parm1: TParm1; Parm2: TParm2): TParm3; 2. Объявите переменную этого типа: var YourDllFunc: TYourDLLFunc; 3. Получаем дескриптор DLL: DLLHandle := LoadLibrary('YourDLL.DLL'); Получаем адрес функции: @YourDLLFunc := GetProcAddress(DLLHandle, 'YourDLLFuncName'); Для использования функции теперь используйте переменную YourDLLFunc, например: Parm3 := YourDLLFunc(Parm1, Parm2); Использование указателей на целое
Сначала вы должны создать тип: Type Pinteger: ^Integer; Var MyPtr: Pinteger; Мне кажется, что в начале вы использовали плохой пример, имеет смысл использовать 32-битный указатель для 16-битной величины или распределять 10 байт для переменной. Pascal позволяет вам использовать методы NEW и DISPOSE, которые автоматически распределяют и освобождают правильные размеры блока. Например, NEW(MyPtr) = GetMem(MyPtr, Sizeof(MyPtr)). Возможно, вы захотите подсчитать количество целочесленных переменных. В этом случае ознакомьтесь с возможностями TList. Пока лучше используйте линейный массив (или указатель на первый элемент, чтобы вычислить их количество, достаточно разделить количество занимаемой массивом памяти на количество элементов). Для полноты, это должно быть: NEW(MyPtr) = GetMem(MyPtr, SizeOf(MyPtr^)); SizeOf(MyPtr) всегда будет равен 4 байта, как 16-битный указатель. Если я правильно разобрался в том, что вы хотите (динамический массив целых, количество элеметнов которого может быть известно только во время выполнения приложения), вы можете сделать так: Type pIntArr = ^IntArr; IntArr = Array[1..1000] of Integer; Var MyPtr : pIntArr; Begin GetMem(MyPtr, 10); { 10 = SizeOf(Integer) * 5 !!} { MyPtr[2]:=1; } <<<< Заполняем массив >>>> MyPtr[2]^:=1; FreeMem(MyPtr,10); End; Технология похожа на ту, которуя Delphi использует при работе с pchar. Синтаксис очень похож: type intarray = array[0..20000] of integer; procedure TForm1.Button1Click(Sender: TObject); var xptr: ^IntArray; begin GetMem(xptr, 10); xptr^[idx] := 1; { где idx от 0 до 4, поскольку мы имеем 10 байте = 5 целых } FreeMem(xptr, 10); end; Обратите внимание на то, в вам в действительности нет необходимости распределять массив для 20,000 элементов, но проверка диапазона Delphi не будет работать, если диапазон равен 20,000. (Предостережение будущим пользователям!) ПамятьФункция MemAvail для Delphi2?
В Delphi 1, для того, чтобы получить самый большой возможный участок памяти, мы могли использовать функцию MemAvail, существует ли эквивалент этой функции в Delphi 2? Нет. Но чтобы получить аппроксимированную сумму доступной памяти, можно воспользоваться функцией API GlobalMemoryStatus (через поле dwAvailVirtual возвращаемой структуры TMemoryStatus). Steve Schafer Как работать с блоками памяти размером более 64K?Nomadic советует: Так можно помещать в один блок памяти записи из TList (TCollection): imlementation { To use the value of AHIncr, use Ofs(AHIncr). } procedure AHIncr; far; external 'KERNEL' index 114; const NEXT_SELECTOR: string[13] = 'NEXT_SELECTOR'; function WriteData: THandle; var DataPtr: PChar; i: Integer; begin Result := GlobalAlloc(GMEM_SHARE or GMEM_ZEROINIT, {pазмеp большого блока}); if Result = 0 then Exit; DataPtr := GlobalLock(Result); {записываем кол-во эл-тов} Inc(DataPtr, {pазмеp счетчика эл-тов}) for i := 0 to {некий}Count-1 do begin if LongInt(PtrRec(DataPtr).Ofs) + {pазмеp подблока} >l= $FFFF then begin Move(NEXT_SELECTOR, DataPtr^, SizeOf(NEXT_SELECTOR)); {некая константа} { коppекция сегмента } PtrRec(DataPtr).Seg := PtrRec(DataPtr).Seg + Ofs(AHIncr); PtrRec(DataPtr).Ofs := $0; end; Inc(DataPtr, {pазмеp нового блока}); end; { for i } GlobalUnlock(Result); end; procedure ReadData(DataHdl: THandle); var DataPtr : PObjectCfgRec; RecsCount: Integer; i: Integer; begin if DataHdl = 0 then Exit; DataPtr := GlobalLock(DataHdl); RecsCount := PInteger(DataPtr)^; Inc(PInteger(DataPtr)); for i := 1 to RecsCount do begin { обpаботать данные } Inc(DataPtr); if PString(DataPtr)^ = NEXT_SELECTOR then begin PtrRec(DataPtr).Seg := PtrRec(DataPtr).Seg + Ofs(AHIncr); PtrRec(DataPtr).Ofs := $0; end; end; { for i } GlobalUnlock(DataHdl); end; СобытияНазначение обработчика события OnClick пункту меню, созданному во время выполнения программы
Поскольку метод OnClick является свойством, то при динамическом создании элемента меню вы можете назначить имя метода обработчику OnClick: theMenuitem.OnClick := TheOnClickHandler; Затем, в обработчике OnClick, вы приводите sender к TMenuItem и читаете имя: procedure theform.TheOnClickHandler(Sender: TObject); var fName: String; begin fName := TMenuItem(Sender).name; … end; События для компонентов, созданных во время работы программы I
Вы должны вручную создать метод, который будет иметь тот же самый набор параметров, как и у события, которое вы хотите обработать. Затем вы должны вручную установить свойство OnXXX, чтобы она указывала на метод, который вы создали. Пример: TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private FMyButton: TButton; protected procedure Button1Click(Sender: TObject); {Кодируем это вручную,для соответствия} {структуреTNotifyEvent} end; procedure TForm1.FormCreate(Sender: TObject); begin FMyButton := TButton.Create; {Здесь устанавливаем позицию, заголовок и все остальное} FMyButton.OnClick := MyButtonClick; end; procedure TForm1.MyButtonClick(Sender: TObject); begin ShowMessage('Эй! Ты нажал на мою кнопку!'); end; События для компонентов, созданных во время работы программы II
Вот простейший код для нового проекта с одной кнопкой и меню. (Надеюсь, в этом ничего сложного нет ... :) procedure TForm1.Button1Click(Sender: TObject); var NewItem: TMenuItem; begin NewItem := TMenuItem.Create(Form1); NewItem.Caption := 'Динамический элемент ...'; NewItem.OnClick := xyz;MainMenu1.Items.Insert(0, NewItem); ←Примечание: рекомендую бегло ознакомиться с Delphi-примером для команды Insert… end; {Любая старая 'xyz'-процедура (в настоящее время может быть определена одна, например, Form1.DblClick)} procedure TForm1.xyz(Sender: TObject); begin showmessage('Запусти эту процедуру !!'); end; Примечание: Если вы пользуетесь неопределенной процедурой, вам понадобиться объявить ее. Лично я все это сделал в «верхнем правом углу» объявления типа формы, примерно так: private { Private declarations } public { Public declarations } procedure xyz(Sender: TObject); ←К этой процедуре могут иметь доступ не только события Form1 … Установите свойство обработчика события (например, OnClick, OnDblClick, OnMouseDown и пр.) на процедуру, которую вы создали для обработки этого события. Вам нужно убедиться в том, что параметры в точности соответствуют параметрам ожидаемого заданного обработчика события. Например: MySpeedButton.OnClick := MyClickEventHandler; где… procedure MyClickEventHandler(Sender: TObject); begin end; МассивыДинамические массивы VSottNick пишет: Если хочется, чтобы в многомерном массиве был разный размер у разных измерений например: VarArray: array[1..2, 1..?] of TType , где ? зависит от "строки" массива (1..2) То дозволяется сделать так: 1. Объявление Var VarArray: array of array of array………… 2. Установка длин SetLength(VarArray, Razmernost1); // У первого измерения SetLength(VarArray[1], Razmernost2); // У второго измерения первой «строки» SetLength(VarArray[2], Razmernost3); // У второго измерения второй «строки» SetLength(VarArray[n], Razmernost4); // У второго измерения n-ной «строки» SetLength(VarArray[1][1], Razmernost5); // У третьего измерения первой «строки» первого «столбца» SetLength(VarArray[1][2], Razmernost6); // У третьего измерения первой «строки» второго «столбца» SetLength(VarArray[n][m], Razmernost7); // У третьего измерения n-ной «строки» m-ного «столбца» т.д. Все можно изменять в процессе естественно. 3. Получение длин Razmernost1:=Length(VarArray); // У первого измерения (количество строк) Razmernost2:=Length(VarArray[1]); // У второго измерения первой «строки» (количество столбцов) Razmernost3:=Length(VarArray[2]); // У второго измерения второй «строки» (количество столбцов) Razmernost4:=Length(VarArray[n]); // У второго измерения n-ной «строки» (количество столбцов) Razmernost5:=Length(VarArray[1][1]); // У третьего измерения первой «строки» первого «столбца» Razmernost6:=Length(VarArray[1][2]); // У третьего измерения первой «строки» второго «столбца» Razmernost7:=Length(VarArray[n][m]); // У третьего измерения n-ной «строки» m-ного «столбца» 4. Обращение VarArray[n][m][o][p][r]:=1; // :Integer // К элементу n-ной «строки», m-ного «столбца», // o-того «?», p-того «?», r-того «?» 5. Обнуление (освобождение памяти) SetLength (VarArray, 0); // Всех сразу Динамические массивы VI
Например, если вам необходимо сохранить «GIZMOS» в вашем массиве, сделайте следующее: CONST MaxGIZMOS = $FFFF Div (SizeOf(GIZMOS)) { или что-то другое, смотря какой максимальный размер GIZMOS вы планируете...} TYPE pGIZMOArray = ^GIZMOArray; GIZMOArray = Array[1..MaxGIZMOS] of GIZMOS; VAR TheGIZMOS: pGIZMOArray; GIZMOcount: integer; BEGIN GetMem(TheGIZMOS,(GIZMOcount+1)*SizeOf(GIZMO)); {Нужна дополнительная единица, поскольку массив GetMem ведет отсчет с нуля…} TheGIZMOS^[index] := Whatever; ну и так далее… TList — такой динамический массив. Для получения дополнительной информации обратитесь к электронной справке. Если вы хотите это делать сами, то вам необходимо использовать GetMem для получения указателя на распределенную динамическую память, и затем FreeMem для освобождения памяти, занятой динамическим массивом. Tlist сделает это за вас самым надежным образом. Динамические массивы VII
Существует несколько способов сделать это. Применять тот или иной способ зависит от того, какой массив вы используете — массив строк или массив чисел (целые, натуральные и пр.). 1. Если вам необходим простой динамический одномерный массив строк, я предлагаю вам взглянуть на компонент tStringList, он сам заботится о функциях управления и легок в использовании. 2. Если вам необходим динамический многомерный массив строк, вы также можете воспользоваться tStringList (в случае, если число элементов вашего массива не превышает лимит для tStringList, я полагаю он равен 16,000). Чтобы сделать это, создайте функцию линейного распределения как показано ниже: Допустим у вас есть трехмерный массив строк, текущее измерение [12,80,7], и вы хотите найти элемент [n,m,x]. Вы можете найти этот элемент в приведенном одномерном массиве с помощью формулы ((n-1)*80*7 + (m-1)*80 + x). Затем вы можете использовать это в качестве индекса в tStringList. Для диманического изменения одной из границ массива, используйте метод tStringList Move, служащий как раз для таких целей. (Метод состоит из некоторых технологических внутренних циклов, но выполняются они очень быстро, поскольку tStringList манипулирует не с самими строками, а с указателями на них.) 3. Если вам необходим динамический одномерный массив чисел, то в общих словах я приведу его ниже, но есть масса мелких деталей. Объявите указатель на тип массива, имеющего максимальное количество элементов данного типа (помните о том, что Delphi-16 позволяет иметь типам область памяти, ограниченной 64K), например так: type bigArray: array[1..32000] of integer; {или ^double, или что-то еще} pMyArray: ^bigArray; затем распределите сам массив: getMem (pMyArray, sizeof(integer) * n); где n — количество элементов. После этого вы можете ссылаться на элементы массива следующим образом: pMyArray^[51] Не забудьте освободить массив с помощью FreeMem после того, как вы его использовали. Изменить размер массива, определить новый указатель, перераспределить или обменяться с другим массивом можно так: pTemp: ^bigArray; getMem(pTemp, sizeof(integer) * newnumelements); memcopy(pTemp, pMyArray, sizeof(integer)*n); {n – количество элементов в pMyArray} freeMem(pMyArray, sizeof(integer)*n); pMyArray := pTemp; 4. Если вам необходим многомерный массив чисел, скомбинируйте технику, описанную в пункте (3), с функцией распределения, описанной в пункте (2). 5. Если для вашего массива необходим участок памяти больше чем 64K, вам необходимо разработать список указателей на участки памяти, но эта тема выходит за рамки данной статьи. Лично я инкапсулировал все в своем объекте. Я использую, как я это называю, «Basic String Object» (BSO), базовый строковый объект, который осуществляет динамическое распределение и освобождение памяти для строк любого размера. Непосредственно это PChar, указывающий на распределенную память. У меня существует два внешних свойства: AsString и AsPChar. Также у меня есть различные свойства и методы, позволяющие иметь различные способы доступа и манипулировать строками. Я написал свои собственные malloc(), calloc() и realloc(), используя частные методы объекта TString для сканирования распределенной памяти. Это классно работает, когда мне нужно «захватить» блок памяти. С помощью двух методов я могу распределить необходимую мне память (блоками, так что это не занимает много процессорного времени), и освобождать ее (когда существует определенный резерв – и снова так, чтобы не тратить много процессорного времени). О другой идее я уже рассказывал (открытый массив). Если вам нужна проверка выхода за границы и/или динамическое изменение размера массива, вы можете использовать метод, аналогичный методу работы со строковым объектом (описанный мною выше), но вам необходимо будет интегрировать свойство-массив по умолчанию, чтобы иметь к нему простой доступ. Это позволит вам иметь индексы и использовать нужный вам тип. TMyDynamicObject = … PROPERTY Array[idx:LONGINT]:TMyType READ GetArray WRITE PutArray DEFAULT; … VAR Mine :TMyDynamicObject; … Mine := TMyDynamicObject.Create; FOR i := 10 TO 20 DO Mine[i] := {значение} {ЧУДОВИЩНАЯ РАСТРАТА ПАМЯТИ - если вы действительно используете такие большие массивы и хэш-таблицы } Mine[-100000] := {значение} Mine[+100000] := {значение} Если в вашем распоряжении находится «редкозаполненный» массив, использование хэш-таблицы дало бы существенный выигрыш. Я преобразую индексные значения в строки, а все остальное перепоручаю TStrings, но не из-за того, что я такой ленивый, а из-за того, что он сделает это лучше меня, мне нужно всего лишь осуществить преобразование в строки. Для того, чтобы хранить все, что вы хотите, вы можете использовать TList (или TStringList.Objects)! TList.Items хранят указатели на объекты или записи, но они ничего не могут сделать с ними, поэтому вы можете привести их к типу longint, и больше о них не беспокоиться! Вот пример хранения в TList списка целых: var aList: TList; I : Integer; L : Longint; begin aList := TList.Create; L := 93823; aList.Add(Pointer(L)); aList.Add(Pointer(83293)); for I := 1 to aList.Count do L := L + Longint(aList.Items[I-1]); aList.Free; end; В TList и TStringList вы можете иметь до 16380 элементов. А теперь обещанный пример того, как можно хранить в TList записи (или объекты), вернее, указатели на них: type PMyRec = TMyRec; TMyRec = record Name: string[40]; Addr : string[25]; Comments: string; salary: Double; end; var aList: TList; aRecPtr: PMyRec; I : Integer; begin aList := TList.Create; New(aRecPtr); with aRecPtr^ do begin Name := 'Валентин'; Addr := 'неизвестен'; Comments := 'Автор Советов по Delphi'; Salary := 999000.00; end; aList.Add(aRecPtr); aList.Add(…); … for I := 1 to aList.Count do begin aRecPtr := PMyRec(aList.Items[I-1]); {что-то делаем с записью} end; {теперь избавляемся от всех записей и самого списка-объекта} for I := 1 to aList.Count do Dispose(PMyRec(aList.Items[I-1])); aList.Free; end; Динамические массивы VIIIИногда разработчик, работая с массивами, не знает какого размера массив ему нужен. Тогда Вам пригодится использование динамических массивов. var intArray : array of integer; При таком объявлении размер массива не указывается. Что бы использовать его дальше необходимо определить его размер (обратите внимание, что размер динамического массива можно устанавливать в программе): begin intArray:=(New(IntArray,100); //Размер массива? 100 end; Пример массива констант (Array of Const) III
procedure foo(a : array of const); implementation var var1: longint; var2: pointer; var3: integer; begin var1 := 12345678; var2 := @var1; var3 := 1234; foo([var1, var2, var3]); В действительности, массив array of const более корректным было бы назвать массивом array of tvariant. Tvariant — множественный выбор типов переменной, в которой можно задать номер типа. В Visual Basic у него имеется наследник. Delphi также позволяет использовать имена. Определите тип, например, так: TYPE NAME1 = Array[1..4,1..10] of Integer; Затем, в вашей секции CONST: NAME2: NAME1 = ((1,2,3,4,5,6,7,8,9,10), (1,2,3,4,5,6,7,8,9,10), (1,2,3,4,5,6,7,8,9,10), (1,2,3,4,5,6,7,8,9,10)); Массив объектов-изображений
Вы не сможете сделать это напрямую и "визуально", но если вы не возражаете против нескольких строк кода, то я покажу как это может быть просто: type TForm1 = class(TForm) … public images: array [1..10] of TImage; … end; procedure TForm1.FormCreate(…); var i: integer; begin … for i := 1 to 10 do begin images[i] := TImage.Create(self); with images[i] do begin parent := self; tag := i; { это облегчит идентификацию изображения } … установите другие необходимые свойства, например: OnClick := MyClickEventHndlr; end; end; … end; Для того, чтобы убедиться, что все модули в секции «uses» установлены правильно, бросьте на форму один такой динамический компонент, и затем удалите его, или установите его видимость в False. Более сложный способ заключается в разработке собственного компонента, делающего описанное выше. Массив TPOINT
Const ptarr : Array[0..4] Of TPoint =((x:0; y:4), … (x:4; y:4)); Создание больших массивов
В 16-битной версии Delphi нельзя сделать это непосредственно. В новой, 32-битной версии, это как-то можно сделать, но за два месяца колупания я так и не понял как. (Некоторые бета-тестеры знают как. Не могли бы они сообщить нам всю подноготную этого дела?) В 16-битной версии Delphi вам необходимо работать с блоками по 32K или 64K и картой. Вы могли бы сделать приблизительно следующее: type chunk: array[0..32767] of byte; pchunk: ^chunk; var BigArray: array[0..31] of pChunk; Для создания массива: for i := 0 to high(bigarray) do new (bigArray[i]); Для получения доступа к n-ному байту в пределах массива (n должен иметь тип longint): bigArray[n shr 15]^[n and $7fff] := y; x := bigArray[n shr 15]^[n and $7fff]; Это даже осуществляет проверку выхода за границы диапазона, если вы установили в ваших настройках опцию «range checking»! n должен находиться в диапазоне [0..32*32*1024] = [0..1024*1024] = [0..1048576]. Для освобождения массива после его использования необходимо сделать следующее: for i := 0 to high(bigarray) do dispose (bigArray[i]); СвойстваРедактор свойств для точкиTPoint не имеет информацию о типе, следовательно, вы не можете зарегистрировать для него редактор свойства. Вы можете иметь редактор свойств только для строк, реальных, порядковых чисел или указателей на объекты. Дело в том, что редактор свойств имеет только следующие методы, чтобы иметь доступ к свойствам через RTTI: GetValue/SetValue для строк (strings) GetFloatValue/SetFloatValue для натуральных чисел (floats) GetOrdValue/SetOrdValue для порядковых (и указателей) Решением может быть создание класса TPersistentPoint, являющегося наследником TPersistent и имеющего те же свойства, что и TPoint. Вы можете просто «обернуть» TPoint для хранения значений, или создать явные поля. Непосредственное использование TPoint сделает использование метода Assign легким и быстрым для кодирования. Для процедур чтения и записи вы можете использовать поля записи, как показано ниже: type TPersistentPoint = class(TPersistent) private FPoint: TPoint; published property X : integer read FPoint.X write FPoint.X; property Y : integer read FPoint.Y write FPoint.Y; end; – Mike Scott Хитрость вызова редактора свойств
Сделайте ваш редактор свойства наследником TStringListProperty (добавьте STREDIT в список используемых модулей) и согласно вашим обстоятельствам вызывайте метод предка Edit: Unit MyEditor; interface uses STREDIT; type TMyStringListProperty = class(TStringListProperty) procedure Edit; override; end; implementation procedure TMyStringListProperty.Edit; begin if { какие-то условия } then { что-то делаем } else inherited Edit; end; end. - Pat Ritchey Как убрать публичное свойство компонента/формы из списка видимых/редактируемых свойств в Инспекторе Обьектов?Nomadic советует: Из TForm property не убиpал, но из TWinControl было дело. А дело было так: interface type TMyComp = class(TWinControl) … end; procedure Register; implementation procedure Register; begin RegisterComponents('MyPage', [TMyComp]); RegisterPropertyEditor(TypeInfo(String),TMyComp,'Hint',nil); end; [ и т.д.] Тепеpь property 'Hint' в Object Inspector не видно. Рад, если чем-то помог. Если будут глюки, умоляю сообщить. Такой подход у меня сплошь и pядом. Свойство FileName в невизуальном компонентеСледующий код взят из dsgnintf.pas (иногда стоит покопаться в файлах!) для свойства TMPLayer.filename, с помощью C.Calvert… В заголовке модуля компонента… TFileNameProperty = class(TStringProperty) public function getattributes: TPropertyattributes; override; procedure Edit; override; end; добавьте функцию регистрации… RegisterPropertyEditor(Typeinfo(String), TMyComponent, 'Filename', TFileNameProperty); и код… function TFileNameProperty.GetAttributes; begin Result := [paDialog]; end; Procedure TFilenameProperty.edit; var MFileOpen: TOpenDialog; begin MFileOpen := TOpenDialog.Create(Application); MFileOpen.Filename := GetValue; MFileOpen.Filter := 'Правильный тип файлов|*.*'; (* Поместите здесь ваш собственный фильтр...*) MFileOpen.Options := MFileOpen.Options + [ofPathMustExist,ofFileMustExist]; try if MFileOpen.Execute then SetValue(MFileOpen.Filename); finally MFileOpen.Free; end; end; ЗаписиПример переменной записиВ Delphi 2.0 я пытаюсь прочесть текстовый файл и получаю проблему. Текстовый файл, который я хочу прочесть, имеет записи фиксированной длины, но в самих записях могут располагаться различные типы с различной длиной, и оканчиваться в различных позициях, в зависимости от типа. Файл выглядит примерно так: TFH.......<First record type, первый тип записи> TBH.......<Second record type, второй тип записи> TAB........<Third record type, третий тип записи> TAA........<Fourth record type, четвертый тип записи> Вы можете поймать больше одного зайца в случае объявления переменной записи, но если сделаете это правильно. Type TDataTag = Array [1..3] of Char; TDataTags = Array [0..NumOfTags-1] of TDataTag; TDataRec = packed Record tagfield: TDataTag; case integer of 0: ( поля для тэга TFH ); 1: ( поля для тэга TBH ); 2: … … end; TMultiRec = packed Record Case Boolean of false: (строка: Array [0..1024] of Char); { должно установать строку максимально возможной длины } true : ( data: TDataRec ); End; Const DataTags: TDataTags = ('TFH', 'TBH', …); var rec: TMultirec; ReadLn(datafile, rec.line); Case IndexFromDataTag(rec.data.tagfield) Of 0: … 1: … IndexFromDataTag должен искать передаваемый тэг поля в массиве DataTags. Определите все поля в TDataRec как Array [1..someUpperBound] of Char. – Peter Below Передача массива записей символов в Memo
Тема: Передача массива записей символов в Memo. Обработка больших строк в 16-битной версии Delphi задача далеко непростая. Особенно когда строки являются частью структуры записи и вы хотите передать их в TMemo. В данном совете показано как создать структуру записи размером 1000 символов, прочесть в нее содержимое Memo и затем записать ее обратно в Memo. Основной метод, который мы здесь используем — метод Memo GetTextBuf. Используемая структура записи представляет собой простую строку и массив из 1000 символов, но структура могла бы быть сложнее. unit URcrdIO; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,Forms, Dialogs, StdCtrls,dbtables; type TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; type TMyRec = record MyArray: array [1..1000] of char; mystr: string; end; var Form1: TForm1; MyRec : TMyRec; mylist : TStringlist; PMyChar : PChar; myfile : file; mb : TStream; implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin assignfile(myfile, 'c:\testblob.txt'); rewrite(myfile,1); fillchar(MyRec.MyArray,sizeof(MyRec.MyArray),#0); pmychar:=@MyRec.MyArray; StrPCopy(pmychar,memo1.text); Blockwrite(MyFile,MyRec,SizeOf(MyRec)); closefile(MyFile); end; procedure TForm1.Button2Click(Sender: TObject); begin assignfile(myfile, 'c:\testblob.txt'); reset(myfile,1); fillchar(MyRec.MyArray, sizeof(MyRec.MyArray),#0); Blockread(MyFile, MyRec, SizeOf(MyRec)); pmychar:=@MyRec.MyArray; Memo1.SetTextBuf(pmychar); end; end. Освобождение записей
Для начала необходимо привести объект к нужному типу, например, так: var i: integer; begin … for i := 0 to MyList.Count - 1 do dispose(PMyRecord(MyList[i])); MyList.Free; end; или begin for i := 0 to MyList.Count - 1 do dispose(PMyRecord(MyList.items[i])); MyList.Free; end; Items — свойство по умолчанию, поэтому вам нет необходимости определять это, хотя обратное не помешает. Теперь можно заняться созданием работоспособной и полезной функцией. В форме: var p : ^mystruct; begin new(p); … dispose(p); end; операторы new() и dispose() в точности соответствуют процедурам getmem() и freemem(), за исключением того, что компилитор распределяет количество байт под размер структуры, на которую ссылается переменная-указатель. По этой причине указатель должен быть типизированным указателем, и следущий код неверен: var p: pointer; begin new(p); end; поскольку невозможно установить размер памяти, на которую должен ссылаться указатель. С другой стороны, если вы используете getmem() и freemem(), вы можете распределять байты для нетепизированного указателя, например: var p : pointer; begin getmem(p, 32767); … freemem(p, 32767); end; СтрокиStrTok для Delphi 2
Я передалал это для работы в Delphi 2.0, код приведен ниже (эта функция первоначально была написана John Cooper 76356,3601 и модифицирована мной для адаптации под Delphi 2.0). …вот этот код: 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; – Ralph Friedman Как мне перекодировать строки из Win-кодировки в Dos-кодировку и наоборот?
Как мне перекодировать строки из Win-кодировки в Dos-кодировку и наоборот? Nomadic отвечает: A: CharToOEM, OEMToChar, CharToOEMBuff, OEMToCharBuff. Заметьте однако, что эти функции не умеют делать таких, например, вещей, как koi8-r в DOS и т. п. ТипыУ меня константы могут иметь значение, отличное от заданного. Как лечить?Nomadic советует: DX.Bug: Const из другого unit'а дает неверное значение. Симптоматика – Unit Main; Interface Uses VData; Const Wko=0.9; … Unit VData; …Implementation Uses Main; Procedure ...; Begin { вот здесь Wko=...E+230 - наверное, бесконечность } End; Похоже, это действительно bug, причем ОСОБО ОПАСНЫЙ, т.к. может исказить результаты расчетов, не вызвав заметных нарушений работы программы. В общем так. Эксперимент показал, что любая вещественная константа, определенная в интерфейсе модуля, может быть неверно (и не обязательно очень неверно – например, вместо 0.7 может появиться 0.115) прочитана в другом модуле. Баг особенно опасен тем, что он неустойчив и может пропадать и возникать без видимых причин (например, возникнуть, если предыдущая компиляция была неудачной и исчезнуть после использования константы в модуле, где она определена). Лечится (вpоде бы) указанием типа const Wko: double = 0.9; правда, теперь это уже не совсем константа… Значение вычисляемого поля Paradox вместо 25.55 становится 24.5499999…
Значение вычисляемого поля вместо 25.55 у меня выводится как 24.5499999. Скажите, что я делаю неправильно? Вы не виноваты в ошибке калькуляции! Я обнаружил ту же проблему в пакете учета, который я сейчас создаю. Мне кажется, что Borland сам делает в своем коде некий перерасчет значений. Вы можете обойти проблему с помощью функции Round: SalesIncVAT:=round(SalesIncVAT*100)/100; {дает вам два десятичных порядка} ничего экстраординарного, это основное свойство математики плавающей точки, которая обеспечивает точность только в заданном интервале десятичных цифр. Точнее говоря, тип float точен для промежуточных целых чисел и для долей, которые представляют собой сумму компонентов в степени 2, любое другое число округляется исходя из переменной точности (7 цифр для «одинарной» точности, 15 для двойной и 20 для расширенной). Можно использовать процедуру Round или str: var s : string; begin str(SalesIncVat:10:2,s); {10 символов для целой части (с точкой) и 2 десятичных цифры} Label1.Caption:=s; В справке написано, что функция FloatToStr преобразует число в строку с 15 десятичными цифрами – вот почему ваше число отображается столь причудливым образом, попробуйте эту функцию с числами типа 25.5, 25.25, 25.125 или подобными, которые имеют конечное представление в двоичной нотации, и эта проблема должна у вас исчезнуть. Или используйте функцию FloatToStrF, которой в параметрах необходимо указать общую длину строки и количество десятичных цифр. Классовые/статические/переменные общего доступа
Здесь кроется небольшая хитрость: получение эквивалентной функциональности с помощью классового метода. Просто объявите NodeCount как регулярную типизированную константу в секции implementation вашего файла. type TNode = class public NodeCount: Integer = 0; {ЭТО НЕ ДОПУСКАЕТСЯ} constructor Create; Class Function GetNodeCount : word; {другой необходимый код} end; implementation const NodeCount : word = 0; TNode.Create; begin inherited Create; Inc(NodeCount); end; Function TNode.GetNodeCount : word; begin result := NodeCount; end; Итак, теперь ваш код может выглядеть так, как вы хотели: SampleNode := TNode.Create; x := SampleNode.GetNodeCount; следующая строка также корректна: x := TNode.GetNodeCount; Чем отличается тип String в Delphi 2 и выше от аналогичного в Delphi 1?Nomadic советует: B D2 и выше на самом деле используется тип LongString вместо String, а старый тип тепеpь обзывается ShortString (о чем, кстати, написано в help). Из того же help можно узнать, что указатель LongString указывает на nullterminated string и потому возможно обычное приведение типа LongString к PChar (о чем я и написал), которое сводится просто к смене вывески. Там же можно узнать, что длина строки хранится в dword перед указателем. Есть также намек на то, что при присваивании другой строке информация не копируется, а увеличивается только счетчик ссылок. Более подробную информацию можно почерпнуть из system.pas: type StrRec = record allocSiz: Longint; refCnt: Longint; length: Longint; end; От себя добавлю: Сама переменная LongString указывает на байт, непосредственно следующий за этой процедурой, там же находится собственно значение строки. Значение '' (пустая строка) представляется как указатель nil, кстати, поэтому сpавнение str='' это быстpая операция. Теперь подробнее о счетчике ссылок. Я уже говорил, что при присваивании копирования не происходит, а только увеличивается счетчик. Когда он уменьшается? Ну, очевидно, когда в результате операции значение строки меняется, то для старого значения счетчик уменьшается. Это понятно. Более непонятно, когда освобождаются значения, на которые ссылаются поля некого класса. Это происходит в System. TObject.FreeInstance пpи вызове _FinalizeRecord, а информация берется из vtInitTable (кстати, здесь же очищаются Variant). Ещё более непонятно, когда освобождаются переменые String, которые описаны как локальные в пpоцедурах/функциях/методах. Здесь работает компилятор, которые вставляет эти неявные операции в код этой функции. Тепеpь о типе PString. Hа самом деле переменные этого типа указывают на такие же значения, как и LongString, но для переменных этого типа для всех опеpаций по созданию/копиpованию/удалению нужно помнить об этих самых счетчиках ссылок. Иногда без этого типа не обойтись. Вот опеpации для этого типа (sysutils.pas): { String handling routines } { NewStr allocates a string on the heap. NewStr is provided for backwards compatibility only. } function NewStr(const S: string): PString; { DisposeStr disposes a string pointer that was previously allocated using NewStr.DisposeStr is provided for backwards compatibility only. } procedure DisposeStr(P: PString); { AssignStr assigns a new dynamically allocated string to the given string pointer.AssignStr is provided for backwards compatibility only. } procedure AssignStr(var P: PString; const S: string); Можно отметить, что явно задать использование long strings можно декларацией var sMyLongString: AnsiString; // long dinamically allocated string sMyWideString: WideString; // wide string (UNICODE) sMyShortString1: ShortString; // old-style string sMyShortString2: String[255]; // old-style string, no more than 255 chars Хотелось бы также предупредить наиболее частные ошибки при использовании длинных строк: • Если Вы передаёте указатель PChar на буфер, взятый от длинной строки, в функцию, которая может изменить содержание буфера, то убедитесь, что на этот буфер указывает только одна строка. Это верно в случаях сложения строк, вызова UniqueString или SetLength и некоторых других; • Если Вы используете длинные строки как аргументы или результаты для функций, располагающихся в DLL, то в DLL надо использовать модуль ShareMem; • Не используйте длинные строки как члены структур типа record. Используйте там короткие строки или array[0..n] of char. Также нельзя использовать в структурах типа record динамические массивы. Данные ограничения отсутствуют для классов. Различия TMEMOFIELD
Во-первых, если аргумент size у GetMem равен нулю, GetMem устанавливает указатель в nil (не отбрасывайте такой способ, но разумней самому установить его в nil). Также в отладчике вы могли бы проверять значение DataSize (или getTextLen) перед самим вызовом. (Проигнорируйте следующий параграф, если Table1Notes не Memo.) Во-вторых, если Table1Notes — Memo-поле, вы, вероятно, захотите использовать Table1Notes.getTextLen, не DataSize, поскольку DataSize возвращает размер сегмента буфера записи (0-254), тогда как getTextLen возвратит вам реальный размер Memo. (Для строкового поля DataSize работать будет, но очень странно, поскольку возвращает ноль.) Также вы можете воспользоваться getTextBuf вместо getData, не знаю точно почему, но мои многочисленные экспериметны показали, что getTextBuf работает правильно и устойчиво, а getData нет. Поскольку "wordwrapping" (перенос слов) доступен в вашем приложении, вы можете заменить символы #10 (перевод строки) и #13 (возврат каретки) на пробелы, например так: cursor: pchar; cursor := ваш буфер; while cursor^ <> #0 do if (cursor^ = #13) or (cursor^ = #10) then cursor^ := ' '; Данный способ прост, поскольку нам нет нужды перемещать текст из переменной в переменную, хотя и не без недостатка, поскольку в конце каждой строки мы получаем два пробела, что может неправильно интерпретироваться при переносе строк. В качестве альтернативы, вместо пробела вы можете применить другой служебный символ, который ваш текстовый процессор воспримет в качестве прерывания строки, или проигнорирует его (например, символ #8). Если вам нужно просто избавиться от символов перевода строки, воспользуйтесь двумя курсорами как показано ниже (извините, не тестировал): out, in: pchar; out := ваш буфер; in := out; while in^ <> #0 do begin if (in^ <> #10) and (in^ <> #13) then begin out^ := in^; inc(out); end; inc(in); end; out^ := #0; Если вместо этого вы хотите заменить каждую пару CR-LF или отдельный CR или LF единичным пробелом, попробуйте это: out, inn: PChar; out := ваш буфер; inn := out; while in^ <> #0 do begin if (in^ = #10) then begin end else if (in^ = #13) then begin if (in+1)^ Если вместо этого вы хотите заменить каждую пару CR-LF или отдельный CR или LF единичным пробелом, попробуйте это: out, inn: PChar; out := buf; inn := out; while inn^ <> #0 do begin if (inn^ = #10) or ((inn^ = #13) and ((inn+1)^ <> #10)) then begin out^ := ' '; Inc(out); end else if (inn^ = #13) then { только CR, игнорируем } else begin out^ := inn^; Inc(out); end; Inc(inn); end; out^ := #0; { буфер теперь закрыт } Непроверенное: эффект уменьшения размера (путем установки терминатора #0) этого PChar позволит уменьшить время компиляции массивов и буферов GetMem, что же будет при использовании StrAlloc/StrDispose? Вот конечный код после учета всех мелочей! Например, нам, в конечном счете, нужно сообщить указателю о необходимости возвратиться к началу своей новой строки. procedure TForm1.RemoveSpaces(var InBuf: PChar; Size: Word); var Input, OutPut, Orig: PChar; begin GetMem(Output, Size); input := Inbuf; Orig := Output; while input^ <> #0 do begin if (input^ <> #10) and (input^ <> #13) then begin output^ := input^; inc(output); end; inc(input); end; Output^ := #0; Output := Orig; InBuf := Output; end; Я все еще немало удивлен тому как работает GetData! Я все еще не хочу использовать TMemo! Если кто-то может решить эту проблему, я буду очень рад! Пока же я готовлю для вас материал, включающий новые процедуры печати! Наведем порядок в беспорядке! Я уже имею реализацию вывода текста с любым шрифтом и в любой позиции, выраженной в дюймах, и это только начало! Но что я думаю действительно классно вышло, так это диманическая сетка! Вы можете создавать сетку с любым количеством строк и колонок. Назначьте текст и ячейку, установите горизонтальное и вертикальное выравнивание, выберите стиль границы для каждой ячейки и изучите множество других способов манипулирования и печати сетки! Функция, возвращающая тип
Вы можете сделать это в C++. В ObjectPascal это также можно сделать, смотрите пример: // функция Chameleon, возвращающая тип сгенерированного исключения unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,StdCtrls; type MyBoolean = class public Value : boolean; end; MyInteger = class public Value : integer; end; MyClass = class public Value : TStrings; end; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } procedure MyProc; function Chameleon : boolean; end; var Form1: TForm1; implementation {$R *.DFM} function TForm1.Chameleon : boolean; var b : MyBoolean; i : MyInteger; c : MyClass; r : integer; begin r := Random(3); case r of 0 : begin b := MyBoolean.Create; raise b; end; 1 : begin i := MyInteger.Create; raise i; end; 2 : begin c := MyClass.Create; raise c; end; end; end; procedure TForm1.MyProc; begin try Chameleon; excepton MyBoolean do ShowMessage('Функция возвратила класс MyBoolean'); on MyInteger do ShowMessage('Функция возвратила класс MyInteger'); on MyClass do ShowMessage('Функция возвратила класс MyClass'); end; end; procedure TForm1.Button1Click(Sender: TObject); begin Chameleon; end; end. Взгляните на тип данных Variant в D2: следующий код function AnyType(const TypeParm: integer): Variant; begin case TypeParm of 1: Result := 1; 2: Result := 2.0; 3: Result := 'Три'; 4: Result := StrToDate('4/4/1944'); end; end; абсолютно бестолковый, но полностью корректный! Следующий код содержит объявление трех функций, принимающих на входе один и тот же параметр, но выдающих результаты различных типов (результат физичиски один и тот же, и занимает он 4 байта). Я не думаю, что можно одурачить delphi, чтобы с помощью этого метода возвратить строку. Это может привести к разрушению менеджера кучи. Вместо этого вызывайте необходимую вам функцию. Каждый вызов передается MyFuncRetAnything, а P1 определяет возвращаемый тип. Если хотите, можете написать другую обертку, делающую для вас еще и приведение типов. 3 вызова, 1 код. Я понимаю, что это в действительности не то, что нужно, по я просто хотел продемонстрировать другой способ. (вы можете возвращать строки как тип PChar, который также занимает 4 байта). Вы должны использовать некоторую память, распределяемую вызовом процедуры (может быть передавать результаты как P2?). {моя форма имеет 3 метки, одну кнопку и этот код} var MyFuncRetInt : Function(P1, P2 : Integer) : Integer; MyFuncRetBool : Function (P1, P2 : Integer) : LongBool; MyFuncRetPointer : Function (P1, P2 : Integer) : Pointer; function MyFuncRetAnything (P1, P2 : Integer) : Integer; var RetPointer : Pointer; RetBool : LongBool; RetInteger : Integer; begin RetPointer := nil; RetBool := False; RetInteger := 4711; case P1 of 1 : Result := Integer (RetPointer); 2 : Result := Integer (RetBool); 3 : Result := RetInteger; end; end; procedure TForm1.Button1Click(Sender: TObject); begin if MyFuncRetBool (2, 1900) then Label1.Caption := 'True' else Label1.Caption := 'False'; Label2.Caption := IntToStr(MyFuncRetInt(3, 1900)); Label3.Caption := IntToHex(Integer(MyFuncRetPointer(1, 1900)), 16); end; initialization MyFuncRetInt := @MyFuncRetAnything; MyFuncRetBool := @MyFuncRetAnything; MyFuncRetPointer := @MyFuncRetAnything; end. Преобразование формата MS BINARY в IEEE
«Использование, независимое от машинного уровня» не так просто в реализации с процессорами, выпущенными до Intel-го математического сопроцессора 80x87. Я не уверен в том, что процессоры 80x86 имели какие-либо родные инструкции для выполнения операций с плавающей точкой. По-видимости, поэтому Microsoft создал свой собственный формат для чисел с плавающей точкой; он сам осуществлял всю арифметику с помощью библиотеки времени выполнения. Сегодня 80x87 осуществляет такую арифметику автоматически, и IEEE теперь стандарт. Delphi хранит следующие типы чисел с плавающей точкой в формате IEEE:
Обратите внимание на то, что тип Real (6 байт) отсутствует в данном списке. Я могу ошибаться, но мне кажется что тип Real – синтезированный в Pascal тип; он может без особых проблем существовать на процессорах ниже 80x87. [В сторону: электронная справка Delphi сообщает, что по умолчанию (через директиву компилятора $N+), компилятор будет генерировать код для выполнения ВСЕХ операций с плавающей точкой, используя инструкции 80x87, включая тип Real. Также, для работы с типом Real, компилятор генерирует вызовы библиотеки времени выполнения, или же я полностью неправ в вышесказанном! :) ] Во всяком случае, в электронной справке Visual Basic я увидел, что VB оперирует с типами данных Single и Double, которые также относятся к типу IEEE, и идентичны Delphi-типам Single и Double. Тем не менее, в справке отсутствует упоминание «Microsoft Binary Format». Для того, чтобы разобраться в вопросе, я «опустился» до DOS и запустил QBasic, новую версию интерпретатора Microsoft QuickBasic, включаемую теперь в поставку DOS. Если мы посмотрим в электронную справку, то увидим следующее: MKSMBF$ и MKDMBF$ преобразуют числа формата IEEE в «числовые строки» формата Microsoft-Binary, которые могут храниться в строковых переменных типа FIELD. CVSMBF и CVDMBF преобразуют эти строки обратно в числа формата IEEE. MKSMBF$(выражение-единичной-точности!) MKDMBF$(выражение-двойной-точности#) CVSMBF (4-байтовая-числовая-строка) CVDMBF (8-байтовая-числовая-строка)
Эти функции могут оказаться полезными при поддержке файлов данных, созданных с помощью старых версий Basic. Суммируя вышесказанное, можно дать 3 рекомендации для получения доступа к вашим «MetaStock»-файлам: 1. Напишите вашу программу в QBasic/DOS 2. Найдите замену (с учетом совместимости с Delphi) для функций преобразований, упомянутых выше. 3. Напишите эти функции сами. Вы должны найти документацию для старых типов Single и Double, применявшихся в «Microsoft Binary Format», возможно в справочных файлах старых версий MS Basic. ПеременныеСтатические переменные
Да, это работает. Объявите переменную в секции const, например: procedure p; const MyVariable : Integer = 0; begin Inc(MyVariable); end; В нашем примере переменная MyVariable содержит количество вызовов P. Тем не менее, это лучшее решение, чем использование взамен какого-либо поля объекта (если это возможно). РазноеПереключение ключей компилятора
{$R+,L+} {Это директива компилятора плюс комментарий} {{$R+,L+} {Эта строка – два комментария, похоже на картинку?} Аналогично: {$DEFINE DEVEL} {$IFDEF DEVEL} …… {$ELSEIF} Переключение с «devel» компиляции на не-«devel» версию происходит простым прибавлением второй скобки в первой строке. Единственное нажатие клавиши позволит переключать вам ключи компилятора. Также для скоростных манипулиций и кратковременных изменений отлично подойдут комментарии, расположенные за строкой: if i=0 then inc(i); {выражение+комментарий} ср. { if i=0 then inc(i); {закомментарена вся строка} Сравните – два нажатия клавиш для установки фигурной скобки или десяток нажатий для установки (* *) до и после строки. Клавиша Del поможет вам вернуться в предыдущее состояние. – P Gallagher Получение ссылки на класс из объекта IМне необходимо получить ссылку на класс из объекта. Например, если у меня есть ссылка на объект (например, указатель на экземпляр TLabel), то мне необходимо получить ссылку на класс (например, ссылка на класс TLabel) для того, чтобы мне еще создать необходимое количество объектов данного класса. Другими словами, мне нужно дублировать экземпляры классов, создаваемые кем-то еще. Класс, о котором идет речь, в Delphi не зарегистрирован (его нет в палитре), поэтому GetClass('TLabel') не работает, даже если экземпляры класса существуют, работать с ними можно только через RTTI. Вдобавок к этому, у меня нет даже кода класса, поэтому работа через RTTI - единственный выход. Вот пример, который получает ссылку на класс и назначает значения новому классу того же типа. Имейте в виду, что вам необходимо сделать некоторое преобразование типов, чтобы с полученным типом класса можно было сделать что-либо полезное, поскольку возвращаемый класс имеет тип TClass. type TLabelClass = class of TLabel; procedure TForm1.Button1Click(Sender: TObject); var Ref : TLabelClass; New : TLabel; begin Ref := TLabelClass(Label1.ClassType); New := Ref.Create(Self); New.Parent := Self; New.Caption := 'Фантастика!'; end; Реплицирование класса может быть осуществлено одним из двух способов. Во-первых, вы можете воспользоваться методом Assign (который требует, чтобы ваши классы были наследниками TPersistent). Данный способ заключается в использовании метода Assign, работающего с TPersistentClass: New.Assign(Label1); Второй способ заключается в использовании автоматической поточности компонента (этот способ требует, чтобы ваши классы являлись наследниками TComponent, и чтобы они были зарегистрированы для потоковой системы). В вашем вопросе вы исходили из неправильного предположения; классы могут регистрироваться потоковой системой И НЕ регистрироваться в Палитре Компонентов; обычно эти две вещи связаны, но не обязательно. Например, скажем, у вас имеется следующий класс: TCustomer = class(TComponent) private FCompany: string; FPhone : LongInt; published property Company: string read FCompany write FCompany; property Phone: LongInt read FPhone write FPhone; end; Вы можете зарегистрировать класс для потоковой системы следующим образом: RegisterClass(TCustomer); который позволяет знать как осуществлять поточность для TCustomer, но не регистрирует его в Палитре Компонентов. После регистрации классов, вы можете реплицировать их следующим образом: procedure TForm1.Button1Click(Sender: TObject); var Ref: TComponentClass; New: TComponent; Stream: TMemoryStream; begin Ref := TComponentClass(Label1.ClassType); New := Ref.Create(Self); Stream := TMemoryStream.Create; try Stream.WriteComponent(Label1); Stream.Position := 0; Stream.ReadComponent(New); finally Stream.Free; end; end; – Rick Rogers Получение ссылки на класс из объекта IIМне необходимо получить ссылку на класс из объекта… TObject.ClassType var ClassRef: TComponentClass; NewComp: TComponent; begin TClass(ClassRef) := Sender.ClassType; NewComp := ClassRef.Create(Self); … – Pat Ritchey Работа с комментариями в большом куске кода
В Паскале существует 2 способа обозначить комментарии – {} и (* *). Вы можете вставлять один комментарий в другой (осуществлять вложенность). Следовательно, вставляя (* в начале вашего блока, и *) в конце, вы все еще можете работать с вложенными комментариями типа { }. |
|
||||||||||||||||
Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх |
||||||||||||||||||
|