• Объекты
  • Проблема циклических ссылок
  • Создание множества экземпляров
  • Параметры
  • Передача функции как параметра
  • Проблема передачи записи
  • Указатели
  • Указатель на функцию I
  • Указатель на функцию II
  • Использование указателей на целое
  • Память
  • Функция MemAvail для Delphi2?
  • Как работать с блоками памяти размером более 64K?
  • События
  • Назначение обработчика события OnClick пункту меню, созданному во время выполнения программы
  • События для компонентов, созданных во время работы программы I
  • События для компонентов, созданных во время работы программы II
  • Массивы
  • Динамические массивы V
  • Динамические массивы VI
  • Динамические массивы VII
  • Динамические массивы VIII
  • Пример массива констант (Array of Const) III
  • Массив объектов-изображений
  • Массив TPOINT
  • Создание больших массивов
  • Свойства
  • Редактор свойств для точки
  • Хитрость вызова редактора свойств
  • Как убрать публичное свойство компонента/формы из списка видимых/редактируемых свойств в Инспекторе Обьектов?
  • Свойство FileName в невизуальном компоненте
  • Записи
  • Пример переменной записи
  • Передача массива записей символов в Memo
  • Освобождение записей
  • Строки 
  • StrTok для Delphi 2
  • Как мне перекодировать строки из Win-кодировки в Dos-кодировку и наоборот?
  • Типы 
  • У меня константы могут иметь значение, отличное от заданного. Как лечить?
  • Значение вычисляемого поля Paradox вместо 25.55 становится 24.5499999…
  • Классовые/статические/переменные общего доступа
  • Чем отличается тип String в Delphi 2 и выше от аналогичного в Delphi 1?
  • Различия TMEMOFIELD
  • Функция, возвращающая тип
  • Преобразование формата MS BINARY в IEEE
  • Переменные 
  • Статические переменные
  • Разное 
  • Переключение ключей компилятора
  • Получение ссылки на класс из объекта I
  • Получение ссылки на класс из объекта II
  • Работа с комментариями в большом куске кода
  • Pascal

    Объекты

    Проблема циклических ссылок

    У меня имеется объект A и объект B, и им обоим нужно вызывать методы друг друга…

    Объявите абстрактный базовый класс, определяющий интерфейс класса для того, чтобы другие классы могли его видеть. Используйте виртуальные абстрактные методы и свойства. Затем объявите другие классы подклассами базового класса (при необходимости). Данный метод существенно поможет в структурировании вашего приложения.

    (Mike Scott.)

    Создание множества экземпляров

    Delphi 1

    list:=Tlist.create;

    For i:= 1 to 1000 do begin

     SSObject:=TSSObject.create;

     {поместите куда-нибудь ссылку на созданный объект - например, в Tlist}

     list.add(SSObject);

    end;

    Параметры

    Передача функции как параметра

    Delphi 1

    В нашем случае лучшим решением будет использование процедурного типа. Допустим, что 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.

    Проблема передачи записи

    Delphi 1

    Может это не то, что вы ищете, но идея такая:

    Определите базовый класс с именем, скажем, 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

    Delphi 1

    Это то, что я нашел при создании простой машины состояний:

    Ниже приведен простой пример для 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

    Delphi 1

    Что лично я использую, чтобы вызвать какую-то функцию из 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);

    Использование указателей на целое

    Delphi 1

    Сначала вы должны создать тип:

    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 2

    В 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 пункту меню, созданному во время выполнения программы

    Delphi 1

    Поскольку метод OnClick является свойством, то при динамическом создании элемента меню вы можете назначить имя метода обработчику OnClick:

    theMenuitem.OnClick := TheOnClickHandler;

    Затем, в обработчике OnClick, вы приводите sender к TMenuItem и читаете имя:

    procedure theform.TheOnClickHandler(Sender: TObject);

    var

     fName: String;

    begin

     fName := TMenuItem(Sender).name;

     …

    end;

    События для компонентов, созданных во время работы программы I

    Delphi 1

    Вы должны вручную создать метод, который будет иметь тот же самый набор параметров, как и у события, которое вы хотите обработать. Затем вы должны вручную установить свойство 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

    Delphi 1

    Вот простейший код для нового проекта с одной кнопкой и меню. (Надеюсь, в этом ничего сложного нет ... :)

    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;

    Массивы

    Динамические массивы V

    SottNick пишет:

    Если хочется, чтобы в многомерном массиве был разный размер у разных измерений например: 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

    Delphi 1

    Например, если вам необходимо сохранить «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

    Delphi 1

    Существует несколько способов сделать это. Применять тот или иной способ зависит от того, какой массив вы используете — массив строк или массив чисел (целые, натуральные и пр.).

    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;

    Igor Nikolaev aKa The Sprite

    Пример массива констант (Array of Const) III

    Delphi 1

    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));

    Массив объектов-изображений

    Delphi 1

    Вы не сможете сделать это напрямую и "визуально", но если вы не возражаете против нескольких строк кода, то я покажу как это может быть просто:

    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

    Delphi 1

    Const ptarr : Array[0..4] Of TPoint =((x:0; y:4), … (x:4; y:4));

    Создание больших массивов

    Delphi 1

    В 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

    Хитрость вызова редактора свойств

    Я пишу редактор для свойства TStrings. В зависимости от значений других свойств, я хотел бы показывать или свой редактор свойства, или редактор свойства TStringListProperty, заданный по умолчанию, но я не знаю как передавать управление TStringListProperty...

    Сделайте ваш редактор свойства наследником 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

    Delphi 1

    Тема: Передача массива записей символов в 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.

    Освобождение записей

    Delphi 1

    Для начала необходимо привести объект к нужному типу, например, так:

    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

    Я передалал это для работы в 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…

    Delphi 1

    Значение вычисляемого поля вместо 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, которой в параметрах необходимо указать общую длину строки и количество десятичных цифр.

    Классовые/статические/переменные общего доступа

    Delphi 1

    Здесь кроется небольшая хитрость: получение эквивалентной функциональности с помощью классового метода. Просто объявите 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

    Delphi 1

    Во-первых, если аргумент 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! Если кто-то может решить эту проблему, я буду очень рад! Пока же я готовлю для вас материал, включающий новые процедуры печати! Наведем порядок в беспорядке! Я уже имею реализацию вывода текста с любым шрифтом и в любой позиции, выраженной в дюймах, и это только начало! Но что я думаю действительно классно вышло, так это диманическая сетка! Вы можете создавать сетку с любым количеством строк и колонок. Назначьте текст и ячейку, установите горизонтальное и вертикальное выравнивание, выберите стиль границы для каждой ячейки и изучите множество других способов манипулирования и печати сетки!

    Функция, возвращающая тип

    Delphi 1

    Вы можете сделать это в 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

    Delphi 1

    «Использование, независимое от машинного уровня» не так просто в реализации с процессорами, выпущенными до Intel-го математического сопроцессора 80x87. Я не уверен в том, что процессоры 80x86 имели какие-либо родные инструкции для выполнения операций с плавающей точкой. По-видимости, поэтому Microsoft создал свой собственный формат для чисел с плавающей точкой; он сам осуществлял всю арифметику с помощью библиотеки времени выполнения. Сегодня 80x87 осуществляет такую арифметику автоматически, и IEEE теперь стандарт.

    Delphi хранит следующие типы чисел с плавающей точкой в формате IEEE:

    Single 4 байт
    Double 8 байт
    Extended 10 байт

    Обратите внимание на то, что тип 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-байтовая-числовая-строка)

    Функция Возвращаемое значение
    MKSMBF$ 4-байтовая строка, содержащая число в формате Microsoft-Binary-format
    MKDMBF$ 8-байтовая строка, содержащая число в формате Microsoft-Binary-format
    CVSMBF Число единичной точности в формате IEEE
    CVDMBF Число двойной точности в формате IEEE

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

    Суммируя вышесказанное, можно дать 3 рекомендации для получения доступа к вашим «MetaStock»-файлам:

    1. Напишите вашу программу в QBasic/DOS

    2. Найдите замену (с учетом совместимости с Delphi) для функций преобразований, упомянутых выше.

    3. Напишите эти функции сами. Вы должны найти документацию для старых типов Single и Double, применявшихся в «Microsoft Binary Format», возможно в справочных файлах старых версий MS Basic.

    Переменные 

    Статические переменные

    Delphi 1 

    Да, это работает. Объявите переменную в секции const, например:

    procedure p;

    const MyVariable : Integer = 0;

    begin

     Inc(MyVariable);

    end;

    В нашем примере переменная MyVariable содержит количество вызовов P.

    Тем не менее, это лучшее решение, чем использование взамен какого-либо поля объекта (если это возможно). 

    Разное 

    Переключение ключей компилятора

    Быстрый и легкий путь вкл/выкл директив компилятора. Весь Borland pascal.

    {$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 

    Работа с комментариями в большом куске кода

    Delphi 1

    В Паскале существует 2 способа обозначить комментарии – {} и (* *). Вы можете вставлять один комментарий в другой (осуществлять вложенность). Следовательно, вставляя (* в начале вашего блока, и *) в конце, вы все еще можете работать с вложенными комментариями типа { }.








    Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх