Delphi - база знаний

         

Не сохраняются изменения в базе Paradox


не сохраняются изменения в базе Paradox




Где-нибудь при закрытии главной формы выполните нижеследующие куски кода:
при открытой таблице:

Table.FlushBuffers;

Для прочих:

Table.Open;
Check(dbiSaveChanges(Table.Handle)); 
Table.Close;

Чтобы сбросить кэш, можно еще после этого сделать:

asm


  mov ah, $0D
  int $21
end; 

Взято из





Недоступная закладка в компоненте TabbedNotebook


Недоступная закладка в компоненте TabbedNotebook




Есть ли возможность в компоненте Tabbednotebook сделать какую-либо страницу недоступной? То есть не позволять пользователю щелкать на ней и видеть ее содержимое?

Да, такая возможность существует. Самый простой путь - удалить страницу, например так:



withTabbedNotebook do
  Pages.Delete(PageIndex);




и снова включить ее (при необходимости), перегрузив форму.

Блокировка (а не удаление) немного мудренее, поскольку необходима организация цикла в процедуре создания формы, присваивающая имена закладкам компонента TabbedNotebook. Например так:

J := 0;
with TabbedNotebook do
  for I := 0 to ComponentCount - 1 do
    if Components[I].ClassName = 'TTabButton' then
      begin
        Components[I].Name := ValidIdentifier(TTabbedNotebook(
          Components[I].Owner).Pages[J]) + 'Tab';
        Inc(J);
      end;


где ValidIdentifier ValidIdentifier - функция, которая возвращает правильный Pascal-идентификатор, производный от строки 'Tab':

function ValidIdentifier(theString: str63): str63;
{--------------------------------------------------------}
{ Конвертирует строку в правильный Pascal-идентификатор, }
{ удаляя все неправильные символы и добавляя символ '_', }
{ если первый символ - цифра                             }
{--------------------------------------------------------}
var
  I, Len: Integer;
begin
  Len := Length(theString);
  for I := Len downto 1 do
    if not (theString[I] in LettersUnderscoreAndDigits) then
      Delete(theString, I, 1);
  if not (theString[1] in LettersAndUnderscore) then
    theString := '_' + theString;
  ValidIdentifier := theString;
end; {ValidIdentifier}

Затем мы можем сделать закладку компонента TabbedNotebook недоступной:

with TabbedNotebook do
  begin
    TabIdent := ValidIdentifier(Pages[PageIndex]) + 'Tab';
    TControl(FindComponent(TabIdent)).Enabled := False;
{ Переключаемся на первую доступную страницу: }
    for I := 0 to Pages.Count - 1 do
      begin
        TabIdent := ValidIdentifier(Pages[I]) + 'Tab';
        if TControl(FindComponent(TabIdent)).Enabled then
          begin
            PageIndex := I;
            Exit;
          end;
      end; {for}
  end; {with TabbedNotebook}

следующий код восстанавливает доступность страницы:

with TabbedNotebook do
  for I := 0 to Pages.Count - 1 do
    begin
      TabIdent := ValidIdentifier(Pages[I]) + 'Tab';
      if not TControl(FindComponent(TabIdent)).Enabled then
        TControl(FindComponent(TabIdent)).Enabled := True;
    end; {for}

Взято с





Негатив картинки


Негатив картинки




var
Line: pByteArray;
  i, j: integer;
begin
  // считываем высоту картинки
  for i := 0 to Image1.Picture.Bitmap.Height - 1 do
  begin
    //сканируем по линиям рисунок
    Line := Image1.Picture.Bitmap.ScanLine[i];
    for j := 0 to Image1.Picture.Bitmap.Width * 3 - 1 do
      //меняем цвет на обратный исходя из RGB
      Line^[j] := 255 - Line^[j];
  end;
  Image1.Refresh;
end;


Взято с





Неявное обращение к компонентам, манипуляции в run-time


Неявное обращение к компонентам, манипуляции в run-time



Cодержание раздела:









См. также другие разделы:




См. также статьи в других разделах:










Необходимо, чтобы дочерняя форма не активизировала родительское окно


Необходимо, чтобы дочерняя форма не активизировала родительское окно




Сделайте родительским окном рабочий стол.

procedure TForm2.CreateParams(VAR Params: TCreateParams); 
begin 
  Inherited CreateParams(Params); 
  Params.WndParent := GetDesktopWindow; 
end; 



Непосредственное обращение к BDE


Непосредственное обращение к BDE



Cодержание раздела:









Несколько колонок в TComboBox


Несколько колонок в TComboBox




procedureTForm1.ComboBox1DrawItem(Control: TWinControl; 
  Index: Integer; Rect: TRect; State: TOwnerDrawState); 
var 
  strVal, strAll: string; 
  pos1, pos2: Integer; 
  rc: TRect; 
  arrWidth: array [0..3] of Integer; 
begin 
  Combobox1.Canvas.Brush.Style := bsSolid; 
  Combobox1.Canvas.FillRect(Rect); 
  // Die einzelnen Spalten mussen durch ';' getrennt sein 
  // the columns must be separated by ';' 
  strAll := Combobox1.Items[Index]; 

  arrWidth[0] := 0; 
  arrWidth[1] := 100;  // Width of column 1 
  arrWidth[2] := 200;  // Width of column 2 
  arrWidth[3] := 300;  // Width of colimn 3 

  // Zeichenbereich fur erste Spalte 
  // Drawingrange for first column 
  rc.Left   := Rect.Left + arrWidth[0] + 2; 
  rc.Right  := Rect.Left + arrWidth[1] - 2; 
  rc.Top    := Rect.Top; 
  rc.Bottom := Rect.Bottom; 

  // Text fur erste Spalte ausfiltern 
  // Get text for first column 
  pos1   := Pos(';', strAll); 
  strVal := Copy(strAll, 1, pos1 - 1); 
  // Text ausgeben 
  // Draw Text 
  Combobox1.Canvas.TextRect(rc, rc.Left, rc.Top, strVal); 
  // Trennlinie zwischen Spalten zeichnen 
  // Draw separating line betwenn columns 
  Combobox1.Canvas.MoveTo(rc.Right, rc.Top); 
  Combobox1.Canvas.LineTo(rc.Right, rc.Bottom); 

  // Zeichenbereich fur zweite Spalte 
  // Drawingrange for second column 
  rc.Left  := Rect.Left + arrWidth[1] + 2; 
  rc.Right := Rect.Left + arrWidth[2] - 2; 

  // Text fur zweite Spalte ausfiltern 
  // Get text for second column 
  strAll := Copy(strAll, pos1 + 1, Length(strAll) - pos1); 
  pos1   := Pos(';', strAll); 
  strVal := Copy(strAll, 1, pos1 - 1); 

  // Text ausgeben 
  // Draw Text 
  Combobox1.Canvas.TextRect(rc, rc.Left, rc.Top, strVal); 
  // Trennlinie zwischen Spalten zeichnen 
  // Draw separating line betwenn columns 
  Combobox1.Canvas.MoveTo(rc.Right, rc.Top); 
  Combobox1.Canvas.LineTo(rc.Right, rc.Bottom); 

  // Zeichenbereich fur dritte Spalte 
  // Drawingrange for third column 
  rc.Left  := Rect.Left + arrWidth[2] + 2; 
  rc.Right := Rect.Left + arrWidth[3] - 2; 

  // Text fur dritte Spalte ausfiltern 
  // Get text for third column 
  strAll := Copy(strAll, pos1 + 1, Length(strAll) - pos1); 
  pos1   := Pos(';', strAll); 
  strVal := Copy(strAll, 1, pos1 - 1); 

  // Text ausgeben 
  // Draw Text 
  Combobox1.Canvas.TextRect(rc, rc.Left, rc.Top, strVal); 
  // Trennlinie zwischen Spalten zeichnen 
  // Draw separating line betwenn columns 
  Combobox1.Canvas.MoveTo(rc.Right, rc.Top); 
  Combobox1.Canvas.LineTo(rc.Right, rc.Bottom); 
  strAll := Copy(strAll, pos1 + 1, Length(strAll) - pos1); 
end; 


// Example/ Beispiel: 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
  with Combobox1.Items do 
  begin 
    Add('first;second;third;'); 
    Add('column1;column2;column3;'); 
  end; 
end; 


procedure TForm1.FormCreate(Sender: TObject); 
begin 
  //Oder im Objekt Inspektor einstellen 
  //Or set this Property in the Object Inspector 
  Combobox1.Style := csOwnerDrawFixed; 
end;


Взято с





Net глазами дельфийца. Использование Delphi в .Net


.Net глазами дельфийца. Использование Delphi в .Net





.Net глазами дельфийца. Использование Delphi в .Net

Для программистов, рассматривающих вопрос перехода на новую систему разработки, один из главных вопросов ? как в новой системе можно будет использовать старые наработки.
В настоящей статье рассмотрены некоторые простые способы использования наработок Delphi (автор использовал версию 6) в .Net. Естественно, полностью портировать Delphi в .Net под силу только разработчикам Borland (или аналогичным серьезным людям, владеющим к тому же полными исходниками Delphi), поэтому здесь мы не будем рассматривать клиентские (визуальные) приложения, использующие VCL. Тем не менее некоторый объем функционального кода, реализованного в Delphi, может быть использован и в .Net (пример такого кода ? вычислительные модули).
Итак, для переноса Delphi-кода в .Net можно использовать следующие способы:
- переписывание кода;
- использование в .Net COM-объектов, разработанных в Delphi;
- использование в .Net библиотек DLL, разработанных в Delphi;
- хакерские способы

Переписывание кода

Этот способ, несмотря на его прямолинейность, в некоторых случаях может оказаться наиболее рациональным и даже упростить результирующий код.
Техника переноса алгоритмов на другие языки знакома многим программистам. Более того, существуют автоматические трансляторы типа Pas2C, которые входной паскалевский код преобразуют в C/C++-код (естественно, кое-что приходится править потом вручную, но основа получается вполне приемлемая).
Не останавливаясь подробно на очевидных моментах ручной трансляции кода, хотелось бы напомнить следующее:
- поскольку в C# нет процедур и функций вне классов, для реализации глобальных подпрограмм целесообразно использовать специальный(-ые) класс(ы) со статическими методами;
- поскольку в C# нет глобальных переменных, для их реализации целесообразно использовать специальный(-ые) класс(ы) со статическими полями;
- для реализации конструкций, эквивалентных записям Delphi, можно использовать как классы, так и структуры (struct) ? решение зависит от объема данных, которые, возможно, будут копироваться при передаче в качестве параметров и пр.;
- поскольку в C# объекты удаляются автоматически, необходимо тщательно проанализировать последовательность создания и удаления объектов, особенно если в Delphi-объектах в деструкторе выполнялось освобождение ресурсов ? в таких случаях оптимальным вариантом будет или введение соответствующих методов «закрытия», или реализация интерфейса IDisposable.

Использование в .Net COM-объектов, разработанных в Delphi

Поскольку стандарт OLE четко определяет протокол обмена и форматы данных, то никаких особенностей при разработке COM-объектов в Delphi нет (не считая того, что нежелательно использовать имена методов, совпадающие с методами класса Object из C#: Equals, GetHashCode, GetType, ReferenceEquals, ToString).
Если же исходный код реализован, например, в виде набора классов Delphi, то можно написать в Delphi очень простой COM-объект (inproc-server), который будет выполнять функции фасада (см. например Шаблоны проектирования на www.dotsite.spb.ru ).
Кстати, во многих случаях введение «фасада» позволяет даже упростить систему со стороны основного кода.

Использование в .Net библиотек DLL, разработанных в Delphi

Т.к. в Delphi позволяет разрабатывать динамические библиотеки DLL, то можно существующий Delphi-код «упаковать» в библиотеку. Этот способ заставляет выполнить несколько бОльшую работу в C#, чем при использовании COM-объектов, хотя в некоторых случаях вполне может быть использован из соображений, например, быстродействия системы.
Вызов функций DLL из C# достаточно неплохо описан в документации .Net. Ниже приводится пример использования DLL, разработанной в Delphi.

Заголовки DLL-функций в Delphi:
//Процедура без параметров
procedure Proc1; stdcall;
// Процедура с целочисленными параметрами
procedure Proc2(A, B: integer); stdcall;
// Процедура с вещественными параметрами
procedure Proc3(A, B: double); stdcall;
// Процедура с логическими параметрами
procedure Proc4(A, B: boolean); stdcall;
// Процедура с параметрами типа дата/время
procedure Proc5(A, B: TDateTime); stdcall;
// Процедура со строковыми параметрами
procedure Proc6(P1, P2: PChar); stdcall;

Обратите внимание на два момента:
- используется модификатор вызова stdcall
- строки в качестве параметров передаются как PChar

using System;

using System.Runtime.InteropServices;



namespace Test1
{
  /// <summary>
  /// Обертка для вызова функций Delphi, размещенных в DLL
  /// </summary>
  public class LibWrap {
    /// <summary>
    /// Процедура без параметров
    /// </summary>
    [ DllImport( @"C:\Projects\C#\DelphiPortal\Dll1.dll" ) ]
    public static extern void Proc1();
    /// <summary>
    /// Процедура с целочисленными параметрами
    /// </summary>
    [ DllImport( @"C:\Projects\C#\DelphiPortal\Dll1.dll" ) ]
    public static extern void Proc2(int A, int B);
    /// <summary>
    /// Процедура с вещественными параметрами
    /// </summary>
    [ DllImport( @"C:\Projects\C#\DelphiPortal\Dll1.dll" ) ]
    public static extern void Proc3(double A, double B);
    /// <summary>
    /// Процедура с логическими параметрами
    /// </summary>
    [ DllImport( @"C:\Projects\C#\DelphiPortal\Dll1.dll" ) ]
    public static extern void Proc4(bool A, bool B);
    /// <summary>
    /// Процедура с параметрами типа дата/время
    /// </summary>
    [ DllImport( @"C:\Projects\C#\DelphiPortal\Dll1.dll" ) ]
    public static extern void Proc5(double A, double B);
    /// <summary>
    /// Процедура со строковыми параметрами
    /// </summary>
    [ DllImport( @"C:\Projects\C#\DelphiPortal\Dll1.dll" ) ]
    public static extern void Proc6(
      [MarshalAs(UnmanagedType.LPStr)] string A, 
      [MarshalAs(UnmanagedType.LPStr)] string B);
  }
  /// <summary>
  /// Тестовый класс
  /// </summary>
  class Class1 {
    [STAThread]
    static void Main() {
      // Вызов процедуры без параметров
      LibWrap.Proc1();
      /// Вызов процедуры с целочисленными параметрами
      LibWrap.Proc2(1, 2);
      // Вызов процедуры с вещественными параметрами
      LibWrap.Proc3(1.5, -2.8);
      // Вызов процедуры с логическими параметрами
      LibWrap.Proc4(true, false);
      // Вызов процедуры с параметрами типа дата/время
      DateTime dt1 = DateTime.Now;
      DateTime dt2 = dt1.AddDays(2);
      LibWrap.Proc5(dt1.ToOADate(), dt2.ToOADate());
      // Вызов процедуры со строковыми параметрами
      LibWrap.Proc6("Строка 1", "Строка 2");
    }
  }
}

В коде C# стОит обратить внимание на следующее:
- при описании метода LibWrap.Proc5, работающего с параметрами типа дата/время, типы параметров - double
- при описании метода LibWrap.Proc6, работающего со строковыми параметрами, необходимо в явном виде указывать способ маршализации (атрибут MarshalAs)
- объекты типа DateTime преобразуются в формат, понятный для Delphi, с помощью методов ToOADate

Хакерские способы

В каталоге <Microsoft Visual Studio .NET>\FrameworkSDK\Samples\Technologies\Interop\PlatformInvoke\Custom\CS имеется интересный файл - ClassMethods.cs. В этом файле показано, как вызывать классы VC++, размещенные в DLL. Этот пример натолкнул автора статьи на мысль проверить что-то подобное с Delphi.
Т.к. пакеты в Delphi ? не что иное, как DLL, такой вариант выглядел возможным. Более того, с помощью утилиты Depends.Exe, входящей в состав VSN (<Microsoft Visual Studio .NET>\Common7\Tools\Bin), при исследовании соответствующего BPL-файла можно даже увидеть имена экспортируемых функций в стиле C++, например:

@$xp$14Class1@TClass1
@Class1@TClass1@

К сожалению, автору не удалось запустить таким образом объекты <





Взято с сайта




.Net и Delphi8



Cодержание раздела:




Нетипизированные файлы


Нетипизированные файлы



Идем дальше. Есть такое понятие как нетипизированный файл. Это такой файл который содержит разнородные элементы. Например файл EXE - вначале он имеет заголовок, затем двоичный код, в конце какие-то ресурсы. Все части файла имеют разную длину и разную структуру. Тут уже обратится к произвольному элементу сложно, обычно надо вначале узнать где этот элемент находится, подчас это записано в предыдущем куске информации. Работа с такими файлами достаточно сложна и требует вручную разработки алгоритмов его чтения, но в связи гибкостью структуры и компактностью такие файлы составляют большинство. Для работы с нетипизированными файлами используют процедуры BlockRead и BlockWrite, которые позволяют читать/писать произвольное количество байт. Привожу пример пользования этими функциями из справки по Дельфи:

var
FromF, ToF: file;  
NumRead, NumWritten: Integer;  
Buf: array[1..2048] of Char;  
begin
if OpenDialog1.Execute then { Display Open dialog box }  
begin  
AssignFile(FromF, OpenDialog1.FileName);  
Reset(FromF, 1); { Record size = 1 }  
if SaveDialog1.Execute then { Display Save dialog box}  
begin  
AssignFile(ToF, SaveDialog1.FileName); { Open output file }  
Rewrite(ToF, 1); { Record size = 1 }  
Canvas.TextOut(10, 10, 'Copying ' + IntToStr(FileSize(FromF))+ ' bytes...');  
repeat  
BlockRead(FromF, Buf, SizeOf(Buf), NumRead);  
BlockWrite(ToF, Buf, NumRead, NumWritten);  
until (NumRead = 0) or (NumWritten <> NumRead);  
CloseFile(FromF);  
CloseFile(ToF);  
end;  
end;  
end;

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

Пожалуй на этом можно было бы и завершить описание работы с файлами средствами Паскаля и файловых переменных, но заглянув в Help Дельфей я обнаружил еще несколько функций достойных упоминания.

Erase(f) - удаляет файл
FilePos(f) - возвращает текущую позицию чтения/записи в файл
Flush(f) - сбрасывает кэшированные файловые операции на диск
Rename(f, 'MyNewFileName.txt') - переименование файлов
Truncate(f) - файл обрезается до текущей позиции чтения/записи






Низкоуровневые процедуры обработки звука


Низкоуровневые процедуры обработки звука




Ниже приведен код, обрабатывающий аудиосигнал, получаемый со входа звуковой карты (SoundBlaster). Надеюсь он поможет разобраться вам с этой сложной темой.

Включенный в код модуль RECUNIT делает всю изнурительную работу по извлечению звука со входа звуковой карты.



var
WaveRecorder: TWaveRecorder;

...

WaveRecorder := TwaveRecorder(2048, 4); // 4 размером 2048 байт

{ Устанавливает параметры дискретизации }
with WaveRecorder.pWavefmtEx do
begin
  wFormatTag := WAVE_FORMAT_PCM;
  nChannels := 1;
  nSamplesPerSec := 20000;
  wBitsPerSample := 16;
  nAvgBytesPerSec := nSamplesPerSec * (wBitsPerSample div 8) * nChannels;
end;

// Затем используем вариантную запись, поскольку я не знаю
// как получить адрес самого объекта

WaveRecorder.SetupRecord(@WaveRecorder);

// Начинаем запись
WaveRecorder.StartRecord;

...При каждом заполнении буфера вызывается
  процедура WaveRecorder.Processbuffer.

//  Заканчиваем запись
WaveRecorder.StopRecord;
WaveRecorder.Destroy;

 



{
Имя файла: RECUNIT.PAS  V 1.01
Создан: Авг 19 1996 в 21:56 на IBM ThinkPad
Ревизия #7: Авг 22 1997, 15:01 на IBM ThinkPad
-John Mertus

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

Версия 1.00 - первый релиз
1.01 - добавлен TWaveInGetErrorText
}

{-----------------Unit-RECUNIT---------------------John Mertus---Авг 96---}

unit RECUNIT;

{*************************************************************************}

interface

uses

  Windows, MMSystem, SysUtils, MSACM;

{  Ниже определен класс TWaveRecorder для обслуживания входа звуковой    }
{  карты. Ожидается, что новый класс будет производным от TWaveRecorder  }
{  и перекроет TWaveRecorder.ProcessBuffer. После начала записи данная   }
{  процедура вызывается каждый раз при наличии в буфере аудио-данных.    }

const

  MAX_BUFFERS = 8;

type

  PWaveRecorder = ^TWaveRecorder;
  TWaveRecorder = class(TObject)
    constructor Create(BfSize, TotalBuffers: Integer);
    destructor Destroy; override;
    procedure ProcessBuffer(uMsg: Word; P: Pointer; n: Integer);
      virtual;

  private
    fBufferSize: Integer; // Размер буфера
    BufIndex: Integer;
    fTotalBuffers: Integer;

    pWaveHeader: array[0..MAX_BUFFERS - 1] of PWAVEHDR;
    hWaveHeader: array[0..MAX_BUFFERS - 1] of THANDLE;
    hWaveBuffer: array[0..MAX_BUFFERS - 1] of THANDLE;
    hWaveFmtEx: THANDLE;
    dwByteDataSize: DWORD;
    dwTotalWaveSize: DWORD;

    RecordActive: Boolean;
    bDeviceOpen: Boolean;

    { Внутренние функции класса }
    function InitWaveHeaders: Boolean;
    function AllocPCMBuffers: Boolean;
    procedure FreePCMBuffers;

    function AllocWaveFormatEx: Boolean;
    procedure FreeWaveFormatEx;

    function AllocWaveHeaders: Boolean;
    procedure FreeWaveHeader;

    function AddNextBuffer: Boolean;
    procedure CloseWaveDeviceRecord;

  public
    { Public declarations }
    pWaveFmtEx: PWaveFormatEx;
    WaveBufSize: Integer; // Размер поля nBlockAlign
    InitWaveRecorder: Boolean;
    RecErrorMessage: string;
    QueuedBuffers,
      ProcessedBuffers: Integer;
    pWaveBuffer: array[0..MAX_BUFFERS - 1] of lpstr;
    WaveIn: HWAVEIN; { Дескриптор Wav-устройства }

    procedure StopRecord;
    function 477576218068 StartRecord: Boolean;
    Function477576218068 SetupRecord(P: PWaveRecorder): Boolean;

  end;

  {*************************************************************************}

implementation

{-------------TWaveInGetErrorText-----------John Mertus---14-Июнь--97--}

function TWaveInGetErrorText(iErr: Integer): string;

{ Выдает сообщения об ошибках WaveIn в формате Pascal                  }
{ iErr - номер ошибки                                                  }
{                                                                      }
{**********************************************************************}
var

  PlayInErrorMsgC: array[0..255] of Char;

begin

  waveInGetErrorText(iErr, PlayInErrorMsgC, 255);
  TWaveInGetErrorText := StrPas(PlayInErrorMsgC);
end;

{-------------InitWaveHeaders---------------John Mertus---14-Июнь--97--}

function TWaveRecorder.AllocWaveFormatEx: Boolean;

{ Распределяем формат большого размера, требуемый для инсталляции ACM-в}
{                                                                      }
{**********************************************************************}
var

  MaxFmtSize: UINT;

begin

  { maxFmtSize - сумма sizeof(WAVEFORMATEX) + pwavefmtex.cbSize }
  if (acmMetrics(0, ACM_METRIC_MAX_SIZE_FORMAT, maxFmtSize) <> 0) > then
  begin
    RecErrorMessage := 'Ошибка получения размера формата максимального сжатия';
    AllocWaveFormatEx := False;
    Exit;
  end;

  { распределяем структуру WAVEFMTEX }
  hWaveFmtEx := GlobalAlloc(GMEM_MOVEABLE, maxFmtSize);
  if (hWaveFmtEx = 0) then
  begin
    RecErrorMessage := 'Ошибка распределения памяти для структуры WaveFormatEx';
    AllocWaveFormatEx := False;
    Exit;
  end;

  pWaveFmtEx := PWaveFormatEx(GlobalLock(hWaveFmtEx));
  if (pWaveFmtEx = nil) then
  begin
    RecErrorMessage := 'Ошибка блокировки памяти WaveFormatEx';
    AllocWaveFormatEx := False;
    Exit;
  end;

  { инициализация формата в стандарте PCM }
  ZeroMemory(pwavefmtex, maxFmtSize);
  pwavefmtex.wFormatTag := WAVE_FORMAT_PCM;
  pwavefmtex.nChannels := 1;
  pwavefmtex.nSamplesPerSec := 20000;
  pwavefmtex.nBlockAlign := 1;
  pwavefmtex.wBitsPerSample := 16;
  pwavefmtex.nAvgBytesPerSec := pwavefmtex.nSamplesPerSec *
    (pwavefmtex.wBitsPerSample div 8) * pwavefmtex.nChannels;
  pwavefmtex.cbSize := 0;

  { Все успешно, идем домой }
  AllocWaveFormatEx := True;
end;

{-------------InitWaveHeaders---------------John Mertus---14-Июнь--97--}

function TWaveRecorder.InitWaveHeaders: Boolean;

{ Распределяем память, обнуляем заголовок wave и инициализируем        }
{                                                                      }
{**********************************************************************}
var

  i: Integer;

begin

  { делаем размер буфера кратным величине блока... }
  WaveBufSize := fBufferSize - (fBufferSize mod pwavefmtex.nBlockAlign);

  { Устанавливаем wave-заголовки }
  for i := 0 to fTotalBuffers - 1 do
    with pWaveHeader[i]^ do
    begin
      lpData := pWaveBuffer[i]; // адрес буфера waveform
      dwBufferLength := WaveBufSize; // размер, в байтах, буфера
      dwBytesRecorded := 0; // смотри ниже
      dwUser := 0; // 32 бита данных пользователя
      dwFlags := 0; // смотри ниже
      dwLoops := 0; // смотри ниже
      lpNext := nil; // зарезервировано; должен быть ноль
      reserved := 0; // зарезервировано; должен быть ноль
    end;

  InitWaveHeaders := TRUE;
end;

{-------------AllocWaveHeader----------------John Mertus---14-Июнь--97--}

function TWaveRecorder.AllocWaveHeaders: Boolean;

{ Распределяем и блокируем память заголовка                             }
{                                                                       }
{***********************************************************************}
var

  i: Integer;

begin

  for i := 0 to fTotalBuffers - 1 do
  begin
    hwaveheader[i] := GlobalAlloc(GMEM_MOVEABLE or GMEM_SHARE or
      GMEM_ZEROINIT, sizeof(TWAVEHDR));

    if (hwaveheader[i] = 0) then
    begin
      { Примечание: Это может привести к утечке памяти, надеюсь скоро исправить }
      RecErrorMessage := 'Ошибка распределения памяти для wave-заголовка';
      AllocWaveHeaders := FALSE;
      Exit;
    end;

    pwaveheader[i] := GlobalLock(hwaveheader[i]);
    if (pwaveheader[i] = nil) then
    begin
      { Примечание: Это может привести к утечке памяти, надеюсь скоро исправить }
      RecErrorMessage := 'Не могу заблокировать память заголовка для записи';
      AllocWaveHeaders := FALSE;
      Exit;
    end;

  end;

  AllocWaveHeaders := TRUE;
end;

{---------------FreeWaveHeader---------------John Mertus---14-Июнь--97--}

procedure TWaveRecorder.FreeWaveHeader;

{ Просто освобождаем распределенную AllocWaveHeaders память.            }
{                                                                       }
{***********************************************************************}
var

  i: Integer;

begin

  for i := 0 to fTotalBuffers - 1 do
  begin
    if (hWaveHeader[i] <> 0) then
    begin
      GlobalUnlock(hwaveheader[i]);
      GlobalFree(hwaveheader[i]);
      hWaveHeader[i] := 0;
    end
  end;
end;

{-------------AllocPCMBuffers----------------John Mertus---14-Июнь--97--}

function TWaveRecorder.AllocPCMBuffers: Boolean;

{ Распределяем и блокируем память waveform.                             }
{                                                                       }
{***********************************************************************}
var

  i: Integer;

begin

  for i := 0 to fTotalBuffers - 1 do
  begin
    hWaveBuffer[i] := GlobalAlloc(GMEM_MOVEABLE or GMEM_SHARE, fBufferSize);
    if (hWaveBuffer[i] = 0) then
    begin
      { Здесь возможна утечка памяти }
      RecErrorMessage := 'Ошибка распределения памяти wave-буфера';
      AllocPCMBuffers := False;
      Exit;
    end;

    pWaveBuffer[i] := GlobalLock(hWaveBuffer[i]);
    if (pWaveBuffer[i] = nil) then
    begin
      { Здесь возможна утечка памяти }
      RecErrorMessage := 'Ошибка блокирования памяти wave-буфера';
      AllocPCMBuffers := False;
      Exit;
    end;
    pWaveHeader[i].lpData := pWaveBuffer[i];
  end;

  AllocPCMBuffers := TRUE;
end;

{--------------FreePCMBuffers----------------John Mertus---14-Июнь--97--}

procedure TWaveRecorder.FreePCMBuffers;

{ Освобождаем использованную AllocPCMBuffers память.                    }
{                                                                       }
{***********************************************************************}
var

  i: Integer;

begin

  for i := 0 to fTotalBuffers - 1 do
  begin
    if (hWaveBuffer[i] <> 0) then
    begin
      GlobalUnlock(hWaveBuffer[i]);
      GlobalFree(hWaveBuffer[i]);
      hWaveBuffer[i] := 0;
      pWaveBuffer[i] := nil;
    end;
  end;
end;

{--------------FreeWaveFormatEx--------------John Mertus---14-Июнь--97--}

procedure TWaveRecorder.FreeWaveFormatEx;

{ Просто освобождаем заголовки ExFormat headers                         }
{                                                                       }
{***********************************************************************}
begin

  if (pWaveFmtEx = nil) then
    Exit;
  GlobalUnlock(hWaveFmtEx);
  GlobalFree(hWaveFmtEx);
  pWaveFmtEx := nil;
end;

{-------------TWaveRecorder.Create------------John Mertus-----Авг--97--}

constructor TWaveRecorder.Create(BFSize, TotalBuffers: Integer);

{ Устанавливаем wave-заголовки, инициализируем указатели данных и      }
{ и распределяем буферы дискретизации                                  }
{ BFSize - размер буфера в байтах                                      }
{                                                                      }
{**********************************************************************}
var

  i: Integer;
begin

  inherited Create;
  for i := 0 to fTotalBuffers - 1 do
  begin
    hWaveHeader[i] := 0;
    hWaveBuffer[i] := 0;
    pWaveBuffer[i] := nil;
    pWaveFmtEx := nil;
  end;
  fBufferSize := BFSize;

  fTotalBuffers := TotalBuffers;
  { распределяем память для структуры wave-формата }
  if (not AllocWaveFormatEx) then
  begin
    InitWaveRecorder := FALSE;
    Exit;
  end;

  { ищем устройство, совместимое с доступными wave-характеристиками }
  if (waveInGetNumDevs < 1) then
  begin
    RecErrorMessage := 'Не найдено устройств, способных записывать звук';
    InitWaveRecorder := FALSE;
    Exit;
  end;

  { распределяем память wave-заголовка }
  if (not AllocWaveHeaders) then
  begin
    InitWaveRecorder := FALSE;
    Exit;
  end;

  { распределяем память буфера wave-данных }
  if (not AllocPCMBuffers) then
  begin
    InitWaveRecorder := FALSE;
    Exit;
  end;

  InitWaveRecorder := TRUE;

end;

{---------------------Destroy----------------John Mertus---14-Июнь--97--}

destructor TWaveRecorder.Destroy;

{ Просто освобождаем всю память, распределенную InitWaveRecorder.       }
{                                                                       }
{***********************************************************************}

begin

  FreeWaveFormatEx;
  FreePCMBuffers;
  FreeWaveHeader;
  inherited Destroy;
end;

{------------CloseWaveDeviceRecord-----------John Mertus---14-Июнь--97--}

procedure TWaveRecorder.CloseWaveDeviceRecord;

{ Просто освобождаем (закрываем) waveform-устройство.                   }
{                                                                       }
{***********************************************************************}
var

  i: Integer;

begin

  { если устройство уже закрыто, то выходим }
  if (not bDeviceOpen) then
    Exit;

  { работа с заголовками - unprepare }
  for i := 0 to fTotalBuffers - 1 do
    if (waveInUnprepareHeader(WaveIn, pWaveHeader[i], sizeof(TWAVEHDR)) <> 0)
      then

      RecErrorMessage := 'Ошибка в waveInUnprepareHeader';

  { сохраняем общий объем записи и обновляем показ }
  dwTotalwavesize := dwBytedatasize;

  { закрываем входное wave-устройство }
  if (waveInClose(WaveIn) <> 0) then
    RecErrorMessage := 'Ошибка закрытия входного устройства';

  { сообщаем вызвавшей функции, что устройство закрыто }
  bDeviceOpen := FALSE;

end;

{------------------StopRecord-----------------John Mertus---14-Июнь--97--}

procedure TWaveRecorder.StopRecord;

{ Останавливаем запись и устанавливаем некоторые флаги.                 }
{                                                                       }
{***********************************************************************}
var

  iErr: Integer;

begin

  RecordActive := False;
  iErr := waveInReset(WaveIn);
  { прекращаем запись и возвращаем стоящие в очереди буферы }
  if (iErr <> 0) then
  begin
    RecErrorMessage := 'Ошибка в waveInReset';
  end;

  CloseWaveDeviceRecord;
end;

{--------------AddNextBuffer------------------John Mertus---14-Июнь--97--}

function TWaveRecorder.AddNextBuffer: Boolean;

{ Добавляем буфер ко входной очереди и переключаем буферный индекс.     }
{                                                                       }
{***********************************************************************}
var

  iErr: Integer;

begin

  { ставим буфер в очередь для получения очередной порции данных }
  iErr := waveInAddBuffer(WaveIn, pwaveheader[bufindex], sizeof(TWAVEHDR));
  if (iErr <> 0) then
  begin
    StopRecord;
    RecErrorMessage := 'Ошибка добавления буфера' + TWaveInGetErrorText(iErr);
    AddNextBuffer := FALSE;
    Exit;
  end;

  { переключаемся на следующий буфер }
  bufindex := (bufindex + 1) mod fTotalBuffers;
  QueuedBuffers := QueuedBuffers + 1;

  AddNextBuffer := TRUE;
end;

{--------------BufferDoneCallBack------------John Mertus---14-Июнь--97--}

procedure BufferDoneCallBack(
  hW: HWAVE; // дескриптор waveform-устройства
  uMsg: DWORD; // посылаемое сообщение
  dwInstance: DWORD; // экземпляр данных
  dwParam1: DWORD; // определяемый приложением параметр
  dwParam2: DWORD; // определяемый приложением параметр
  ); stdcall;

{ Вызывается при наличии у wave-устройства какой-либо информации,       }
{ например при заполнении буфера                                        }
{                                                                       }
{***********************************************************************}
var

  BaseRecorder: PWaveRecorder;
begin

  BaseRecorder := Pointer(DwInstance);
  with BaseRecorder^ do
  begin
    ProcessBuffer(uMsg, pWaveBuffer[ProcessedBuffers mod fTotalBuffers],
      WaveBufSize);

    if (RecordActive) then
      case uMsg of
        WIM_DATA:
          begin
            BaseRecorder.AddNextBuffer;
            ProcessedBuffers := ProcessedBuffers + 1;
          end;
      end;
  end;
end;

{------------------StartRecord---------------John Mertus---14-Июнь--97--}

function TWaveRecorder.StartRecord: Boolean;

{ Начало записи.                                                        }
{                                                                       }
{***********************************************************************}
var

  iErr, i: Integer;

begin

  { начало записи в первый буфер }
  iErr := WaveInStart(WaveIn);
  if (iErr <> 0) then
  begin
    CloseWaveDeviceRecord;
    RecErrorMessage := 'Ошибка начала записи wave: ' +
      TWaveInGetErrorText(iErr);

  end;

  RecordActive := TRUE;

  { ставим в очередь следующие буферы }
  for i := 1 to fTotalBuffers - 1 do
    if (not AddNextBuffer) then
    begin
      StartRecord := FALSE;
      Exit;
    end;

  StartRecord := True;
end;

{-----------------SetupRecord---------------John Mertus---14-Июнь--97--}

function TWaveRecorder.SetupRecord(P: PWaveRecorder): Boolean;

{ Данная функция делает всю работу по созданию waveform-"записывателя". }
{                                                                       }
{***********************************************************************}
var

  iErr, i: Integer;

begin

  dwTotalwavesize := 0;
  dwBytedatasize := 0;
  bufindex := 0;
  ProcessedBuffers := 0;
  QueuedBuffers := 0;

  { открываем устройство для записи }
  iErr := waveInOpen(@WaveIn, WAVE_MAPPER, pWaveFmtEx,
    Integer(@BufferDoneCallBack),

    Integer(P), CALLBACK_FUNCTION + WAVE_ALLOWSYNC);
  if (iErr <> 0) then
  begin
    RecErrorMessage := 'Не могу открыть входное устройство для записи: ' + ^M
      +

    TWaveInGetErrorText(iErr);
    SetupRecord := FALSE;
    Exit;
  end;

  { сообщаем CloseWaveDeviceRecord(), что устройство открыто }
  bDeviceOpen := TRUE;

  { подготавливаем заголовки }

  InitWaveHeaders();

  for i := 0 to fTotalBuffers - 1 do
  begin
    iErr := waveInPrepareHeader(WaveIn, pWaveHeader[I], sizeof(TWAVEHDR));
    if (iErr <> 0) then
    begin
      CloseWaveDeviceRecord;
      RecErrorMessage := 'Ошибка подготовки заголовка для записи: ' + ^M +
        TWaveInGetErrorText(iErr);
      SetupRecord := FALSE;
      Exit;
    end;
  end;

  { добавляем первый буфер }
  if (not AddNextBuffer) then
  begin
    SetupRecord := FALSE;
    Exit;
  end;

  SetupRecord := TRUE;
end;

{-----------------ProcessBuffer---------------John Mertus---14-Июнь--97--}

procedure TWaveRecorder.ProcessBuffer(uMsg: Word; P: Pointer; n:
  Integer);

{ Болванка процедуры, вызываемой при готовности буфера.                 }
{                                                                       }
{***********************************************************************}
begin
end;

end.



Взято с





Новичкам сюда!


Новичкам сюда!


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


1.  
2.  
3.  
4.  
5.  
6.  
7.  
8.  
9.  
10.  
11.  
12.  
13.  
14.  
15.  
16.  
17.  
18.  

Нужна база данных, работающая без каких-либо драйверов, Без ODBC/BDE/ADO/DAO etc


Нужна база данных, работающая без каких-либо драйверов, Без ODBC/BDE/ADO/DAO etc





Компонент называемый VolgaDB:


Автор:

Vit

Взято из





О DelphiX


О DelphiX




DelphiX - плагин под Дельфи, но не простой, а... нет, и не золотой, он очень нужный! Нужнее DelphiX'а есть только один другой плагин - RX Library, но о нем в другой секции. Но что же это за плагин такой? ДельфиИкс - маленький модуль для Дельфей, который позволяет работать с DirectX под Delphi. С названием разобрались, теперь надо разобраться с тем, как он работает. В состав ДельфИкса входят: DXDraw, DXDib, DXImagelist, DX3D, DXSound, DXWave, DXWaveList, DXInput, DXPlay, DXSpriteEngine, DXTimer, DXPaintBox. Теперь расскажу о каждом понемногу:

DXDraw
основной компонент ДельфИкса, это его "рабочий стол", на котором отображается все остальное.
 

DXDib


абсолютно ненужный компонент, не знаю зачем он здесь...  
 

DXImagelist


компонент для работы с файлами графики, контейнер, куда пихают все картинки
 

DX3D


комментарии излишни...  
 

DXSound


компонент для доступа к библиотеке DirectX dxsound.dll
 

DXWave


то же, что и DXDib  
 

DXWaveList


то же, что и DXImageList, но для звука
 

DXInput


Этот компонент должен служить для ввода данных с клавиатуры, мышки и джойстика  
 

DXPlay


компонент для создания мультиплеера
 

DXSpriteEngine


то же, что и DXSound, но для графики  
 

DXTimer


обычный таймер. но с немного расширенными возможностями
 

DXPaintBox

- ???

Скачать ДельфИкс вы сможете на сайте http://instrumentari.narod.ru

Взято из





O сохранении иконок 32х32 в 256-цветном формате


O сохранении иконок 32х32 в 256-цветном формате



Высылаю Вам информацию для FAQ о сохранении иконок 32х32 в 256-цветном
формате. Распоряжайтесь ею как Вам нужно. Размер кода большой, думаю для FAQ
не годится, скорее для раздела "Обмен опытом", но Вам видней. И сообщите,
пожалуйста, где эта информация будет размещена, чтоб не посылать каждому,
кто обратится, все описание, а просто дать ссылку.
С уважением, Владимир, г.Иркутск, nsvi@hotbox.ru


Суть вопроса: я столкнулся с проблемой сохранения полноцветных иконок, когда
понадобилось немного изменить имеющиеся у меня для своих программ. Ни родной
Image Editor от Delphi6, ни другие редакторы не смогли мне помочь. Могли это
делать платные редакторы, но они не для нас. Начав разбираться, я обнаружил,
что созданная функцией CreateIconIndirect иконка нормально выглядит, если
после создания ее кинуть на форму, однако после записи Icon.SaveToFile
иконка обезображивается. Это происходит на стадии записи иконки. Поискав
информацию, облазив форумы, я понял, что либо этой проблемой никто не
занимался, либо с ней все мирятся и заниматься не хотят, хотя интересующиеся
имеются.
Став заниматься проблемой вплотную, я решил использовать нетипизированные
файлы, которым все равно, что в них записывают. Эти файлы хорошо подходят
для работы с массивами байт и, если создать массив полностью соответствующий
структуре иконки, то записав массив в нетипизированный файл, получим искомую
иконку.
Пришлось изучить структуру файла ICO и вот результат.
Для использования процедуры нет необходимости изучать файл ICO, если только
вы не хотите ничего изменять, например, размеры иконки. В принципе, можно
сделать универсальную процедуру, которая бы определяла размер иконки и, в
соответствии с размерами, создавала иконку. Все адреса и размеры, указанные
в процедуре, являются абсолютными и изменению не подлежат, так как именно от
них зависит, будет ли соответствовать создаваемый массив файлу ICO.
В процедуру введено автоопределение количества цветов палитры, от этого
зависит структура файла. Вам нет нужды указывать количество используемых
цветов.
Кроме того, введено определение размера рисунка и если он не 32х32, то
процедура прекратит работу. Это произойдет и в случае если кому-то захочется
использовать более 256 цветов. Тем, кто хочет узнать о структуре файлов ICO,
читать после кода процедуры.
Используя этот метод можно работать с любыми файлами, например, для открытия
и побайтного изменения EXE-файлов.
Для работы этой процедуры нужен рисунок 32х32 на канве (у меня Image2),
диалоговое окно SaveDialog1 (но не SavePictureDialog1)
и все. Если нужно, измените имя канвы, оно встречается в двух строчках кода,
и имя диалогового окна. Процедура с комментариями ниже:
//Записываем картинку как иконку (файл .ico)
{Так как стандартные функции не дают возможности создавать 256 цветные
иконки, мы пойдем другим путем, хоть он и медленнее и длиннее.
Создадим массив байт, который запишем как файл, это и будет
искомая иконка. Для этого создадим промежуточные массивы, иначе процедура
станет слишком навороченной, затем проверим количество используемых цветов
и, в зависимости от этого, создадим окончательный массив.}

procedure TForm1.ToolButton6Click(Sender: TObject);
var
  MAnd, MXor, MOsn, MCol: array of Byte;
  MPix, MOrg: array of TColor;
  a, b, c, n, m: integer;
  Bt, Bu, Indicator: Byte;
  p: TColor;
  k: boolean;
  F: file;
  LenOsn, LenXor, LenCol: integer;
begin
  if (Image2.Picture.Height <> 32) or (Image2.Picture.Width <> 32) then
    begin
      ShowMessage('Исходный рисунок не 32х32');
      exit;
    end;
{Создание массивов: MAnd - массив маски AND, MXor - массив маски Xor,
основного массива MOsn - куда будет собираться вся информация,
массив MPix - массив пикселов рисунка начиная с левого нижнего угла,
MCol - массив таблицы цветов - палитры, MOrg - массив оригинальных цветов}
{Заполнение массивов MPix и MAnd}
{Активизация массивов}
  SetLength(MPix, 1024);
  SetLength(MAnd, 128);
{Установка счетчиков}
  b := 0; //Счетчик битов
  a := 0; //Счетчик пикселов картинки
  c := 0; //Счетчик байтов маски MAnd
  Bt := 0; //Формируемый байт маски MAnd
{Преобразование картинки в удобный формат, с началом от 0,
и перенос ее в таком виде в массив MPix, меняя по пути белый -
будущий прозрачный цвет - на черный, заодно заполняем массив MAnd}
  for m := 31 downto 0 do
    for n := 0 to 31 do
      begin
        MPix[a] := Image2.Canvas.Pixels[n, m];
        p := MPix[a];
        if p = RGB(255, 255, 255) then
          begin
            MPix[a] := RGB(0, 0, 0);
            if b = 0 then
              Bt := Bt + 128;
            if b = 1 then
              Bt := Bt + 64;
            if b = 2 then
              Bt := Bt + 32;
            if b = 3 then
              Bt := Bt + 16;
            if b = 4 then
              Bt := Bt + 8;
            if b = 5 then
              Bt := Bt + 4;
            if b = 6 then
              Bt := Bt + 2;
            if b = 7 then
              Bt := Bt + 1;
          end;
        b := b + 1;
        if b = 8 then
          begin
            MAnd[c] := Bt;
            Bt := 0;
            c := c + 1;
            b := 0;
          end;
        a := a + 1;
      end;
{Заполнение маски MOrg нулями, предполагая 256 цветов}
  SetLength(MOrg, 256);
  for n := 0 to 255 do
    MOrg[n] := RGB(255, 255, 255);
{Оцениваем количество цветов и от результата переделываем MOrg}
  a := 0; //Счетчик количества цветов
  for n := 0 to 1023 do
    begin
      p := MPix[n];
      k := false;
      for m := 0 to 255 do
        begin
          if k = false then
            if p = MOrg[m] then
              k := true;
        end;
      if k = false then
        begin
          if a > 255 then
            begin
              ShowMessage('Ваш рисунок имеет более 256 цветов');
              MOrg := nil;
              MPix := nil;
              MAnd := nil;
              exit;
            end;
          MOrg[a] := p;
          a := a + 1;
        end;
    end;
  LenOsn := 2238;
  LenXor := 1024;
  LenCol := 1024;
  Indicator := 1;
  if a < 15 then
    begin
      LenOsn := 766;
      LenXor := 512;
      Indicator := 0;
    end;
{Заполняем маски нулями}
  SetLength(MXor, LenXor);
  SetLength(MCol, LenCol);
  for n := 0 to LenXor - 1 do
    begin
      MXor[n] := 0;
    end;
  for n := 0 to LenCol - 1 do
    begin
      MCol[n] := 0;
    end;
{Заполнение массива MXor согласно массива MPix}
  Bu := 0;
  b := 0;
  c := 0;
  for n := 0 to 1023 do
    begin
      p := MPix[n];
      k := false;
      for Bt := 0 to 255 do
        begin
          if k = false then
            if p = MOrg[Bt] then
              begin
                k := true;
                if indicator = 1 then
                  MXor[n] := Bt
                else if b = 1 then
                  begin
                    Bu := 16 * Bu + Bt;
                    MXor[c] := Bu;
                    c := c + 1;
                    b := 0;
                  end
                else
                  begin
                    Bu := Bt;
                    b := 1;
                  end;
              end;
        end;
    end;
{Заполняем массив MCol согласно массива MOrg}
  begin
    a := 0;
    for n := 0 to 255 do
      begin
        p := MOrg[n];
        MCol[a] := GetBValue(p);
        MCol[a + 1] := GetGValue(p);
        MCol[a + 2] := GetRValue(p);
        a := a + 4;
      end;
  end;
{Заполняем MOsn нулями}
  SetLength(MOsn, LenOsn);
  for n := 0 to LenOsn - 1 do
    MOsn[n] := 0;
{Заполняем массив MOsn для 256 цветной иконки}
  if indicator = 1 then
    begin
      MOsn[2] := 1;
      MOsn[4] := 1;
      MOsn[6] := 32;
      MOsn[7] := 32;
      MOsn[14] := $A8;
      MOsn[15] := 8;
      MOsn[18] := $16;
      MOsn[22] := $28;
      MOsn[26] := 32;
      MOsn[30] := $40;
      MOsn[34] := 1;
      MOsn[36] := 8;
      MOsn[42] := $80;
      MOsn[43] := 4;
      MOsn[55] := 1;
      m := 0;
      for n := 62 to 1085 do
        begin
          MOsn[n] := MCol[m];
          m := m + 1;
        end;
      m := 0;
      for n := 1086 to 2109 do
        begin
          MOsn[n] := MXor[m];
          m := m + 1;
        end;
      m := 0;
      for n := 2110 to 2237 do
        begin
          MOsn[n] := MAnd[m];
          m := m + 1;
        end;
    end;
{Заполняем массив MOsn для 16-цветной}
  if indicator = 0 then
    begin
      MOsn[2] := 1;
      MOsn[4] := 1;
      MOsn[6] := 32;
      MOsn[7] := 32;
      MOsn[8] := 4;
      MOsn[14] := $E8;
      MOsn[15] := 2;
      MOsn[18] := $16;
      MOsn[22] := $28;
      MOsn[26] := 32;
      MOsn[30] := 64;
      MOsn[34] := 1;
      MOsn[36] := 4;
      MOsn[43] := 2;
      MOsn[54] := 16;
      m := 0;
      for n := 62 to 125 do
        begin
          MOsn[n] := MCol[m];
          m := m + 1;
        end;
      m := 0;
      for n := 126 to 637 do
        begin
          MOsn[n] := MXor[m];
          m := m + 1;
        end;
      m := 0;
      for n := 638 to 765 do
        begin
          MOsn[n] := MAnd[m];
          m := m + 1;
        end;
    end;
{Закрываем все массивы, кроме MOsn}
  MAnd := nil;
  MXor := nil;
  MOrg := nil;
  MCol := nil;
  MPix := nil;
{Записываем массив MOsn в файл, для этого создаем нетипизированный файл,
активизируем его и побайтно пишем в него данные из массива MOsn}
  if SaveDialog1.Execute then
    begin
      AssignFile(f, SaveDialog1.FileName + '.ico');
      Rewrite(f, 1);
      for n := 0 to LenOsn - 1 do
        begin
          Bt := MOsn[n];
          BlockWrite(f, Bt, 1);
        end;
      CloseFile(f);
      MOsn := nil;
    end;
end;


Существуют иконки более сложной структуры. Они могут содержать несколько
рисунков. Я с ними не разбирался.
Может быть кто-то из профессионалов скажет - примитивщина, нагородил. Ну что
ж, может быть есть пути проще и лучше, но когда я спрашивал на форумах никто
не смог помочь. Главное, работает без проблем.
Я всего месяц как познакомился с Delphi и Паскалем, думаю
можно кое-что и простить.
О структуре иконки. Информации немного в интернете, но кое-что я раскопал.
Пришлось просмотреть коды разных иконок, чтобы иметь о них представление.
Файл ICO очень похож на BMP:
в начале файла заголовок BITMAPFILEHEADER, размером обычно 22 байта. В нем
содержится информация о файле. За ним идет BITMAPINFOHEADER, 40 байт,
содержит информацию о ширине и высоте иконки в пикселах, количество бит на
пиксел в структуре картинки, занимаемое место таблицей цветов и другая
информация. Для примера оба заголовка для 16-цветной иконки.
В квадратных скобках номер байта в файле, далее эго значение.
[2] := 1; // Всегда 1
[4] := 1; // Всегда 1
[6] := 32; // Ширина и
[7] := 32; // высота в пикселах?
[8] := 4 // Не знаю
[14] := $E8; // Младший байт размера файла минус 22(размер BITMAPFILEHEADER)
[15] := 2; // Старший байт размера файла
[18] := 22; // Размер BITMAPFILEHEADER
[22] := 40; // Размер заголовка BITMAPINFOHEADER
[26] := 32; // Ширина в пикселах
[30] := 64; // Высота в пикселах обоих масок в сумме
[34] := 1; // Число плоскостей
[36] := 4; // бит/пиксел для таблицы пикселов
[43] := 2; // Старший байт размера таблицы пикселов (т.е.$200=512)
[54] := 16; // Число используемых цветов (не обязательно)
Остальные не указанные байты и до адреса 62 все 0.
После заголовков находится таблица цветов.
Каждый цвет занимает 4 байта. По порядку - синий, зеленый, красный и нулевой
байты. Первые 3 байта образуют цвет пиксела. То есть, каждый пиксел на
иконке может иметь 256х256х256 оттенков. При 16 цветной иконке таблица
цветов занимает 16х4=64 байта, при 256 цветной - 1024 байта. По идее, при
записи иконки любая программа должна просканировать иконку и все найденные
цвета записать в таблицу.Почему этого не происходит я не знаю. Windows эти
цвета при записи игнорирует и устанавливает свои.
Если же при 16 цветной палитре используются только, допустим, 6 цветов, то
остальные 10 цветов не нужны, однако место для них остается, но оно не
используется.
Следующим блоком в файле идет таблица пикселов. Первый пиксел это самый
нижний в левом углу. И переписываются они в таблицу построчно. Основной
параметр, по которому таблица организуется, это количество бит на пиксел.
Каждый элемент этой таблицы - это номер цвета в таблице цветов. При выводе
на экран иконки берется очередной элемент, то есть номер цвета в таблице
цветов, берет по номеру цвет из таблицы цветов и этот цвет выводится на
экран. Так как для определения номера в таблице из 16 цветов достаточно 4
бит, то каждый байт таблицы пикселов определяет 2 пиксела. Причем, младшие 4
разряда относятся к первому, а старшие - к следующему пикселам.
Для 256 цветов необходим полный байт. Поэтому, таблица пикселов занимает
соответственно 512 и 1024 байтов.
Таблица пикселов это образ маски XOR иконки, цвет, который должен быть
прозрачным, заменен черным, в остальном же это рисунок, который вы создали в
редакторе.
И последним идет таблица маски AND. Это двухцветная маска вашего рисунка,
имеет только белый цвет (бит включен) и черный цвет (бит равен 0). Белый
цвет находится там, где будет прозрачный цвет, черный скрывает все
остальное. При наложении двух масок друг на друга тот пиксел, у которого на
маске AND белый, а на маске XOR черный цвет, будет невидим.
Так как маска AND монохромная, то один байт содержит информацию о 8
пикселах, а размер маски будет 1024/8=128 байт.
Имеем:
для 16-цветной иконки размер 22+40+64+512+128=766 байт,
для 256 - 22+40+1024+1024+128=2238 байт.
Создав массив из соответствующим образом пребразованного рисунка, мы можем
записать его байты в нетипизированный файл, которому все равно, что в нем
находится, добавив к имени расширение .ico, мы получим то, что было нужно.
Конечно, есть разные варианты размеров иконок, у них могут меняться размеры
заголовков, использоваться другая информация, но структуры эти должны
присутствовать. Существуют иконки гораздо большие размером, но я с такими не
разбирался.

Взято с Vingrad.ru




Об авторских правах


Об авторских правах


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

обязана осуществляться с ссылкой на первоисточник

(в тех случаях, когда автором ответа являюсь я (

Vit) вы можете использовать ответ вообще без всяких ограничений!).

Я старался везде, где это только было возможно указывать источник информации. Но форум есть форум, и конечно же никто не может гарантировать, что какие-то коды не были присланы участниками форума без согласования с авторами и ссылок на первоисточник. Кроме того, коды и ответы в сети зачастую стали уже безымянными, они цитируются с незначительными исправлениями во многих FAQ, статьях, сайтах и форумах (так я уже встречал свои коды в чужих FAQ). Поэтому, я допускаю что выпуском этого FAQ я мог (естественно не специально и без злого умысла) нарушить чьи-то авторские права. Если такое произошло, сообщите мне по адресу: vit@vingrad.ru и я постараюсь изъять такие материалы так быстро, как это возможно или (по согласованию с автором) предоставить полную информацию об авторских правах на тот или иной код.








Объект TPrinter


Объект TPrinter



Cодержание раздела:






Объектное ориентирование


Объектное ориентирование



Cодержание раздела:

Примечание от Vit: в раздел помещены не только вопросы по объектно-ориентированному программированию и ООП модели Дельфи, но и часть специфических вопросов по организации VCL, я решил не выносить их отдельно в VCL, так как считаю вполне логичным собрать всё в одном разделе.































См. также статьи в других разделах:





См. также другие разделы:









Объяснения некоторых ошибок при работе с базами данных


Объяснения некоторых ошибок при работе с базами данных



Cодержание раздела:




















См. также статьи в других разделах:









См. также другие разделы:




ObjectHaven


ObjectHaven




СУБД ObjectHaven предназначена для управления Базами Данных, состоящими из множества взаимосвязанных COM-объектов, наделенных свойством перманентности. Хотя ObjectHaven построена по принципу объектно-ориентированных СУБД, ее реализация имеет ряд существенных отличий от традиционных ООСУБД. Важнейшим отличием ObjectHaven от ООСУБД является то, что в Базах Данных ObjectHaven хранятся COM-объекты, а не объекты какого-либо конкретного языка программирования. Это дает основание говорить об ObjectHaven как о представителе принципиально нового класса компонентно-ориентированных СУБД. ObjectHaven обеспечивает следующие преимущества:

Открытость метаданных, Улучшенная совместимость, Поддержка гетерогенных сред разработки

Большинство существующих ООСУБД предоставляют свои собственные средства описания метаданных БД, и связи метаданных БД с конкретными системами программирования. Так, для введения в ООСУБД поддержки такого языка программирования, как, например, C++, разработчикам ООСУБД потребуется написать комплект библиотек и утилит, обеспечивающих экспорт метаданных в файлы, содержащие исходные тексты на C++, причем скорее всего метаданные, предназначенные для экспорта в C++, будут непригодны для экспорта, например, в Pascal, и наоборот. При таком подходе не остается других разумных способов предоставления метаданных для разных систем программирования, кроме как содержание нескольких копий одних и тех же метаданных, предназначенных для экспорта в различные системы программирования, что в свою очередь требует постоянных усилий по сохранению соответствия различных версий метаданных. Соответственно поддержку нескольких языков программирования подобными ООСУБД нельзя назвать удовлетворительной для тех задач, которые требуют гетерогенную разработку. Для Баз Данных, управляемых ObjectHaven, метаданными являются обычные библиотеки типов COM, которые на сегодняшний день поддерживается всеми популярными системами программирования, ориентированными на разработку под Windows; и все СП под Windows, которые могут появиться в будущем, наверняка также будут поддерживать эту технологию.

Полная инкапсуляция реализации компонентов базы данных. Совместимость версий базы данных.

При обычном подходе, принятом в ООСУБД, метаданные, помимо всего прочего, хранят в себе информацию об иерархии классов перманентных объектов, их методах и членах данных. Такая подробная информация не нужна клиенту базы данных (его не должны интересовать подробности реализации объектов базы данных, ему достаточно интерфейсной части), и ограничивает гибкость модели данных. Несоблюдение принципа отделения интерфейса от реализации в типичных ООСУБД влечет да собой трудности в разработке и сопровождении программных продуктов, использующих такие ООСУБД. Например, доработка модели данных потребует доработку всех приложений, ее использующих. Использование в ObjectHaven библиотек типов COM в качестве хранилищ метаданных позволяет неукоснительно следовать принципу отделения интерфейса от реализации. Даже существенные доработки модели данных в базе данных ObjectHaven при надлежащем подходе не приведут к конфликтам версий модулей программной системы, использующей базу данных ObjectHaven, что позволит существенно сэкономить на сопровождении такой программной системы.

Улучшенное повторное использование компонентов схемы БД. Возможность сборки БД из повторно используемых компонентов

Эффективное повторное использование кода является розовой мечтой разработчиков ПО. Однако мечта эта на практике зачастую оказывается на удивление тяжело достижимой. Не вдаваясь в анализ причин, приведем некоторые конкретные факты:
Попытки увеличить долю повторного использования кода путем организации репозиториев повторно используемого кода себя не оправдали и уже давно не популярны.
Современное программное обеспечение в среднем на 70% состоит из повторно используемых компонентов, приобретенных у третьих фирм. Такие компоненты используют какую-либо относительно универсальную технологию, вроде ActiveX, и поставляются в виде исполняемых модулей, без исходных текстов.
Наиболее популярные СП, такие как Delphi и VisualBasic, используют либо ActiveX, либо аналогичные технологии, либо и то и другое вместе.
Очевидно, что на сегодняшний день путь к успешному повторному использованию кода лежит через задействование технологии, обеспечивающей унифицированный способ взаимодействия с повторно используемыми программными компонентами. ObjectHaven в связке с Automation и стандартизованным набором COM-интерфейсов предоставляет как раз такую технологию в приложении к базам данных. ObjectHaven позволяет сформировать целый рынок повторно используемых компонентов БД, ориентированных на применение в наиболее популярных предметных областях, и распространяемых в двоичном виде, без исходных текстов.

Возможность контролировать сложность базы данных

Использование СОМ предполагает проектирование схемы БД в форме набора объектов, реализующих четко определенные интерфейсы. Это позволяет абстрагироваться от деталей реализации отдельных компонентов схемы БД, сосредоточившись на проектировании интерфейсов, в результате чего схема БД становится более простой для понимания. Сокрытие реализации отдельных компонентов схемы БД обеспечивает большой "запас прочности" схемы БД относительно увеличения ее сложности, так как интерфейсы в гораздо меньшей степени подвержены изменениям, чем объекты, их реализующие. Таким образом, СУБД ObjectHaven обеспечивает стабильную основу для разработки сложных баз данных. Соблюдение в ObjectHaven принципа отделения интерфейса от реализации позволяет локализовать сложность модели данных в отдельных компонентах, благодаря чему появляется возможность постепенного, контролируемого наращивания сложности БД путем эволюционного развития ее отдельных компонентов.




Обмен данными с Excel


Обмен данными с Excel



Автор: Fernando Silva

В Delphi 5, для обмена данными между Вашим приложением и Excel можно использовать компонент TExcelApplication, доступный на Servers Page в Component Palette.

На форме находится TStringGrid, заполненный некоторыми данными и две кнопки, с названиями To Excel и From Excel. Так же на форме находится компонент TExcelApplication со свойством Name, содержащим XLApp и свойством ConnectKind, содержащим ckNewInstance.

Когда нам необходимо работать с Excel, то обычно мы открываем ExcelApplication, затем открываем WorkBook и в конце используем WorkSheet.

Итак, несомненный интерес представляет для нас листы (WorkSheets) в книге (WorkBook). Давайте посмотрим как всё это работает.

Посылка данных в Excel

Это можно сделать с помощью следующей процедуры :

procedure TForm1.BitBtnToExcelOnClick(Sender: TObject);
var
  WorkBk: _WorkBook; //  определяем WorkBook
  WorkSheet: _WorkSheet; //  определяем WorkSheet
  I, J, K, R, C: Integer;
  IIndex: OleVariant;
  TabGrid: Variant;
begin
  if GenericStringGrid.Cells[0, 1] <> '' then
    begin
      IIndex := 1;
      R := GenericStringGrid.RowCount;
      C := GenericStringGrid.ColCount;
   // Создаём массив-матрицу
      TabGrid := VarArrayCreate([0, (R - 1), 0, (C - 1)], VarOleStr);
      I := 0;
   //  Определяем цикл для заполнения массива-матрицы
      repeat
        for J := 0 to (C - 1) do
          TabGrid[I, J] := GenericStringGrid.Cells[J, I];
        Inc(I, 1);
      until
        I > (R - 1);

   // Соединяемся с сервером TExcelApplication
      XLApp.Connect;
    // Добавляем WorkBooks в ExcelApplication
      XLApp.WorkBooks.Add(xlWBatWorkSheet, 0);
   // Выбираем первую WorkBook
      WorkBk := XLApp.WorkBooks.Item[IIndex];
   // Определяем первый WorkSheet
      WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;
   // Сопоставляем Delphi массив-матрицу с матрицей в WorkSheet
      Worksheet.Range['A1', Worksheet.Cells.Item[R, C]].Value := TabGrid;
   // Заполняем свойства WorkSheet
      WorkSheet.Name := 'Customers';
      Worksheet.Columns.Font.Bold := True;
      Worksheet.Columns.HorizontalAlignment := xlRight;
      WorkSheet.Columns.ColumnWidth := 14;
   // Заполняем всю первую колонку
      WorkSheet.Range['A' + IntToStr(1), 'A' + IntToStr(R)].Font.Color := clBlue;
      WorkSheet.Range['A' + IntToStr(1), 'A' + IntToStr(R)].HorizontalAlignment := xlHAlignLeft;
      WorkSheet.Range['A' + IntToStr(1), 'A' + IntToStr(R)].ColumnWidth := 31;
   // Показываем Excel
      XLApp.Visible[0] := True;
   // Разрываем связь с сервером
      XLApp.Disconnect;
   // Unassign the Delphi Variant Matrix
      TabGrid := Unassigned;
    end;
end;


Получение данных из Excel

Это можно сделать с помощью следующей процедуры :

procedure TForm1.BitBtnFromExcelOnClick(Sender: TObject);
var
  WorkBk: _WorkBook;
  WorkSheet: _WorkSheet;
  K, R, X, Y: Integer;
  IIndex: OleVariant;
  RangeMatrix: Variant;
  NomFich: WideString;
begin
  NomFich := 'C:\MyDirectory\NameOfFile.xls';
  IIndex := 1;
  XLApp.Connect;
// Открываем файл Excel
  XLApp.WorkBooks.Open(NomFich, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, 0);
  WorkBk := XLApp.WorkBooks.Item[IIndex];
  WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;
// Чтобы знать размер листа (WorkSheet), т.е. количество строк и количество
// столбцов, мы активируем его последнюю непустую ячейку
  WorkSheet.Cells.SpecialCells(xlCellTypeLastCell, EmptyParam).Activate;
// Получаем значение последней строки
  X := XLApp.ActiveCell.Row;
// Получаем значение последней колонки
  Y := XLApp.ActiveCell.Column;
// Определяем количество колонок в TStringGrid
  GenericStringGrid.ColCount := Y;
// Сопоставляем матрицу WorkSheet с нашей Delphi матрицей
  RangeMatrix := XLApp.Range['A1', XLApp.Cells.Item[X, Y]].Value;
// Выходим из Excel и отсоединяемся от сервера
  XLApp.Quit;
  XLApp.Disconnect;
//  Определяем цикл для заполнения TStringGrid
  K := 1;
  repeat
    for R := 1 to Y do
      GenericStringGrid.Cells[(R - 1), (K - 1)] := RangeMatrix[K, R];
    Inc(K, 1);
    GenericStringGrid.RowCount := K + 1;
  until
    K > X;
// Unassign the Delphi Variant Matrix
  RangeMatrix := Unassigned;
end;

Взято с Исходников.ru




Обмен информацией между приложениями Win32


Обмен информацией между приложениями Win32 - Win16




Пользуйтесь сообщением WM_COPYDATA.
Для Win16 константа определена как $004A, в Win32 смотрите в WinAPI Help.

#define WM_COPYDATA                     0x004A 
/* 
 * lParam of WM_COPYDATA message points to... 
 */ 
typedef struct tagCOPYDATASTRUCT { 
    DWORD dwData; 
    DWORD cbData; 
    PVOID lpData; 
} COPYDATASTRUCT, *PCOPYDATASTRUCT; 

Обработка исключений


Обработка исключений



Each function listed below returns error handling information or performs a task that relates to error handling.



DbiGetErrorContext:
After receiving an error code back from a call, enables the client to probe BDE for more specific
error information.

DbiGetErrorEntry:
Returns the error description of a specified error stack entry.

DbiGetErrorInfo:
Provides descriptive error information about the last error that occurred.

DbiGetErrorString:
Returns the message associated with a given error code.


Взято с

Delphi Knowledge Base




Обработка исключения index not found


Обработка исключения index not found




Как мне открыть таблицу dBASE без требуемого MDX-файла? Я постоянно получаю исключение "Index not found..." (индекс не найден).

Во время создания таблицы dBASE с production-индексом (MDX) в заголовке DBF-файла устанавливается специальный байт. При последующем открытии таблицы, dBASE-драйвер читает этот специальный байт и, если он установлен, он также пытается открыть файл MDX. Если попытка открыть файл MDX заканчивается неудачей, возникает исключительная ситуация.

Для решения этой проблемы вам необходимо обнулить этот байт (28-й десятичный байт) в файле DBF, избавляющий таблицу от зависимости MDX-файла.

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

unitFixit;

interface

uses

  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
  Controls, Forms, Dialogs, StdCtrls, DB, DBTables, Grids, DBGrids;

type

  TForm1 = class(TForm)
    Table1: TTable;
    ;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var

  Form1: TForm1;

implementation

{$R *.DFM}

const

  TheTableDir = 'c:\temp\';
  TheTableName = 'animals.dbf';

procedure RemoveMDXByte(dbFile: string);
{ Данная процедура использует в качестве параметра имя файла DBF   }
{ и исправляет его заголовок для того, чтобы не требовать MDX-файл }
const

  Value: Byte = 0;
var

  F: file of byte;
begin

  AssignFile(F, dbFile);
  Reset(F);
  Seek(F, 28);
  Write(F, Value);
  CloseFile(F);
end;

procedure TForm1.Button1Click(Sender: TObject);
{ Данная процедура вызывается в ответ на нажатие кнопки. Она }
{ пытается открыть таблицу и, если файл .MDX не найден,      }
{ DBF-файл исправляется и управление вновь передается данной }
{ процедуре для повторного открытия таблицы, но уже без MDX  }
begin

  try
    { устанавливаем каталог таблицы }
    Table1.DatabaseName := ThheTableDir;
    { устанавливаем имя таблицы }
    Table1.TableName := TheTableName;
    { пытаемся открыть таблицу }
    Table1.Open;
  except
    on E: EDBEngineError do
      { Нижеследующее сообщение указывает на то, что файл MDX не найден: }
      if Pos('Index does not exist. File', E.Message) > 0 then
      begin
        { Сообщаем пользователю о наличии проблемы. }
        MessageDlg('Файл MDX не найден. Попытка открытия
          без индекса.', mtWarning, [mbOk], 0);
          { удаляем байт MDX из заголовка таблицы }
          RemoveMDXByte(TheTableDir + TheTableName);
          { Посылаем кнопке сообщение для эмуляции ее нажатия. }
          { Этот трюк заставит данную процедуру выполниться    }
          { повторно, и таблица будет открыта без файла MDX    }
          PostMessage(Button1.Handle, cn_Command, bn_Clicked, 0);
      end;
  end;
end;

end.



Взято из





Обработка событий Qt в Kylix-приложении


Обработка событий Qt в Kylix-приложении



Автор: Андрей Боровский ()

Что представляют собой события Qt?

Система Windows взаимодействует с приложениями посредством механизма сообщений, обработка которых осуществляется в оконной функции. В приложениях, созданных на основе Qt library, сообщения используются редко, а основным механизмом взаимодействия между приложениями и системой являются события. Концептуально события Qt очень похожи на события Kylix. В ответ на событие система вызывает обработчик, являющийся методом Qt-объекта. Каждому событию в Qt соответствует свой класс. Обработчику события передается ссылка на экзмпляр класса события, содержащий необходимую информацию о нем. Для того, чтобы определить собственный обработчик события для какого-либо Qt класса, следует создать производный класс этого класса и заместить (override) метод-обработчик соответствующего события. Однако средства Object Pascal не позволяют создавать производные классы Qt и замещать их методы, поэтому в Kylix реализован специальный механизм обработки событий.

События Qt и Kylix

Прежде чем приступить к рассмотрению специальных методов обработки событий Qt в Kylix, необходимо отметить, что в большинстве случаев у программиста не возникает необходимости обращаться к этим методам. Для большинства событий Qt в объектной модели Kylix определены соответствующие события Kylix, и обработка этих событий может быть выполнена стандартными средствами Object Pascal. Тем не менее иногда возникает необходимость ввести в приложении обработку Qt событий, не определенных в VisualCLX. В CLXDisplay API предусмотрено несколько механизмов обработки таких событий. В этой статье будет рассмотрен один из них.

Как и в Delphi, в Kylix приложения строятся на основе класса TApplication. В Kylix в классе TApplication определено свойство OnEvent, позволяющее назначать приложению обработчик событий Qt. Обработчик OnEvent вызывается для каждого события каждого объекта Qt, являющегося дочерним объектом форм приложения, а также для других событий приложения.

Тип процедуры обработчика декларируется следующим образом:

procedure(Sender: QObjectH; Event: QEventH; var Handled: Boolean) of object;

В параметре Sender передается ссылка на объект, который должен реагировать на событие. Параметр Event является указателем на объект события. Он позволяет идентифицировать событие и получить информацию о нем. Переменная Handled позволяет сообщить компоненту Visual CLX о том, следует ли вызывать обработчик события, определенный в компоненте.

Рассмотрим простой пример: допустим мы хотим, чтобы перерисовка окна компонента Label1 типа TLabel не выполнялась, если некоторой глобальной переменной NoRedraw типа Boolean присвоено значение True. для этого напишем следующую процедуру AppEventHandler:


 uses Qt, ...

 ...

 procedure TForm1.AppEventHandler(Sender: QObjectH; Event: QEventH; 
 var Handled: Boolean);
 begin
   Handled:=False;                       // Allow  default event processing
   if Sender=Label1.Handle then          // Identify sending Qt object
   if QEvent_isQPaintEvent(Event) then   // Identify the Paint event
   Handled:=NoRedraw;        // Disable or enable default event processing  
 end; 

 procedure TForm1.FormCreate(Sender: TObject);
 begin
   ...
   Application.OnEvent:=AppEventHandler;
 end; 

 procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
 begin
   ...
   Application.OnEvent:=nil;
 end;



Определить, какому объекту соответствует событие можно, сравнивая значение Sender с полем Handle экземпляров объектов Kylix. Определенные в модуле Qt.pas функции QEvent_isQXXXEvent действуют подобно оператору is языка Object Pascal и позволяют определить тип события, требующего обработки. Аргументом этих функций является ссылка на объект QEvent, являющийся базовым для всех объектов событий. Функция возвращает True, если ей передан укзатель на объект события соответствующего ей типа и False в противном случае. Так как любой объект события в Qt является потомком класса QEvent, после определения типа объекта события мы можем преобразовать тип значения Event к типу указателя на соответствующий объект события и при помощи функций CLXDisplay API получить доступ к методам этого объекта. Переменная Handled служит для того, чтобы разрешить или запретить дальнейшую обработку события стандартными средствами. В начале мы устанавливаем значение этой переменной равным False, для того, чтобы события обрабатывались компонентами. Для того, чтобы запретить дальнейшую обработку события, нужно присвоить этой переменной значение True.

Обратите внимание на присвоение значения nil свойству OnEvent в методе FormClose. Так как время жизни объекта TApplication превышает время жизни объекта TForm, программа может пытаться вызвать обработчик OnEvent после уничтожения формы. Если после закрытия формы данное поле не будет помечено как свободное (unassigned), при выходе из программы возникнет ошибка сегментации.

Программа, демонстрирующая механизм Drag and Drop
Далее рассматривается небольшое приложение, демонстрирующее перенос данных между двумя Qt-программами методом Drag and Drop. Средства Drag and Drop, предоставляемые Kylix, позволяют переносить данные только между объектами Kylix-приложения, а для того, чтобы осуществлять перенос между разными приложениями, придется обратиться к методам объектов Qt library.

Последовательность событий при использовании механизма Drag and Drop такова: когда пользователь переводит курсор мыши в режиме перетаскивания в область визуального Qt объекта, являющегося приемником Drag and Drop, система генерирует событие dragEnterEvent и вызывает обработчик этого события, определенный в объекте-приемнике. В ответ на это событие объект должен либо подтвердить выполнение Drag and Drop операции, либо отказаться от него. Если объект-приемник подтверждает операцию, курсор мыши приобретает характерный вид "готов к приему объекта" и пользователь может отпустить кнопку мыши для передачи объекта Drag and Drop объекту-приемнику. В противном случае курсор принимает вид "прием Drag and Drop запрещен" и дальнейшие события Drag and Drop не генерируются. Если объект-приемник разрешил Drag and Drop операцию, при дальнейшем перемещении мыши над видимой областью соответствующего интерфейсного элемента генерируется серия событий dragMoveEvent. Если пользователь отпускает клавишу мыши, генерируется событие DropEvent. Обработчик этого события должен выполнить непосредственную операцию вставки данных. При выходе курсора, находящегося в режиме перетаскивания, за пределы визуального элемента система генерирует событие dragLeaveEvent. Следует особо отметить, что обработчики событий не должны уничтожать объекты событий, так как эти объекты контролируются системой.

Для того, чтобы какой-либо элемент Qt приложения мог принимать данные, передаваемые посредством Drag and Drop, необходимо создать производный класс от класса этого элемента, разрешить в этом классе прием Drag and Drop объектов, вызвав метод setAcceptDrops базового класса QWidget и заместить методы обработки событий dragEnterEvent, dragMoveEvent, dragLeaveEvent и DropEvent. Программирование Drag and Drop для Kylix приложения имеет некоторые отличия. В Kylix-приложении прием объектов Drag and Drop разрешен для всех компонентов VCL, поэтому вызывать setAcceptDrops не нужно. Кроме того, поскольку методы компонентов VisualCLX перекрывают обработчики событий Drag and Drop, подтверждать Drag and Drop операции необходимо также и в обработчике события dragMoveEvent. В принципе в Kylix приложении вообще нет необходимости обрабатывать событие dragEnterEvent, но в приводимом ниже примере такая обарботка выполняется для большей наглядности.

Демонстрационное приложение позволяет перетаскивать и вставлять фрагменты текста. Для приема и передачи объектов Drag and Drop используется компонент Label. Ниже приводится обработчик Qt событий AppEventHandler.

 procedure TForm1.AppEventHandler;
 var
   QMS : QMimeSourceH;
   S : WideString;
 begin
   Handled:=False;
   if Sender=Label1.Handle then
   if QEvent_isQDropEventEvent(Event) then
   begin
     if QEvent_isQDragEnterEvent(Event) then
     begin
       if QDropEvent_source(QDropEventH(Event))=Label1.Handle then Exit;
       QMS:=QDropEvent_to_QMimeSource(QDropEventH(Event));
       QDropEvent_acceptAction(QDropEventH(Event), QTextDrag_canDecode(QMS));
     end else
     if QEvent_isQDragMoveEvent(Event) then
     begin
       if QDropEvent_source(QDropEventH(Event))=Label1.Handle then Exit;
       QMS:=QDropEvent_to_QMimeSource(QDropEventH(Event));
       QDropEvent_acceptAction(QDropEventH(Event), QTextDrag_canDecode(QMS))
     end else
     begin
       QMS:=QDropEvent_to_QMimeSource(QDropEventH(Event));
       if QTextDrag_canDecode(QMS) then
       QTextDrag_decode(QMS, @S);
       Label1.Caption:=S;
     end;
   end;
 end;



Прежде чем разобрать работу этого метода, отмечу одну деталь: в модуле Qt содержится ошибка. При первых попытках скомпилировать программу для работы с Drag and Drop, компиляция проходила без проблем, однако приложение аварийно завершалось сразу после запуска. Анализ сообщений, выводимых на консоль, показал, что программа не может разрешить ссылку на внешний объект с именем QEvent_isQDropEventEvent. Эта функция декларируется в файле Qt.pas, однако, судя по сообщениям, выводившимся во время выполнения программы, разделяемая библиотека libqtintf.so.2 не экспортирует функцию с таким именем. Очевидно, что в имя функции вкралась опечатка. Для того, чтобы исправить ошибку я изменил декларацию внешней функции QEvent_isQDropEventEvent в файле Qt.pas. В разделе implementation, в строке, объявляющей экспорт функции, после ключевого слова name я заменил "QEvent_isQDropEventEvent" на "QEvent_isQDropEvent", после чего программа заработала. Остальные имена в декларации функции я менять не стал, так что модуль Qt по-прежнему экспортирует эту функцию как QEvent_isQDropEventEvent. Если Вы хотите иметь возможность запускать демонстрационное приложение из этой статьи, Вы должны сделать тоже самое и перекомпилировать модуль Qt. Между прочим, эта же ошибка имеет место и в соответствующем модуле Delphi 6.

Функции QEvent_isQDragEnterEvent, QEvent_isQDragMoveEvent и QEvent_isQDropEventEvent служат для идентификации событий. Порядок их вызова неслучаен. Отношения наследования между классами этих трех событий можно представить так: QDropEvent => QDragMoveEvent => QDragEnterEvent. Таким образом функция QEvent_isQDropEventEvent вернет значение True для всех трех событий. Действия, связанные собственно с событием DropEvent, следует выполнять только если первые проверки вернули False.

Функция QDropEvent_source вызывает метод source объекта события QDropEvent - базового объекта событий Drag and Drop. Обратите внимание на преобразование типа аргумента функции. Такое преобразование корректно, поскольку объекты QDragEnterEvent и QDragMoveEvent являются потомками объекта QDropEvent. QDropEvent_source позволяет определить источник объекта Drag and Drop. Поскольку в создаваемом приложении компонент Label является и приемником и источником объектов Drag and Drop, мы должны проверять, какой Qt объект создал данный объект Drag and Drop и не выполнять дальнейшую обработку события, если источником объекта является компонент Label1.

Функция QDropEvent_to_QMimeSource преобразует объект типа QDropEvent в объект типа QMimeSource, являющийся контейнером передаваемых данных. Функция QDropEvent_acceptAction позволяет подтвердить операцию Drag and Drop, или отказаться от нее. Первый аргумент функции - ссылка на объект QDropEvent, второй аргумент - значение True или False. Для определения того, следует ли обрабатывать событие, мы используем функцию QTextDrag_canDecode. Эта функция возвращает True, если переданный ей объект QMimeSource содержит данные в текстовом формате и False в противном случае. Функция QTextDrag_decode извлекает текстовые данные из объекта QMimeSource. Обратите внимание, что обработчик не должен пытаться уничтожить объекты QMimeSource

Для того, чтобы Qt приложение могло стать источником объектов Drag and Drop, необходимо создать объект-контейнер данных и вызвать один из методов этого объекта : drag или dragCopy. Объект для перетаскивания лучше всего создавать в момент начала движения мыши при нажатой левой кнопке. В нашем приложении эту функцию выполняет обработчик события OnMouseMove объекта Label1:

 procedure TForm1.Label1MouseMove(Sender: TObject; Shift: TShiftState; X,
   Y: Integer);
 var
   TextDragObject : QTextDragH;
   S : WideString;
   name : PChar;
 begin
   if ssLeft in Shift then
   begin
     S:=Label1.Caption;
     name:='TextDrag';
     TextDragObject:=QTextDrag_create(@S, Label1.Handle, name);
     if QDragObject_drag(TextDragObject) then Label1.Caption:='';
   end;
 end;



Функция QTextDrag_create создает экземпляр объекта-контейнера QTextDrag. Первый параметр - указатель на строку текста, которую мы хотим скопировать. Параметр name может содержать любую строку PChar. Функция QDragObject_drag инициирует процесс Drag and Drop. Эта функция не возвращает управление до тех пор, пока пользователь не отпустит кнопку мыши. QDragObject_drag возвращает значение True, если был выбран режим перемещения данных и False, если был выбран режим копирования. Выбор режима в системе определяется состоянием клавиши Ctrl. Процедура QDragObject_dragCopy выполняет те же действия, но возвращает управление сразу и не передает программе никаких значений. Хотя функция QDragObject_drag возвращает управление обработчику только после того, как пользователь отпустит левую кнопку мыши, обработчик не должен пытаться уничтожить объект QDragObject. Время жизни этого объекта контролируется системой, и попытка уничтожить его вызовет ошибку сегментации.

Полный исходный текст демонстрационного приложения можно скачать здесь. При работе с демонстрационным приложением следует учесть, что не все приложения, написанные для Qt/KDE, поддерживают Drag and Drop. Некоторые приложения, например KWord, могут быть источниками текстовых объектов Drag and Drop, но не приемниками. Учтите также, что в настоящее время Drag and Drop в KDE работает не очень стабильно. Работая с демонстрационной программой, вы можете столкнуться с тем, что некоторые другие приложения, использующиеся как источники или приемники объектов Drag and Drop, будут зависать. Это не связано с Kylix, так как при использовании "фирменных" примеров из поставки Qt возникают те же проблемы, по крайней мере на момент написания этой статьи.

Генерация событий Qt в программе

Обычно события Qt генерируются системой в ответ на действия пользователя или события в самой системе. Однако, иногда бывает желательно вызвать обработчик события из непосредственно из программы. Для этого необходимо создать объект соответствующего события и передать его методу Qt объекта. CLXDisplay API позволяет сделать это при помощи набора функций QOpenWidget_XXXEvent, определенных для различных типов событий. В следующем небольшом примере создаются объекты событий, при помощи которых в поле ввода визуального компонента Edit1 типа TEdit вводится текст, а затем эмулируется нажатие клавиши BackSpace (код $1003), стирающее последний введенный символ.

var
  KeyEvent : QKeyEventH;
  S : WideString;
 ...
 begin
   ...
   S:='Some little text';
   KeyEvent:=QKeyEvent_create(QEventType_KeyPress, 0, 0, 0, @S, False, 0);
   QOpenWidget_keyPressEvent(QOpenWidgetH(Edit1.Handle), KeyEvent);
   QKeyEvent_destroy(KeyEvent);
   KeyEvent:=QKeyEvent_create(QEventType_KeyPress, $1003, 0, 0, nil, False, 0);
   QOpenWidget_keyPressEvent(QOpenWidgetH(Edit1.Handle), KeyEvent);
   QKeyEvent_destroy(KeyEvent);
   ...



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

Статья и примеры программ © 2001 Андрей Наумович Боровский.


Взято с Исходников.Ru






Обращение через свойство Controls


Обращение через свойство Controls





procedure TForm1.UpDown1Click(Sender: TObject; Button: TUDBtnType);
var
  I: Integer;
  ChildControl: TControl;
begin
for I:= 0 to GroupBox1.ControlCount -1 do  
begin  
ChildControl := GroupBox1.Controls[I];  
ChildControl.Top := ChildControl.Top + 15  
end;  
end;


Проверить тип контрола надо оператором is:

if edit1 is TEdit then....

Затем доступ ко всем свойствам путем приведения класса:

(edit1 as TEdit).text:=''; 


Автор ответа: Kiber_rat
Взято с Vingrad.ru




Обратные вызовы BDE32 для получения статуса операций


Обратные вызовы BDE32 для получения статуса операций




Дополнительная документация, описывающая вызовы функций BDE, находится в файле BDE32.HLP (расположенном в каталоге, где установлен 32-битный IDAPI).

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

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

Чтобы это сделать, необходимо сперва вызвать функцию DbiGetCallBack(), возвращающую дескриптор обратного вызова, который мог быть уже установлен (с этими параметрами), и сохранить информацию в структуре данных. Затем установить свой обратный вызов, заменяя им любой установленный до этого.

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

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

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

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

unitTestbc1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, Grids, DBGrids, DB, DBTables, ComCtrls;

type
  TForm1 = class(TForm)
    Table1: TTable;
    BatchMove1: TBatchMove;
    Table2: TTable;
    Button1: TButton;
    ProgressBar1: TProgressBar;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses Bde; {Здесь расположены Dbi Types и Procs}

{$R *.DFM}

{тип структуры данных для сохранения информации о предыдущем обратном вызове}
type
  TDbiCbInfo = record
    ecbType: CBType;
    iClientData: longint;
    DataBuffLn: word;
    DataBuff: pCBPROGRESSDesc;
    DbiCbFn: pointer;
  end;
type
  PDbiCbInfo = ^TDbiCbInfo;

  {Наша функция обратного вызова}

function DbiCbFn(ecbType: CBType;
  iClientData: Longint;
  CbInfo: pointer): CBRType stdcall;
var
  s: string;
begin
  {Проверяем, является ли тип обратного вызова тем, который мы ожидаем}
  if ecbType = cbGENPROGRESS then
  begin
    {если iPercentDone меньше нуля, извлекаем число}
    {обработанных записей из параметра szMsg}
    if pCBPROGRESSDesc(cbInfo).iPercentDone < 0 then
    begin
      s := pCBPROGRESSDesc(cbInfo).szMsg;
      Delete(s, 1, Pos(': ', s) + 1);
      {Вычислям процент выполненного и изменяем линейку прогресса}
      Form1.ProgressBar1.Position :=
        Round((StrToInt(s) / Form1.Table1.RecordCount) * 100);
    end
    else
    begin
      {Устанавливаем линейку прогресса}
      Form1.ProgressBar1.Position :=
        pCBPROGRESSDesc(cbInfo).iPercentDone;
    end;
  end;
  {существовал ли предыдущий зарегистрированный обратный вызов?}
  {если так - осуществляем вызов и возвращаемся}
  if PDbiCbInfo(iClientData)^.DbiCbFn <> nil then
    DbiCbFn :=
      pfDBICallBack(PDbiCbInfo(iClientData)^.DbiCbFn)
      (ecbType,
      PDbiCbInfo(iClientData)^.iClientData,
      cbInfo)
  else
    DbiCbFn := cbrCONTINUE;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  CbDataBuff: CBPROGRESSDesc; {Структура DBi}
  {структура данных должна хранить информацию о предыдущем обратном вызове}
  OldDbiCbInfo: TDbiCbInfo;
begin
  {Убедимся в том, что перемещаемая таблица открыта}
  Table1.Open;
  {Убедимся в том, что таблица-приемник закрыта}
  Table2.Close;
  {получаем информацию о любом установленном обратном вызове}
  DbiGetCallBack(Table2.Handle,
    cbGENPROGRESS,
    @OldDbiCbInfo.iClientData,
    @OldDbiCbInfo.DataBuffLn,
    @OldDbiCbInfo.DataBuff,
    pfDBICallBack(OldDbiCbInfo.DbiCbFn));
  {регистрируем наш обратный вызов}
  DbiRegisterCallBack(Table2.Handle,
    cbGENPROGRESS,
    longint(@OldDbiCbInfo),
    SizeOf(cbDataBuff),
    @cbDataBuff,
    @DbiCbFn);

  Form1.ProgressBar1.Position := 0;
  BatchMove1.Execute;

  {если предыдущий обратный вызов существовал - вновь устанавливаем его,}
  {в противном случае "отрегистрируем" наш обратный вызов}
  if OldDbiCbInfo.DbiCbFn <> nil then
    DbiRegisterCallBack(Table2.Handle,
      cbGENPROGRESS,
      OldDbiCbInfo.iClientData,
      OldDbiCbInfo.DataBuffLn,
      OldDbiCbInfo.DataBuff,
      OldDbiCbInfo.DbiCbFn)
  else
    DbiRegisterCallBack(Table2.Handle,
      cbGENPROGRESS,
      longint(@OldDbiCbInfo),
      SizeOf(cbDataBuff),
      @cbDataBuff,
      nil);

  {Показываем наш успех!}
  Table2.Open;

end;

end.


Взято с






Общие сведения о COM (статья)


Общие сведения о COM (статья)



Эта статья целиком и полностью написана

Fantasist'ом

, за что ему огромное спасибо.


Хотел я в общих словах расказать основную идею СOM. Когда я понял, что это такое показалась такая простая вещь, что можно коротко рассказать. Но не получилось...
Что ж, будет немного подлиннее.

Взято с Vingrad.ru



Общие вопросы о COM/DCOM/MIDAS технологии


Общие вопросы о COM/DCOM/MIDAS технологии



Cодержание раздела:

















См. также статьи в других разделах:




Общие вопросы о CORBA технологии


Общие вопросы о CORBA технологии



Cодержание раздела:






Общие вопросы работы в сети, домены, workgroups, workstations


Общие вопросы работы в сети, домены, workgroups, workstations



Cодержание раздела:




















Обзор BDE API


Обзор BDE API



Cодержание раздела:

















Обзор компонентов InternetExpress


Обзор компонентов InternetExpress




Аннотация

Среди новых возможностей Delphi5 одной из наиболее заметных является технология InternetExpress - средство публикации и обработки данных в Internet на основе технологии MIDAS. InternetExpress представляет собой набор компонентов, позволяющих реализовать полный цикл клиент-серверной обработки данных на базе Internet с использованием как уже имевшихся в распоряжении разработчиков на Delphi средств (создание Internet-приложений на основе ISAPI/NSAPI, ASP и CGI), так и новых средств, например стандарта XML (eXtended Markup Language - дальнейшего развития стандарта HTML, позволяющего реализовать объектный подход к созданию Internet-контента и структурированную передачу и обработку данных через Internet).

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

Краткое описание технологии InternetExpress

До недавнего времени решения на основе MIDAS можно было задействовать в Internet, создав MIDAS-клиент на Java и используя в качестве транспорта данных протокол IIOP. В Delphi 5 появилась возможность для передачи данных пакеты XML (XML data packets), что автоматически сделало пакеты данных MIDAS частью открытого стандарта

В InternetExpress используются средства поддержки XML из MIDAS 3. Поскольку в настоящее время даже лидеры среди Internet-броузеров (Netscape Navigator и Microsoft Internet Explorer) не поддерживают (или поддерживают частично) представление данных по стандарту XML, в InternetExpress реализована специальная технология поддержки XML на основе JavaScript и DHTML, позволяющая использовать InternetExpress даже с теми броузерами, которые вообще не имеют поддержки XML. В частности, для экспериментов с InternetExpress были использованы Netscape Navigator 4.61 и Internet Explorer 4.01, для которых поддержка XML не была реализована. В IE 5.0 уже встроена поддержка XML, поэтому большинство решений на основе этого стандарта требуют для работы наличия этого броузера, что, конечно, далеко не всегда желательно и (или) возможно.

Кстати, если приложение InternetExpress работает с IE 5, то порождаемый XML-пакет будет специальным образом оптимизироваться.

В броузерах, не имеющих встроенной поддержки XML, пакеты данных в этом формате разбираются с использованием специального модуля JavaScript (xmldom.js), который реализует спецификацию DOM (Document Object Model), позволяющую создавать HTML-клиенты для обработки данных на базе обычных web-серверов. Серверные приложения разрабатываются с использованием таких спецификаций взаимодействия с web-сервером, как ISAPI/NSAPI, CGI и ASP, которые позволяют канализировать запросы к данным и сами данные между клиентами и сервером приложений MIDAS

Компоненты InternetExpress

С точки зрения VCL (Visual Components Library, библиотеки визуальных компонент) InternetExpress представляет собой две компоненты базового набора: TXMLBroker и TMIDASPageProducer. TXMLBroker отвечает собственно за формирование XML-пакета, реакцию на изменения в данных и оповещение о действиях, выполняемых клиентом. TMIDASPageProducer отвечает за формирование сборного DHTML-документа, который, собственно, и является клиентским приложением, поскольку содержит все те визуальные элементы, которые соответствуют структуре пакета данных XML. В этот документ передаются XML-пакеты, формируемые компонентом XMLBroker. В тот момент, когда от клиентского приложения приходит сообщение о необходимости передать изменения в данных на сервер приложений, TMIDASPageProducer осуществляет опрос каждого из элементов управления HTML, формирует пакет с данными, подлежащими обновлению и передает их серверу приложений. Таким образом, обработка данных на клиенте происходит с использованием средств HTML, а передача структурированных данных к клиенту и изменений от него - при помощи пакетов данных XML.

Эти компоненты помещаются в web-модуль (WebModule) серверного приложения, для создания которого может быть использован специальный мастер (File->New->Web Server Application).

WebModule является наследником TDataModule и обладает некоторыми дополнительными возможностями по сравнению с базовым классом, которые позволяют обмениваться данными с Web-клиентами. В дополнение к базовому набору InternetExpress имеется несколько компонентов, например TReconcilePageProducer, которые устанавливаются из дополнительных пакетов (packages), входящих в поставку Delphi, и, конечно, существует возможность наследования базовых классов и создания на их основе собственных компонентов с расширенными возможностями.

Помимо визуальных компонентов InternetExpress состоит из ряда классов и интерфейсов, которые будут рассмотрены в разделе "Обзор компонентов InternetExpress".

Пакеты данных XML

Пакеты данных XML используются InternetExpress как транспортные контейнеры для передачи данных от серверной части приложения к клиентской и обратно. В качестве примера можно привести пакет данных, сформированный демонстрационным приложением, описываемым далее в этом обзоре:


<DATAPACKETVersion="2.0">
<METADATA>
<FIELDS>
<FIELD fieldname="Species No" attrname="Species_No" fieldtype="r8"/>
<FIELD attrname="Category" fieldtype="string" WIDTH="15"/>
<FIELD attrname="Common_Name" fieldtype="string" WIDTH="30"/>
<FIELD fieldname="Species Name" attrname="Species_Name" fieldtype="string"
WIDTH="40"/>
<FIELD fieldname="Length (cm)" attrname="Length__cm_" fieldtype="r8"/>
<FIELD attrname="Length_In" fieldtype="r8"/>
<FIELD attrname="Notes" fieldtype="bin.hex" WIDTH="50" SUBTYPE="Text"/>
<FIELD attrname="Graphic" fieldtype="bin.hex" SUBTYPE="Graphics"/>
</FIELDS>
<PARAMS DEFAULT_ORDER="1" PRIMARY_KEY="1" LCID="1033"/>
</METADATA>
<ROWDATA>
<ROW Species_No="90020" Category="Triggerfish" Common_Name="Clown Triggerfish"
Species_Name="Ballistoides conspicillum" Length__cm_="50"
Length_In="19.68503937007874" Notes="Also known as the big spotted triggerfish.
Inhabits outer reef areas and feeds upon crustaceans and mollusks by crushing 
them with powerful teeth. They are voracious eaters, and divers report seeing 
the clown triggerfish devour beds of pearl oysters.&#010;&#010;Do not eat 
this fish. According to an 1878 account, &quot;the poisonous flesh acts 
primarily upon the nervous tissue of the stomach, occasioning violent spasms 
of that organ, and shortly afterwards all the muscles of the body. The frame 
becomes rocked with spasms, the tongue thickened, the eye fixed, the breathing
laborious, and the patient expires in a paroxysm of extreme suffering.
&quot;&#010;&#010;Not edible.&#010;&#010;Range is Indo-Pacific 
and East Africa to Somoa.&#010;"/>
<ROW Species_No="90030" Category="Snapper" Common_Name="Red Emperor"
Species_Name="Lutjanus sebae" Length__cm_="60" Length_In="23.62204724409449"
Notes="Called seaperch in Australia. Inhabits the areas around lagoon coral 
reefs and sandy bottoms.&#010;&#010;The red emperor is a valuable food 
fish and considered a great sporting fish that fights with fury when hooked.
The flesh of an old fish is just as tender to eat as that of the very young.
&#010;&#010;Range is from the Indo-Pacific to East Africa.&#010;"/>
</ROWDATA>
</DATAPACKET>  


Структура пакета данных XML

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

В том случае, если пакет представляет собой разностный пакет данных XML (XML delta packet), в строку PARAMS метаданных добавляется фрагмент вида DATASET_DELTA="1", то есть строка PARAMS будет выглядеть следующим образом:


<METADATA>
<FIELDS>
...
</FIELDS>
<PARAMS DEFAULT_ORDER="1" PRIMARY_KEY="1" LCID="1033" DATASET_DELTA="1"/>
</METADATA>
<ROWDATA>
...
</ROWDATA>
</DATAPACKET>  


Структура разностного пакета данных XML

Серверная часть Internet-приложения на основе InternetExpress

Серверная часть такого приложения состоит из исполняемого модуля, написанного в данном случае на Delphi 5, и включающего WebModule, упомянутый выше, а также файлов-библиотек JavaScript, которые в случае отсутствия поддержки XML броузером передаются на сторону клиента, а именно:

Библиотека Назначение
xmldom.js XML-парзер, соответствующий стандарту DOM, написанный на JavaScript. Позволяет броузерам использовать пакеты данных XML не имея встроенной поддержки этого стандарта. Для IE5 этот файл не передается, а XML-пакет специальными образом оптимизируется.
xmldb.js Библиотека классов доступа к данным, обслуживающая пакеты данных XML.
xmldisp.js Библиотека описания связей между классами доступа к данным в xmldb.js и элементами HTML.
xmlerrdisp.js Библиотека классов для обработки конфликтных ситуаций при внесении изменений в данные. Использует пакет разности данных (XML delta packet) и пакет ошибок (XML error packet).
xmlshow.js Библиотека функций для отображения окон с данными XML.


Таблица 1. Библиотеки JavaScript, входящие в состав серверной части приложения InternetExpress

Примечание:

Для того, чтобы серверная часть приложения InternetExpress корректно обслуживала клиентов без поддержки XML, эти библиотеки (по умолчанию) должны быть размещены в том же каталоге, что и исполняемый модуль серверной части приложения, или же для каждого из компонентов типа TMIDASPageProducer и его наследников свойство IncludePathURL должно указывать место расположения этих файлов (в относительном или полном формате), например /iexpress/, как в демонстрационном примере к данной статье.

Для того, чтобы вышеперечисленные библиотеки были переданы клиентской стороне и задействованы там, достаточно просто включить соответствующие ссылки на них в HTML-документ:


<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript" SRC="xmldom.js"></SCRIPT>
<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript" SRC="xmldb.js"></SCRIPT>
<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript" SRC="xmldisp.js"></SCRIPT>
<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript" SRC="xmlshow.js"></SCRIPT>
<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript" SRC="xmlerrdisp.js"></SCRIPT>  


Примечание:

ссылка на xmldom.js требуется только в том случае, если броузер не имеет встроенной поддержки XML.

Клиентская часть приложения InternetExpress

Клиентская часть приложения на основе InternetExpress представляет собой собственно HTML-документ, порожденный одним или более компонентами типа (или его наследниками) TMIDASPageProducer, исполняемый (интерпретируемый) тем или иным броузером. Как уже было сказано выше, этот документ может содержать элементы отображения и управления, соответствующие структуре пакета данных XML. К ним также могут добавляться элементы управления, формирующие HTML-аналог DBnavigator из состава Delphi VCL в том случае, если соответствующие параметры были заданы при настройке PageProducer, а также другие элементы управления HTML, как связанные с обработкой данных, так и составляющие независимые части интерфейса, например группу для ввода имени пользователя и пароля с целью авторизации доступа к данным системы.

Схема работы приложения InternetExpress

В целом, схема работы приложения на основе InternetExpress выглядит следующим образом:

Броузер обращается по ссылке (URL) к серверному приложению InternetExpress, которое возвращает HTML-документ, являющийся, как правило, некой отправной точкой в алгоритме обработки данных.
По запросу пользователя серверное приложение сначала возвращает очередной HTML-документ, содержащий (при необходимости) ссылки на библиотеки JavaScript, отвечающие за обработку XML-пакетов, а затем уже этот документ посылает запрос серверной части приложения, которая затем посылает клиенту данные в виде пакетов XML, интерпретируемых соответствующими библиотеками JavaScrip.
После того, как пользователь просмотрел набор данных и, при необходимости, внес в них изменения, он имеет возможность передать изменения серверной части приложения. Это процесс запускается событием, которое, как правило, связано с "кнопкой" "Apply Updates" (Внести изменения) и передается серверной части приложения InternetExpress, а именно - компоненту TMIDASPageProducer. Все изменения в данных передаются серверной части приложения в виде разностных пакетов XML (XML delta packets).
Серверная часть получает информацию об изменениях в данных и использует сервер приложений для внесения этих изменений в базу данных. При возникновении конфликта (reconcile error) имеется возможность сформировать HTML-вариант Reconcile Dialog из состава Delphi или разрешить конфликтную ситуацию автоматически, включив компонент TReconcilePageProducer в состав серверной части приложения
Обзор компонентов InternetExpress

Компонент TMIDASPageProducer

Данный компонент отвечает за сборку HTML-документа, отображающего "живой" набор данных, получаемый от сервера приложений, или же "типового" HTML-документа, не осуществляющего обработку данных вообще. Компонент может быть использован для создания Web-приложений на основе MIDAS, которые будут отображать информацию, содержащуюся в базе данных, получая ее через сервер приложений, и передавать ее HTML-клиентам в пакетах данных XML. Реализация компонента находится в модуле (unit) dsprod

При создании Web-модуля ссылка на один из таких компонентов (свойство Producer) должна быть выставлена у соответствующих элементов TWebActionItem.

TMIDASPageProducer создает HTML-документ на основе шаблона. В отличие от других компонентов типа Producer, этот компонент имеет шаблон "по умолчанию" (default), в котором содержатся несколько описаний верхнего уровня, на основе которых в других компонентах порождаются HTML-документы. Помимо шаблонов содержание конечного документа может быть сгенерировано на основе данных, порождаемых другими компонентами, добавленными в Web-модуль, получено от другого компонента TMIDASPageProducer через свойство TMIDASPageProducer.Content и так далее

Примечание:

Связывание HTML-элементов с пакетами данных XML и обработчиками событий HTTP в TMIDASPageProducer осуществляется исключительно по именам HTML-объектов и соответствующих событий, за счет чего становится возможным редактировать сгенерированный HTML-шаблон любым из средств работы с HTML-документами, придавая ему необходимый внешний вид и дополняя логику обработки данных вставками JavaScript, поскольку даже при изменении свойств объектов, порожденных встроенным редактором TMIDASPageProducer, внесенные другими средствами изменения потеряны не будут. В демонстрационном приложении, описываемом ниже в шаблон HTML-документа был изменен с целью добавить заголовок страницы с данными.

Расширение функциональности обработчика шаблонов (свойство TMIDASPageProducer.HTMLdoc) возможно за счет реализации обработчика события TMIDASPageProducer.OnHTMLtag или перекрытия метода TMIDASPageProducer.DoTagEvent. Реализовав свою собственную версию обработчика этого события вы получаете возможность использовать в теле шаблона документа собственные тэги, заменяя их на этапе генерации HTML-документа соответствующими значениями. Пример такого подхода показан в демонстрационном приложении InetXCenter из состава Delphi 5 (модуль InetXCenterProd.pas).

И конечно, возможности InternetExpress можно практически неограниченно расширять, реализуя специальные компоненты-наследники от TMIDASPageProducer и компонентов, используемых для формирования содержимого документа (TDataForm, TQueryForm и так далее). Создавая на их основе специализированные компоненты, вы получаете возможность максимально упростить создание конечного решения на основе InternetExpress за счет реализации специфических возможностей, необходимых вашему Internet-приложению. Например, в демонстрационном приложении InetXCenter за счет создания наследника от компонента TMIDASPageProducer реализованы такие возможности, как генерация таких полей заголовка HTML-документа, как , задание комментариев и описаний, автоматически подставляемых в конечный HTML-документ и другие расширения базового компонента.

Описание компонента TMIDASPageProducer

Поскольку TMIDASPageProducer (TCustomMIDASPageProducer) является собственно генератором содержания HTML-документа, в его описание входит интерфейс IWebContent, который, собственно, это содержание и предоставляет. Заголовок соответствующего класса выглядит следующим образом:



TCustomMIDASPageProducer = class(TPageItemProducer, IWebContent,
IWebComponentEditor, IScriptEditor)




Помимо IWebContent в описании класса участвуют еще два интерфейса: IWebComponentEditor и IScriptEditor, которые, соответственно, являются средствами связи с design-time редактором для компонентов типа TWebComponent и HTML-кода.

Свойство Назначение
HTMLDoc Собственно базовый шаблон, содержащий включения (includes) описателей содержания
HTMLFile Аналогично HTMLDoc, но с привязкой к файлу
IncludePathURL Путь к библиотекам JavaScript (в формате URL). Может быть как полный (http://someserver/iexpress/), т.е. с указанием имени сервера и так далее, так и относительный (/iexpress/).
Styles Описание стилей "по умолчанию" для генерации HTML-документа. Это свойство является аналогом файла стилей, используемого для создания канонических Web-страниц
StylesFile Аналогично Styles, но с привязкой к файлу стилей
WebPageItems Список специальных компонентов, определяющих ключевые элементы документа. Основные типы PageItem включают: DataForm, QueryForm и LayoutGroup. Каждый из базовых компонентов TWebPageItem может иметь вложенные компоненты. Например, для DataForm могут иметься DataGrid, DataNavigator и так далее


Ключевые свойства компонента TMIDASPageProducer

Комбинация компонентов TDataForm, TQueryForm и так далее, определяет структуру и основные параметры отображения HTML-документа, стили же (цвета, шрифты и так далее) определяются свойствами Styles и HTMLDoc.

Другие свойства компонента подробно описаны в документации и файле Справки Delphi.

Следует отметить, что за счет использования для определения состава элементов HTML-документа стандартных компонентов, поставляемых в исходных текстах, становится возможным практически неограниченное расширение функциональных возможностей InternetExpress путем создания специализированных наборов компонент для построения Internet-приложений. Примеры подобного подхода можно найти в демонстрационном приложении InetXCenter из поставки Delphi 5.

Компонент TWebActionItem

TWebActionItem представляет собой невизуальный компонент, позволяющий задавать реакцию Internet-приложения на те или иные события, транслируемые протоколом HTTP от web-клиента. Предоставляя специальные свойства для задания ссылок на компоненты TMIDASPageProducer и TPageProducer, а также пути URL, TWebActionItem дает возможность задавать алгоритм перемещения между HTML-документами, составляющими Internet-приложение, реагировать на передачу параметров и значений полей HTML-документа специфическим образом и так далее. Реализуя обработчик события TWebActionItem.OnAction, программист получает возможность возвращать необходимые данные в полях запросов, устанавливать идентификационные маркеры (Cookies) для Web-клиентов, контролировать генерацию содержания HTML-документов и выполнять ряд других операций практически на самом нижнем уровне функционирования Internet-приложения

Свойство Назначение
Default Означает использование этого компонента как обработчика событий соответствующих типов (свойство MethodType) в тех случаях, когда иной обработчик явно не задан.Из всех компонентов TWebActionItem, присутствующих в контейнере TWebModule, только один может иметь свойство Default равным True
DisplayName Служит для задания отображения компонента в списке компонента TCustomWebDispatcher. Должно быть уникальным для своего контекста.
Enabled Аналогично многим другим компонентам означает разрешение или запрет на выполнение связанных с компонентом действий. В случае установки в False содержимое HTML-документа соответствующим компонентом типа PageProducer генерироваться не будет
MethodType Определяет метод HTTP, при вызове которого со стороны web-клиента будет задействован данный компонент. По умолчанию имеет значение mtAny, то есть все доступные методы, но может принимать значения отдельных типов, например mtGet (запрос на получение web-клиентом содержимого документа).
PathInfo В формате URI (Unified Resource Identifier) задает путь к получателю всех сообщений, принимаемых TWebActionItem. Позволяет перенаправить очередь сообщений другому компоненту PageProducer или HTML-документу.
Producer Ссылка на компонент PageProducer. В случае, если компонент явно не задан, обработчик OnAction должен быть реализован в обязательном порядке для осуществления реакции на сообщение. Если ссылка на PageProducer актуальна (не nil), сообщение обрабатывается или PageProducer, или реализацией OnAction в случае ее наличия


Ключевые свойства компонента TWebActionItem

Примеры использования свойств TWebActionItem можно найти в демонстрационном приложении InetXCenter (модуль InextXCenterModule.pas).

Невизуальные компоненты категории PageItems (элементы HTML-документа)

Компоненты PageItems предназначены для формирования структуры HTML-документа. Точно так же, как и компоненты VCL, они подразделяются на средства отображения типовых элементов HTML-документа и элементов для обработки данных, получаемых от сервера приложений. Для каждого из этих компонентов могут быть созданы наследники, расширяющие их свойства или реализующие те элементы HTML, эквивалента которым нет в текущей реализации InternetExpress. Реализация компонентов PageItems находится в модуле MidItems. При построении HTML-документа компоненты PageItems объединяются в иерархические структуры. Например, компонент TDataNavigator содержит компоненты типа TDataSetButton

На этапе генерации содержания HTML-документа компонентом TMIDASPageProducer эти компоненты генерируют фрагменты HTML-кода, описывающего эквивалентные HTML-элементы. Эти фрагменты собираются TMIDASPageProducer в единый поток и подставляются вместо соответствующих тэгов в шаблоне документа. К элементам HTML привязываются обработчики на JavaScript, которые составляют аналог обработчиков событий для визуальных компонентов Delphi, таких, как OnClick и тому подобных. Отдельные компоненты PageItems позволяют напрямую задать мишень (target) для передачи сообщений (свойство Action) в формате URI, что позволяет осуществлять переход от одного HTML-документа к другому передачу параметров в формате протокола HTTP между этими документами.

За счет использования в TMIDASPageProducer шаблонов для генерации HTML-документов появляется возможность добавлять отдельные визуальные и невизуальные элементы HTML-документа прямым редактированием. Однако используя обработчики событий HTTP можно связывать такие элементы с генерируемыми по шаблону через компоненты TWebActionItem или при помощи создаваемых опять-таки прямым редактированием обработчиков на JavaScript внутри HTML-документа.

Компонент TXMLBroker

Этот компонент осуществляет передачу пакетов данных в формате XML от сервера приложений HTML-клиенту, получение изменений в данных от HTML-клиента, расшифровку разностных пакетов данных XML и передачу информации об изменениях в данных на сервер приложений. Реализация компонента находится в модуле xmlbrokr.

Компонент TXMLBroker может быть использован в приложении, которое одновременно является и MIDAS-клиентом, и серверным Web-приложением. Серверы такого класса как правило имеют две основные функции:

получать пакеты XML-данных от сервера приложений через интерфейс IAppServer.
oбрабатывать сообщения HTTP от броузеров, содержащие пакеты XML-данных с изменениями относительно исходного набора и передавать их серверу приложений.
Для того, чтобы сделать информацию, содержащуюся в базе данных, доступной в формате XML, достаточно добавить компонент TXMLBroker в контейнер WebModule совместно с компонентом TMIDASPageProducer, который будет использовать XML-пакеты данных для создания HTML-страниц.

TXMLBroker автоматически регистрирует себя в Web-модуле (или Web-диспетчере) как автодиспетчеризуемый объект (auto-dispatching object). Это означает, что Web-модуль или Web-диспетчер будут перенаправлять все входящие HTTP-сообщения непосредственно этому. Все входящие сообщения считаются данными для обновления, порождаемыми броузером в ответ на получение HTML-потока, порождаемого компонентом TApplyUpdatesButton. TXMLBroker автоматически передает пакет с XML-данным, содержащий различия в данных, на сервер приложений и возвращает все ошибки, возникшие при обновлении данных тому компоненту управления содержимым (TMIDASPageProducer) документа, который имеет возможность генерации соответствующего ответного сообщения

Свойство
Назначение

AppServer
Интерфейс IAppServer, служащий для связи с провайдерами (providers) данных

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

MaxRecords
Управляет формированием пакетов данных XML.
Значение -1 позволяет компоненту передать все записи из набора данных в XML-пакет;
Значение 0 позволяет передачу только метаданных;
Значение больше нуля собственно определяет число записей (строк), которые могут быть переданы в XML-пакет.

Params
Список параметров, передаваемых серверу приложений. Используется, в частности, для передачи параметров хранимых процедур и SQL-запросов.

ProviderName
Имя провайдера данных

ReconcileProducer
Ссылка на компонент TReconcilePageProducer, который будет использоваться при разрешении конфликтов данных во время операций обновления.

WebDispatch
Перечисляет типы сообщений протокола HTTP, на которые будет реагировать компонент. Как правило, эти сообщения порождаются при нажатии кнопки типа TApplyUpdatesButton на HTML-странице.





Ключевые свойства компонента TXMLBroker

Построение Web-приложения на основе InternetExpress

Для создания Web-приложения необходимо наличие скомпилированного и зарегистрированного приложения-сервера данных. В данном примере используются данные из таблицы biolife.db, входящей в состав демонстрационной базы данных из комплекта Delphi 5. Данные публикуются через контейнер Remote Data Module.

Remote Data Module демонстрационного сервера данных

После создания и регистрации сервера данных необходимо создать клиента для этого сервера, который, в свою очередь, будет являться сервером для HTML-клиента, являющегося третьим звеном в нашей системе. Delphi 5 предоставляет специальный мастер для создания приложений-расширений Web-сервера. Он может быть вызван через меню File->New->Web Server Application и имеет следующий вид:

Мастер создания серверных Web-приложений.

В данном случае мы создаем CGI-приложение, выводящее порождаемый поток данных в устройство стандартного вывода (stdout). Поток данных этого приложения будет без изменений передан вызывающему документу через транспортный протокол.

Мастер автоматически создаст контейнер типа TWebModule, в который необходимо поместить компоненты TMIDASPageProducer и TXMLBroker. Сюда же мы поместим и компонент TDCOMConnection, который будем использовать для подключения с удаленным сервером данных, а также компонент TClientDataSet для доступа к удаленному модулю данных.

Контейнер WebModule с размещенными в нем компонентами TXMLBroker и TMIDASPageProducer.

Определив необходимые для соединения с удаленным сервером свойства, переходим к созданию содержимого HTML-документа. Для этого необходимо назначить для TXMLBroker свойства RemoteServer и ProviderName, а также создать хотя бы один компонент TWebActionItem, вызвав соответствующий редактор по правой кнопке мыши на компоненте TXMLBroker и TMIDASPageProducer.

Всплывающее меню и ActionEditor

Далее необходимо вызвать редактор Web-страниц, для чего необходимо выбрать пункт всплывающего меню компонента TMIDASPageProducer->Web Page Editor.

Примечание:

для работы этого элемента необходимо наличие установленного Microsoft Internet Explorer 4.0 и выше.

После добавления необходимых элементов мы получаем готовое к применению приложение Web-сервера. При установке параметров отображения HTML-документа можно воспользоваться свойствами компонента DataGrid и других элементов HTML-документа для придания ему необходимого внешнего вида, а также вручную доработать HTML-код в соответствующем встроенном редакторе.

Встроенное средство просмотра HTML. Красным выделены автоматически сгенерированные ссылки на библиотеки JavaScript.

После компиляции исполняемый модуль (в нашем примере - XMLServerApp.exe) необходимо поместить в каталог Web-сервера, для которого выделены права на запуск приложений. В этот же каталог необходимо поместить перечисленные в разделе "Серверная часть Internet-приложения на основе InternetExpress" библиотеки JavaScript. Для проверки правильности размещения библиотек можно воспользоваться специальным HTML-файлом scripttest.html, который находится в каталоге Demos\Midas\InternetExpress\TroubleShoot на компакт-диске Delphi 5 или в каталоге установки на жестком диске рабочей станции. Этот HTML-файл проверяет правильность размещения библиотек и настройки Web-сервера и в случае наличия тех или иных ошибок выдает некоторые рекомендации по разрешению проблем.

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

Окно броузера Netscape Navigator со страницей, порожденной демонстрационным приложением InternetExpress.

Несколько советов

Демонстрационное приложение для данного обзора тестировалось под управлением серверов Apache 1.3.6 для платформы Win32 и Microsoft Personal Web Server. Сервер Apache после установки был сконфигурирован следующим образом:

В корневом каталоге документов был создан подкаталог iexpress (F:\inetpub\iexpress), в который были помещены файлы, необходимые для работы приложения InternetExpress (библиотеки JavaScript, файл err.html).
В файл mime.types (перечень mime-типов) была внесены исправления, определяющие библиотеки JavaScript для передачи на клиентское место, а для исполнения на сервере вводящие новое расширение:
text/javascript js
application/x-javascript jss
Свойству компонента TMIDASPageProducer.IncludePathURL было присвоено значение "/iexpress/" с целью указать местонахождение библиотек JavaScript, поскольку по умолчанию TMIDASPageProducer по умолчанию предполагает размещение этих файлов в том же каталоге, что и исполняемый (.exe) модуль, однако в случае использования Apache содержимое каталога /cgi-bin/ по умолчанию считается исполняемым на стороне сервера и требуется дополнительная настройка с целью определить модули с расширением .js и .html как неисполняемые.

Настройка MS Personal Web Server заключалась в определении дополнительного каталога для серверных приложений (того же что и для Apache) и задания для него соответствующих прав доступа (Read и Execute).

Как показало тестирование, при отладке приложений InternetExpress удобнее пользоваться броузером Netscape Navigator (использовались версии 4.61 и 4.7), поскольку в случае возникновения ошибок он дает более полную диагностическую информацию нежели MSIE, старающийся "защитить" пользователя от различных "загадочных" сообщений. К тому же Netscape корректно обрабатывает обращения к localhost в отсутствие соединения с Internet.

Для отладки приложений InternetExpress также удобнее создавать их в CGI-варианте, поскольку IIS, Personal Web Server и Apache не блокируют по записи CGI-приложения в отличие от ISAPI/ASP, которые захватываются кэш-системой этих серверов и для освобождения (например для перезаписи новой версией) exe-файла требуется остановка и повторный запуск web-сервера. В то же время преобразование CGI-приложение в ISAPI или ASP выполняется простой заменой включения (uses) модуля CGIApp на ISAPIApp в исходном тексте проекта.

Приводимое здесь демонстрационное приложение отнюдь не претендует на полноту и законченность. Для более полного ознакомления с возможностями InternetExpress рекомендуется обратиться к демонстрационным примерам из поставки Delphi 5 Enterprise, находящиеся в каталоге Runimage\Delphi50\Demos\Midas\InternetExpress на компакт-диске или в D:\Delphi5\Demos\Midas\InternetExpress (или ином, в зависимости от пути установки Delphi) на жестком диске. Внимательно прочитайте сопроводительные файлы к этим примерам, поскольку некоторые из них требуют специфических настроек Delphi и (или) Web-сервера.

Взято с





Обзор Kylix


Обзор Kylix




Недавно выпущенный фирмой Borland продукт - Kylix (Delphi for Linux) вызвал большой интерес в среде разработчиков. На предрелизном этапе разработки мнения о Kylix порой были диаметрально противоположны, от экзальтированно-восторженных до агрессивно-негативных.
Автор попытался в данной статье изложить свое мнение о данном продукте, основанное на недельном опыте работы его эксплуатации. Претендовать на 100% объективность, поработав столь недолгое время, трудно, но надеюсь, что мои мысли будут интересны и полезны читателям.

Итак, то, что нам так долго обещала компания Borland, свершилось, Kylix выпущен. О начале разработки компания Borland (тогда еще Inprise) объявила приблизительно год назад (или даже ранее). Это вызвало огромную заинтересованность в мире разработчиков ПО, т.к на сегодня Linux является одной из наиболее быстро растущих операционных систем. В последнее время рост Linux намечается не только как ОС для серверов, но и как операционной системы настольных ПК. (в подтверждение этого, я пишу эти строки в Linux) Развитие Linux, как ОС для настольных систем вызвало необходимость создания средств разработки приложений с графическим интерфейсом. Конечно, разработчики графических систем GNOME и KDE предлагают свои средства разработки, такие как GLADE и KDeveloper (которые к тому же бесплатны), но ни одно из этих средств разработки не может полностью удовлетворить возрастающие требования разработчиков. Таким образом, незаполненная ниша средств разработки приложений под Linux и огромная популярность Delphi дает Borland отличные шансы закрепиться на данном рынке.

Установка
Для тестирования была выбрана версия Kylix Server Developer Version 1.0 Build 5.62. Комплект поставки включал 2 CD. Один собственно с Kylix, а второй содержит дополнительные инструменты и компоненты. Откуда все взялось ? Вопрос риторический (ларек за углом). Сразу хочу принести искренние извинения компании Borland, ну нет меня нужной суммы (~2000$) чтобы оценить качество продукта (спонсоры где вы :-) ). Ну хватит о грустном .
В качестве ПК использовался P-III 500 , RAM 64 Mb, HD 10 Gb. Опрерационные системы - Red Hat Linux 7.0, Red Hat Linux 6.2. В качестве графической оболочки использовалась KDE KDE 2, GNOME. Установка проводилась как под аккаунтом root, так и под простым пользователем. Сразу отмечу, что инсталляция проходила гладко и без проблем (если не считать установку патчей glibc, что было запрошено программой инсталляции и также не вызвало затруднений). Графическая оболочка сделана удобно и интуитивно понятна.
После установки запуск графической оболочки также проходил без проблем. Не скажу, что скорость работы IDE меня поразила, но была вполне приемлема и не вызывала неприятных ощущений от заторможенности.
Резюме: инсталляция и запуск IDE не должны вызывать проблем.


IDE и Help
IDE Kylix практически дублирует IDE Delphi 5, чего, вообще говоря, и следовало ожидать. Первое впечатление, что ты и не покидал Delphi. Однако, по прошествии некоторого времени, обнаруживаешь, что отсутствует меню Database (где любимый SQL Explorer?). Средства создания приложения с поддержкой нескольких языков также не обнаружились . Наблюдаются различия и в палитре компонентов: отсутствует закладка Win32 (ну еще бы), на закладку Additional перенесены таймер и PaintBox (закладка System, в которой они находятся в Delphi упразднена, по всей видимости за ненадобностью), введены новые компоненты LCDNumber (ЖК индикатор), TextBrowser и TextViewer.

Чего действительно не будет хватать, так элементов для ввода даты, времени. Поработав еще немного, обнаруживаешь и приятные нововведения. Так, например, TButton обзавелся свойством Color, ТMainMenu имеет свойство Bitmap, позволяющее накладывать под меню картинку.

Object Pascal тоже не огорчил меня, все тот же простой и удобный язык программирования.
Поддержка "великого и могучего" определяется в первую очередь настройкой самой ОС.
Система встроенной подсказки аналогична привычной системе в Windows. В Kylix программа вывода подсказки называется HyperHelp и, судя по About Box, выпущена Bristol Technology Inc.Файлы подсказки имеют расширение hlp, но не открываются в Windows :(.

формате pdf (Acrobat Reader). Один из них - Developer Guide использовался мной очень часто и продуктивно. Документация достаточно полна, детализирована и удобна для изучения.
Обнаружение в каталоге установки папки wineserver подтвердило слухи о том, что в своей работе IDE использует эмуляцию Windows. Какие конкретно функции используются, выяснить не удалось, но наличие данного факта радости мне не прибавило.
Резюме: IDE практически аналогичен Delphi 5. Переход от IDE Delphi к Kylix проходит безболезненно.

Функциональные возможности Kylix



Компоненты создания пользовательского интерфейса
Естественно, что так как сама Borland позиционирует Kylix как Delphi под Linux я часто буду сравнить эти два средства разработки.
Начнем с набора визуальных компонентов в палитре. Большинство визуальных компонентов для создания пользовательского интерфейса присутствуют в обеих средах разработки. Отметим, что в Kylix стандартные компоненты получили свое дальнейшее развитие, т.е были добавлены новые свойства и методы, расширяющие функциональность компонентов и делающие их использование более гибким. В то же время в компонентах Kylix не были замечены какие-либо средства для поддержки восточных языков (Bidi Modes), так же отсутствуют свойства, связанные с плавающими окнами (docking).
Каких-либо кардинальных нововведений в процессе визуального программирования в Kylix мной замечено не было, да и, наверное они не нужны, т.к Delphi предоставляет одну из лучших (если не лучшую) среду визуальной разработки пользовательского интерфейса. Минут за 20-30 без всяких проблем мне удалось написать тестовое приложение типа редактора Notepad.
Резюме: Таким образом, можно сказать, что Kylix является одним из самых удобных средств быстрой разработки графических интерфейсов под Linux.



Работа с базами данных

Еще одним критически важным для разработчика сегментом является создание приложений работы с БД. Данную задачу можно разделить на три части:

1. Отображение данных пользователю.
2. Механизм организации передачи данных приложение - БД.
3. Генерация отчетов и диаграмм.

Первая задача решается компонентами с закладки DataControls. Почему-то на данной закладке не было компонентов DBRichEdit, DBCtrlGrid, DBChart. Особенно хочется выразить сожаление по поводу отсутствия последнего из перечисленных компонентов, т.к на сегодня практически невозможно писать приложения без деловой графики. Остальные компоненты были аналогичны Delphi, что позволяет без проблем отображать данные пользователю.
Механизм организации передачи данных приложение - БД. Реализация данной задачи фирмой Borland вызвала у меня самые неоднозначные реакции.
Во первых наблюдаются некоторые зачатки (а может и атавизмы) технологии MIDAS. Они представлены компонентами Provider и ClientDataSet.
Во вторых, как и анонсировалось ранее, в Kylix в качестве движка работы с серверами БД используется новая технология dbExperss (которую обещают сделать много платформенной). Компоненты размещены на закладке с соответствующим именем. Набор компонентов наиболее близок к закладке ADO в Delphi 5 и включает:
SQLConnection - соединение с БД. Данный компонент по функции аналогичен ADOConnetion, т.е позволяет настроить параметры связи с сервером БД

SQLQuery, SQLTable, SQLDataSet, SQLStoredProc - компоненты работы с объектами БД. В документации эти компоненты названы unidirectional datasets (о значении этого термина позднее)

SQLMonitor - компонент регистрации событий работы с БД

SQLClientDataSet - компонент доступа к данным (client dataset).

Решив разработать небольшое приложение для просмотра и редактирования БД, я разместил на форме SQLConnection и попытался связаться с сервером MySQL (ver 10.8 Distrib 3.23.22-beta, входит в поставку Red Hat Linux 7.0). Здесь я наступил на первые грабли в виде сообщения Unable to load libmysql.so.6.0.0. После недолгих поисков обнаружилась /usr/lib/myslq/libmysql.so.9.0.0, которая и была прописана в свойстве VendorLib, но и это не помогло мне, т.к на все последующие попытки ввода login/password я с неизменным успехом видел Invalid User/Password. Обозлившись на это дело и перепробовав все возможные комбинации host/database name/login/password, с соседней машины Windows подключился к базе без всяких проблем (через ODBC), из командной строки подключение к MySQL проблем тоже не вызвало. Через несколько дней на сайте Borland обнаружилось подтверждение, того что я не одинок. Как было указано, с версией MySQL 3.23 dbExpress не работает. К Interbase 6 на локальной машине подключение не вызвало проблем. Протестировать подключение к Oracle и DB2 не представилось возможным за отсутствием оных. Драйверов для других типов баз данных в поставке не обнаружилось.
Далее по старой памяти кинув на форму DataSource и SQLTable, настроив их нужные свойства (по аналогии с Delphi) и активизировав SQLTable (Active:=true), я было попытался установить свойство DataSource в сетке данных (DBGrid), и тут меня ждали вторые грабли - Operation not allowed on unidirectional dataset. После тщетных попыток найти ошибку в своих действиях, когда уже ничего не помогало, я прибегнул к самому отвратительному :) - чтению документации.
Как оказалось, все компоненты типа DataSet делятся на два типа

Unidirectional

Client dataset

Unidirectional - данный вид набора данных предназначен для извлечения данных через SQL запросы. Он позволяет двигаться по данным только вперед (согласно сортировке заданной order by) и не кэширует изменения, что делает его более быстрым и менее требовательным к ресурсам. Однако, он не позволяет пользователю редактировать данные, накладывать фильтры и Lookup. По вышеуказанным причинам я и не смог подключить unidirectional dataset для отображения в DBGrid.
Второй тип наборов данных - Client dataset. Данный тип наборов данных сохраняет записи из БД в буфер, позволяя перемещаться по записям в любом направлении. Именно данный тип набора данных может быть отображен в DBGrid и других элементах пользовательского интерфейса.
На основе всего вышесказанного, в документации предложено несколько архитектур взаимодействия с БД.

1. Работа на локальной машине с набором данных формируемым TClientDataSet. В Delphi 5 данная модель взаимодействия называлась briefcase. В Kylix (по крайней мере сегодня) нет возможности передавать изменения на сервер, т.е они остаются в файлах *.cds (или xml). В данном случае файлы *.cds представляют собой аналог локальных БД. Загружать/выгружать содержимое на диск можно соответственно функциями LoadFromFile/SaveToFile. Что неудобно в данном методе - определять структуру таблиц.
2. Работа с серверами БД через unidirectional dataset
Данная архитектура позволяет просматривать только одну запись из базы в каждый момент времени. Редактирование недоступно, двигаться по набору записей можно только вперед. Данный вид архитектуры полезен для статистического анализа записей БД, но на его основе практически нельзя построить приложение для ввода и редактирования данных пользователями.
3. Комбинация client и unidirectional dataset. Unidirectional dataset используется для извлечения данных с сервера БД, client dataset для буферизации, отображения, редактирования данных. Здорово завернуто, правда, особенно после Delphi, где до такого еще не додумались. Практически здесь выделены все компоненты трехзвенной архитектуры, но сервер приложений находится в одном приложении с презентационным слоем.
4. Понятное дело, что для создания классического двухуровневого клиент-сервера вышеописанная архитектура (3) слишком сложна и не дает никаких преимуществ. Посему для облегчения нам жизни существует еще одна архитектура доступа к серверам БД.
В качестве SQL client dataset используется компонент SQLClientDataSet, один за всех (Table, Query, StoredProc). Он может отображать данные в элементах управления, вставлять и редактировать их.
5. И наконец последнее, что предлагает Borland. Многоуровневая архитектура баз данных. Данная модель является логическим развитием модели №3
Но не спешите радоваться, т.к внизу как всегда есть приписочка, что компоненты типа Connection component придется приобретать отдельно, когда это станет возможно не сказано. Фактически, в той поставке, что тестировалась, создать трехзвенное приложение было невозможно. Причины этого на мой взгляд, в том что в Delphi было по большому счету две технологии взаимодействия приложений DCOM и CORBA. При тщательном рассмотрении оказывалось, что CORBA в Delphi являлась надстройкой над DCOM, а последний является на сегодня жестко привязанным к платформе Windows. Из данной ситуации есть два выхода: реализовывать DCOM для Linux (кстати частично это все же присутствует в Kylix), либо переходить на платформно незвависимую технологию CORBA. Оба эти пути достаточно сложны и имеют свои подводные камни, так что не будем судить Borland слишком строго.

Генерация отчетов и диаграмм. Решения данной задачи (т.е каких-либо генераторов отчетов) я не обнаружил вовсе. Что, мягко говоря, не очень понятно прикладным программистам.
Подведем итоги. Итак, базы данных и приложения работы с ними, по их устройству можно классифицировать на
1. Локальные БД (Dbase, Paradox). Решения для работы с локальными базами данных, даже формата dbf я не обнаружил, что для меня не понятно. Остается лишь вариант ClientDataSet+*.cds (архитектура 1), что не вызывает у меня большого восторга, т.к на сегодняшний день существует большое количество унаследованных систем хранящих данные именно в формате DBase или Paradox.
2. Двухуровневые БД (клиент - сервер). Приемлемо, достаточно удобно, хотя и непривычно.
3. Многоуровневые. Сейчас невозможно. Хотя в документации описание присутствует. Надеюсь, что в ближайшее время разработка многоуровневых БД станет доступной.
Резюме: работа с базами данных в Kylix еще далека от совершенства. Реально, на сегодня можно разрабатывать приложения клиент-сервер для работы с DB2, Oracle, MySQL( надеюсь, что в ближайшее время ошибки будут устранены) и Interbase.

Разработка приложений для Internet
Что действительно порадовало меня, так это включение в Kylix набора компонентов для работы с Internet - Indy. Данная библиотека содержит большое количество компонентов, обеспечивающих работу с многими сервисами интернета (сокеты TCP и UDP, DNS, FTP итд). Причем для многих протоколов реализованы не только клиенты, но и сервера. Включение Indy явилось безусловным плюсом в развитии Kylix (и Delphi, т.к Indy должен войти в Delphi 6). Большое количество примеров работы с данной библиотекой находится в архиве /demos/Internetdirect/IndyDemos.tar.gz.
Другой стороной работы с Internet является расширение возможностей Web серверов, помимо CGI сценариев, Kylix позволяет разрабатывать и модули популярного Web сервера Apache. Принцип построения модулей Apache аналогичен написанию CGI, т.е используется WebModule итд. По ставнению c Delphi технология разработки не претерпела изменений, поэтому останавливаться подробно на ней мы не будем. Для запуска примеров, по крайней мере под RedHat, пришлось перекомпилировать сервер Apache с включением поддержки DSO. Данная процедура подробно была описана в документации Kylix и не вызывала проблем. А вот инсталляция примера заставила меня немного потрудиться. Вызвано это было тем, что в документации и примере содержались ошибки, связанные с тем, что параметры конфигурации были указаны неверно, вернее был неправильно указан регистр букв( замечу, что для Apache в Unix имена параметров HelloWorld и Helloword две большие разницы). В конце концов тестовый пример был благополучно запущен, но впредь было бы хорошо, чтобы Borland более внимательно относилась к тестированию примеров.
Резюме: разработка приложений для взаимодействия с сервисами Internet удобна и достаточно развита. Создание CGI приложений было дополнено средствами создания модулей для популярного Web сервера Apache. Однако отсутствие поддержки регулярных выражений (а HTTP текстоориентированный протокол ), на мой взгляд, является заметным недостатком в данной области.

Объектная библиотека (CLX)
Библиотека CLX (Borland Component Library for Cross Platform - произносится как кликс) , на основе которой построена иерархия объектов в Kylix, является дальнейшим развитием объектной библиотеки Delphi - VCL. Главной целью разработки CLX стало стремление создать независимую от операционной системы библиотеку классов и компонентов. Наверное, с выходом Delphi 6 эта цель будет окончательно достигнута.
Все основные классы VCL обнаружились и в CLX, т.е родитель - это TObject итд. Миграция от VCL к CLX, как мне кажется, не вызовет серьезных проблем у большинства разработчиков.
Как и в Delphi кроме классов в иерархии присутствуют интерфейсы. Вот тут и кроется один интересный момент. В иерархии интерфейсов явно виднеется IUnknown, в интерфейсах есть GUID, wine для оболочки, чую Windows. Думаю, далее следует вопрос, а сможет ли когда-либо Kylix окончательно стать независимым от Windows COM или Borland будет последовательно переносить COM на Linux?

Конечно, COM является одним из лидеров технологий распределенных приложений, но ведь в последнее время все более заметной становится другая технология CORBA, объективно обладающая рядом достоинств, по сравнению с COM. CORBA активно и довольно успешно поддерживалась на платформе Windows, был разработана версия VisiBroker для Linux.
Если касаться внутреннего устройства CLX, то надо отметить, что она базируется на мультиплатформенной библиотеке Qt фирмы TrollTech http://www.troll.no/. Поскольку Qt написана на C++, необходимо было реализовать промежуточный слой, транслирующий модель компонентов Kylix в вызовы функций Qt (напомню, что в С++ не поддерживаются свойства). Данный в слой был назван CLXDisplay. Отношения между Qt и CLX по части визуальных компонентов сродни отношениям VCL и Win32 API (в части элементов управления). В CLX "общение" c Qt происходит по следующим правилам

Компоненты CLX имеют указатель(handle), связывающий их с объектом соответствующего widget (графический элемент управления - в терминологии Qt). Имя типа handle формируется как имя класса Qt+H. Имена классов в Qt начинаются с Q. Например, для Label имя класса Qt - Qt. Handle при этом будет типа QLabelH. Прямой доступ к методам и членам widget может быть осуществлен через Handle. Так конструктор создания Label на C++

QLabel(QWidget*parent=0, const char *nome=0, WFlags f=0)
Транслируется в Object Pascal
function QLabel_create(parent: QWidgetH; name: PAnsiChar; f: WFlags): QLabelH; overload; cdecl;

При использовании метода класса первым параметром является Handle. Так если, в Qt метод установки Caption
virtual void setText(const QString &)
в Object Pascal будет выглядеть как
TxtLbl := 'this is a test';
QLabel_setText(LabelHandle,@Txtlbl);

События (в терминологии Qt - signals) и обработчики события (в терминологии Qt - slots). В Qt связывание события с обработчиком выглядит так:
// Декларация 
bool connect (const QObject *sender, const char *signal, const char *member)
// Привязка
connect(Button,SIGNAL(clicked()),App,SLOT(quit())

В Object Pascal
function QObject_connect(sender: QObjectH; signal: PAnsiChar; Receiver: QObjectH; member: PAnsiChar): Boolean; overload;

Так например если связать сигнал нажатия кнопки со слотом quit приложения, то при нажатии на кнопку приложение будет закрываться. Таким образом осуществляется делегирование обработки событий. Однако данная техника не позволяет в Kylix (в отличии от С++) писать собственные обработчики событий (слоты). Так, в нашем примере мы не сможем выдать диалог подтверждения закрытия.


Для создания возможности написания собственных обработчиков событий применяются перехватчики (hooks). Для подключения собственного обработчика необходимо: создать hook (ButtonHook := QButton_hook_create(Button);), получить ссылку на обработчик (QButton_clicked_Event(BtnClicked) := DummyClass.ButtonClicked;), связать их (QButton_hook_hook_clicked(ButtonHook, BtnClicked);)

Все вышеописанное говорит о большой работе, проделанной программистами Borland при разработке CLX. За подробностями взаимодействия CLX с Qt рекомендую обратиться к статье Bruno Sonnino "Programming Kylix with the CLXDisplay API", размещенную на сайте Borland.
В связи со всем сказанным возникает вопрос - как будет взаимодействовать с Qt анонсируемый Borland аналог C++ Builder. С одной стороны он должен поддерживать CLX, с другой вышеописанные проблемы с созданием собственных слотов перед ним стоять, по идее, не должны.

Совместимость с Delphi 5
Одной из задач разработки Kylix являлось обеспечение максимальной совместимости с Delphi. Данную задачу можно разбить на несколько подзадач.

Обеспечение идентичности языка программирования. Достигнуто - в обоих средствах разработки приложений используется Object Pascal.
Близкое сходство IDE. Достигнуто. Переход из IDE Delphi в IDE Kylix не вызывает проблем.
Общность библиотеки классов. CLX разрабатывался максимально совместимым с VCL. Анонсировано, что в Delphi 6 VCL будет замещен CLX. Так что, в ближайшее время задача будет решена.
Одинаковость файлов, составляющих проект. Почти достигнута, в Kylix, по умолчанию, файлы *.dfm (описание свойств компонентов формы) имеют расширение *.xfm.
Документация Kylix содержит довольно большой раздел, посвященный переносу приложений и совместимости с Delphi.
В частности, о чем придется забыть в Kylix
1. Вызовы Windows APIРеестр Windows
2. COM & ActiveX
3. BDE&ADO
4. CORBA
5. Трехзвенные БД
Таким образом, на сегодняшний день можно говорить о частичной совместимости Delphi и Kylix, и то на уровне графического интерфейса пользователя.
Моя личная попытка перенести простейший проект с одной формой из Delphi в Kylix и обратно увенчались успехом, однако, по моему мнению, ни один из серьезных проектов автоматически или даже полуавтоматически из Delphi5 в Kylix портирован не будет.
Ранее для Delphi неоднократно отмечалось, что размер даже минимального проекта с одной формой составлял более 200 кбайт. Kylix, к сожалению, поддержал и развил эту традицию. Размер вышеуказанного проекта составил 385 к байт.

Резюме: Разработчики приложили максимум усилий для совместимости Kylix и Delphi. По анонсам Delphi 6 должна быть максимально совместима с Kylix, однако глубокие различия Linux и Windows приводят к тому, что на практике 100% совместимость будет вряд ли достигнута.


Заключение
Итак, подведем итоги. Безусловно, что выход Kylix является событием в мире разработчиков программного обеспечения. Необходимость в продукте данного класса назрела и нужно выразить большую благодарность Borland за усилия, затраченные на разработку. Сильной стороной продукта являются:

Удобный IDE
Документированность продукта
Хорошо продуманная среда разработки графического интерфейса пользователя
Средства разработки приложений для Internet.
Максимальная приближенность к Delphi
Слабые стороны
Отсутствие технологий создания распределенных объектов (в первую очередь CORBA)
Некоторые неисправленные ошибки
Небольшой набор драйверов для серверов баз данных
Невозможность работы с локальными БД
Отсутствие отчетов в БД
Как мне кажется, как среда разработки Kylix дозрел до уровня Delphi 2-3. Можно рекомендовать его как средство разработки графического интерфейса, работы с серверами базами данных клиент-сервер, разработки приложений для работы с сервисами интернета. В остальных областях программирования ( мультимедиа, распределенные объекты, трехзвенные БД) налицо наличие нереализованного потенциала развития. Данному продукту можно поставить твердую четверку.

Автор: Mike Goblin

Взято из

с разрешения автора.






Обзоры по Kylix


Обзоры по Kylix



Cодержание раздела:






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


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




Данная проблема возникает из-за того, что BDE вначале запрашивает базу данных для получения информации о таблице, прежде чем он начнет с ней работать. Как только появляется информация о таблице, она кэшируется и обращение к таблице во время всего сеанса (пока TDatabase.Connection имеет значение True) происходит практически мгновенно. Для того, чтобы использовать кэшируемую информацию и при последующем запуске приложения, в конфигурации BDE найдите необходимый псевдоним и установите BDE CACHE = TRUE и BDE CACHE DIR = 'C:\temp' или любой другой удобный каталог.

ПРИМЕЧАНИЕ:При любом изменении структуры таблицы Вам придется удалять кэш вручную. Имя файла, в котором хранится кэш, Вы можете узнать, посмотрев в любом текстовом редакторе файл SCache.INI.

Взято из

Советов по Delphi от


Сборник Kuliba






ODBC соединения


ODBC соединения




...я обращал ваше внимание на трудности коннекта Delphi-приложений с Watcom. За исключением досадной проблемы с чуствительностью регистров у ODBC драйверов (которая пропадает после установки соответствующих заплаток), мое приложение действительно лучше соединяется с базой данных Watcom, чем LIBS. Вот функция, которую я использую для подключения к серверу:



functionTLogonForm.LogonToServer: Boolean;
begin
  LogonToServer := FALSE;
  MyDatabase.AliasName := DatabaseEdit.Text;
  MyDatabase.Params.Values['USER NAME'] := UserIDEdit.Text;
  MyDatabase.Params.Values['PASSWORD'] := PasswordEdit.Text;
  MyDatabase.Params.Values['SERVER NAME'] := ServerName;
  try
    MyDatabase.Connected := TRUE;
    LogonToServer := TRUE;
  except
    on E: EDatabaseError do
      MessageDlg('Программа не в состоянии подключиться к
        серверу баз данных по следующей причине:
          ' + #10 + #10 + E.Message, mtError, [mbOK], 0);
  end;
end;




Эта функция находится в модуле с формой диалога подключения, на которой расположены три поля редактирования: идентификатор пользователя, пароль и имя базы данных. При щелчке пользователем на кнопке OK, значение из поля с именем базы данных используется для поиска в файле ODBC.INI:



ServerName := ODBCIni.ReadString(DatabaseEdit.Text, 'Database', '');




Этой строчкой мы получаем фактическое имя файла базы данных, к которому нам необходимо получить доступ ('SERVER NAME' - параметр соединения).

Во время разработки я выставил в своем компоненте TDatabase следующие параметры:


 Connected: FALSE
 DatabaseName: DCAC {это псевдоним, используемый приложением}
 KeepConnection: TRUE
 LoginPrompt: FALSE
 Name: MyDatabase
 TransIsolation: tiReadCommitted

AliasName, DriverName и Params в режиме проектирования остаются пусты, DriverName не используется совсем, т.к. во время выполнения приложения используется AliasName (они являются взаимоисключающими, вы можете установить что-то одно, но не оба сразу).
Вот секции Interbase и Watcom моего файла ODBC.INI:

 [DCAC_IB]
 Driver=C:\WIN\SYSTEM\BLINT04.DLL
 Description=DC Aquatics (Interbase)
 Database=D:\DCAC_IB\DCAC.GDB

 [DCAC_WSQL]
 Driver=D:\WSQL\wsqlodbc.dll
 Description=DC Aquatics (Watcom)
 Database=D:\DCAC_WAT\DCAC.DB
 Start=D:\wsql\db32w %d

Если мне необходимо подключиться к базе данных Watcom, все, что мне нужно сделать - изменить содержимое поля редактирования имени базы данных в диалоге подключения на 'DCAC_WSQL'. Если мне нужно использовать базу данных Interbase, я набираю 'DCAC_IB'. Работает замечательно.
Надеюсь это поможет... успехов...

Взято с





Ограничения Foxpro


Ограничения Foxpro





Table and Index Files


Max.# of records per table         1 billion*   
Max. # of chars per record         65,000   
Max. # of fields per record         255   
Max. # of open DBFs            225   
Max. # of chars per field         254   
Max. # of chars per index key (IDX)   100   
Max. # of chars per index key (CDX)   240   
Max. # of open index files per table   unlimited**   
Max. # of open index files in all work areas   unlimited**   

* The actual file size (in bytes) cannot exceed 2 gigabytes for single-user or exclusively opened multi-user tables. Shared tables with no indexes or .IDX indexes cannot exceed 1 gigabyte. Shared tables with structural .CDX indexes cannot exceed 2 gigabytes.
** Limited by memory. In FoxPro for MS-DOS and FoxPro for Windows, also limited by available MS-DOS file handles. Each .CDX file uses only 1 file handle. The number of MS-DOS file handles is determined by the CONFIG.SYS FILES parameter.

Field Characteristics
Max. size of character fields         254   
Max. size of numeric fields            20   
Max. # of chars in field names         10   
Digits of precision in numeric computations   16   


Взято с

Delphi Knowledge Base






Ограничения Paradox


Ограничения Paradox





Table and Index Files


127Tables open per system   
64   Record locks on one table (16Bit) per session   
255   Record locks on one table (32Bit) per session   
255   Records in transactions on a table (32 Bit)   
512   Open physical files (DB, PX, MB, X??, Y??, VAL, TV)   
300   Users in one PDOXUSRS.NET file   
255   Number of fields per table   
255   Size of character fields   
2   Billion records in a table   
2   Billion bytes in .DB (Table) file   
10800   Bytes per record for indexed tables   
32750   Bytes per record for non-indexed tables   
127   Number of secondary indexes per table   
16   Number of fields in an index   
255   Concurrent users per table   
256   Megabytes of data per BLOB field   
100   Passwords per session   
15   Password length   
63   Passwords per table   
159   Fields with validity checks (32 Bit)   
63   Fields with validity checks (16 Bit)   


Взято с

Delphi Knowledge Base






Огромные LCK-файлы (Lock File Has Grown Too Large)


Огромные LCK-файлы (Lock File Has Grown Too Large)




Если .EXE-файл расположен в том же каталоге, что и таблица Paradox, и флажок Local Share установлен в TRUE, .LCK-файл с каждым запросом растет как на дрожжах. Другая условие - вы имеете соединение посредством DbiOpenTable или TTable.Open.

РЕШЕНИЕ:

Установите частный (private) каталог в какое-нибудь другое место
Переместите .EXE-файл в каталог, отличный от каталога с файлами таблиц
Установите Local Share в FALSE

- Scott Frolich

Взято из

Советов по Delphi от


Сборник Kuliba




Эта проблема специфична для таблиц форматат Paradox, относится как 16 битным, так и 32 битным версиям BDE, и может встречается при выполнении одного из следующих условий:

1. Исполняемый файл приложения находится в той же папке, что и таблица.
2. Некорректно установлен (неустановлен) параметр Private Directory.
3. Открытая в TTable таблица Paradox участвует в запросах TQuery.
4. Параметр LOCAL SHARE установлен в True (BDE Administrator, закладка "System")

Для решения проблемы необходимо выполнить следующие шаги:

1. В папке с исполняемым файлом приложения создайте три новых папки: TABLES, PRIV и NET (длина пути NetDir не должна привышать 31 символа). Поместите все таблицы приложения в папку Tables

2. Во время исполнения Ваше приложение должно устанавливать значения:

Session.PrivateDir := ExtractFilePath(ParamStr(0)) + 'PRIV'; Session.NetFileDir := ExtractFilePath(ParamStr(0)) + 'NET';

3. Убедитесь, что значение LOCAL SHARE установлено в False (BDE Administrator, закладка "System")

Эти рекомендации позволяют избежать ошибки "Lock File Too Large".

Источник

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

1. Не размещайте базу и программу в одной папке, тем более, что программу желательно поместить в защищенную от записи папку.

2. Приватный каталог надо разместить в персональной папке, желательно для каждой запущенной копии приложения отдельный, подходящее место персональная папка TEMP\SessionID\

3. Сетевой каталог NETDIR - выделить отдельную папку на сети исключительно только для этой цели и единную для всех приложений BDE

4. LOCAL SHARE - TRUE, можно попробовать и FALSE, если это не будет приводить к порче индексов.

Взято из





OLE и Interbase - прочесть и записать


OLE и Interbase - прочесть и записать




Автор: Rob Minte

procedureTForm1.ReadOLE;
var
  BS:    TBlobStream;
begin
  BS := TBlobStream.Create(Table1BLOBFIELD_BLOB, bmRead);
  OLEContainer1.LoadFromStream(BS);
  BS.Free;
end;

procedure TForm1.WriteOLE;
var
  BS:    TBlobStream;
begin
  BS := TBlobStream.Create(Table1BLOBFIELD_BLOB, bmWrite);
  OLEContainer1.SaveToStream(BS);
  BS.Free;
end;

Взято из





OpenGL: Каким обpазом выбиpать pазмеp шpифта


OpenGL: Каким обpазом выбиpать pазмеp шpифта




OpneGL: Каким обpазом выбиpать pазмеp шpифта, т.к. все мои стpадания по выбоpy паpаметpов шpифта в CreateFont() никак не отpажались на его pазмеpе

Все что я пpидyмал, это юзать glScale(), но в этом слyчае полyчаем плохое качество (по сpавнению с той-же Воpдой) пpи малом pазмеpе символов. Вот часть работающего примера на Си (переведенного мною на Паскаль (АА)).

procedureGLSetupRC( pData: Pointer )
//void GLSetupRC(void *pData)
//{
var
//  HDC hDC;
hDC: HDC;
//  HFONT hFont;
hFont: HFONT;
//  GLYPHMETRICSFLOAT agmf[128];
agmf: array [0..127] of GLYPHMETRICSFLOAT;
//  LOGFONT logfont;
logfont: LOGFONT;

begin

 logfont.lfHeight := -10;
 logfont.lfWidth := 0;
 logfont.lfEscapement := 0;
 logfont.lfOrientation := 0;
 logfont.lfWeight := FW_BOLD;
 logfont.lfItalic := FALSE;
 logfont.lfUnderline := FALSE;
 logfont.lfStrikeOut := FALSE;
 logfont.lfCharSet := ANSI_CHARSET;
 logfont.lfOutPrecision := OUT_DEFAULT_PRECIS;
 logfont.lfClipPrecision := CLIP_DEFAULT_PRECIS;
 logfont.lfQuality := DEFAULT_QUALITY;
 logfont.lfPitchAndFamily := DEFAULT_PITCH;
 //strcpy(logfont.lfFaceName,"Arial");
//  strcpy(logfont.lfFaceName,"Decor");
 StrPCopy( logfont.lfFaceName, 'Decor' );

 glDepthFunc(GL_LESS);
 glEnable(GL_DEPTH_TEST);  // Hidden surface removal
 glFrontFace(GL_CCW);      // Counter clock-wise polygons face out
 glEnable(GL_CULL_FACE);   // Do not calculate insides
 glShadeModel(GL_SMOOTH);  // Smooth shading
 glEnable(GL_AUTO_NORMAL);
 glEnable(GL_NORMALIZE);
 glEnable(GL_COLOR_MATERIAL);

 glClearColor(0.0, 0.0, 0.0, 1.0 );

 glEnable(GL_LIGHTING);
 glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);
 glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);
 glLightfv(GL_LIGHT0,GL_SPECULAR,specular);
 glLightfv(GL_LIGHT0,GL_POSITION,lightPos);
 glEnable(GL_LIGHT0);

 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

 glMaterialfv(GL_FRONT, GL_SPECULAR,specular);
 glMateriali(GL_FRONT,GL_SHININESS,100);

 // Blue 3D Text
 glRGB(0, 0, 255);

 // Select the font into the DC
 hDC := (HDC)pData;
//  hFont = CreateFontIndirect(&logfont);
 hFont := CreateFontIndirect( Addr(logfont) );
 SelectObject (hDC, hFont);

 //create display lists for glyphs 0 through 255 with 0.3 extrusion
 // and default deviation. The display list numbering starts at 1000
 // (it could be any number).

//  if(!wglUseFontOutlines(hDC, 0, 128, 1000, 0., 0.3,
//                            WGL_FONT_POLYGONS, agmf))
 if not wglUseFontOutlines(hDC, 0, 128, 1000, 0., 0.3,

//>                                         ``` - это тебе поможет
//> Выводить текст можно в любым масштабе

                           WGL_FONT_POLYGONS, agmf) then

    Windows.MessageBox(nil,'Could not create Font Outlines',
                    'Error',MB_OK or MB_ICONSTOP);

 // Delete the font now that we are done

 DeleteObject(hFont);
//}
end;

// void GLRenderScene(void *pData)
procedure GLRenderScene(pData: Pointer);
begin
 (*  ...  *)

 // Draw 3D text
 glListBase(1000);
 glPushMatrix();
 // Set up transformation to draw the string.
 glTranslatef(-35.0, 0.0, -5.0);
 glScalef(60.0, 60.0, 60.0);
 glCallLists(3, GL_UNSIGNED_BYTE, 'Decor');
 glPopMatrix();  // Clear the window with current clearing color

 (* ... *)
end;

Автор: Garik Pozdeev (2:5021/15.9)

Автор:

StayAtHome

Взято из





OpenGL - радиальное размытие


OpenGL - радиальное размытие




//К заголовку RadialBlur(For OpenGL)
// Данный код работает правильно только, если в пректе 0 форм ,
// а сам код введен в DPR файл!

program RadialBlur;

uses
  Windows,
  Messages,
  OpenGL;

const
  WND_TITLE = 'Radial Blur';
  FPS_TIMER = 1; // Timer to calculate FPS
  FPS_INTERVAL = 1000; // Calculate FPS every 1000 ms

type
  TVector = array[0..2] of glFloat;
var
  h_Wnd: HWND; // Global window handle
  h_DC: HDC; // Global device context
  h_RC: HGLRC; // OpenGL rendering context
  keys: array[0..255] of Boolean; // Holds keystrokes
  FPSCount: Integer = 0; // Counter for FPS
  ElapsedTime: Integer; // Elapsed time between frames

  // Textures
  BlurTexture: glUint; // An Unsigned Int To Store The Texture Number

  // User vaiables
  Angle: glFloat;
  Vertexes: array[0..3] of TVector;
  normal: TVector;

  // Lights and Materials
  globalAmbient: array[0..3] of glFloat = (0.2, 0.2, 0.2, 1.0);
  // Set Ambient Lighting To Fairly Dark Light (No Color)
  Light0Pos: array[0..3] of glFloat = (0.0, 5.0, 10.0, 1.0);
  // Set The Light Position
  Light0Ambient: array[0..3] of glFloat = (0.2, 0.2, 0.2, 1.0);
  // More Ambient Light
  Light0Diffuse: array[0..3] of glFloat = (0.3, 0.3, 0.3, 1.0);
  // Set The Diffuse Light A Bit Brighter
  Light0Specular: array[0..3] of glFloat = (0.8, 0.8, 0.8, 1.0);
  // Fairly Bright Specular Lighting

  LmodelAmbient: array[0..3] of glFloat = (0.2, 0.2, 0.2, 1.0);
  // And More Ambient Light

{$R *.RES}

procedure glBindTexture(target: GLenum; texture: GLuint);
  stdcall; external opengl32;

procedure glGenTextures(n: GLsizei; var textures: GLuint);
  stdcall; external opengl32;

procedure glCopyTexSubImage2D(target: GLenum; level, xoffset,
  yoffset, x, y: GLint; width, height: GLsizei);
  stdcall; external opengl32;

procedure glCopyTexImage2D(target: GLenum; level: GLint;
  internalFormat: GLenum; x, y: GLint;
  width, height: GLsizei; border: GLint); stdcall; external opengl32;

{------------------------------------------------------------------}
{ Function to convert int to string. (No sysutils = smaller EXE) }
{------------------------------------------------------------------}
// using SysUtils increase file size by 100K

function IntToStr(Num: Integer): string;
begin
  Str(Num, result);
end;

function EmptyTexture: glUint;
var
  txtnumber: glUint;
  data: array of glUint;
  pData: Pointer;
begin
  // Create Storage Space For Texture Data (128x128x4)
  GetMem(pData, 128 * 128 * 4);

  glGenTextures(1, txtnumber); // Create 1 Texture
  glBindTexture(GL_TEXTURE_2D, txtnumber); // Bind The Texture
  glTexImage2D(GL_TEXTURE_2D, 0, 4, 128, 128, 0, GL_RGBA,
    GL_UNSIGNED_BYTE, pData);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  result := txtNumber;
end;

procedure ReduceToUnit(var vector: array of glFloat);
var
  length: glFLoat;
begin
  // Calculates The Length Of The Vector
  length := sqrt((vector[0] * vector[0]) + (vector[1] * vector[1]) +
    (vector[2] * vector[2]));
  if Length = 0 then
    Length := 1;

  vector[0] := vector[0] / length;
  vector[1] := vector[1] / length;
  vector[2] := vector[2] / length;
end;

procedure calcNormal(const v: array of TVector;
  var cross: array of glFloat);
var
  v1, v2: array[0..2] of glFloat;
begin
  // Finds The Vector Between 2 Points By Subtracting
  // The x,y,z Coordinates From One Point To Another.

  // Calculate The Vector From Point 1 To Point 0
  v1[0] := v[0][0] - v[1][0]; // Vector 1.x=Vertex[0].x-Vertex[1].x
  v1[1] := v[0][1] - v[1][1]; // Vector 1.y=Vertex[0].y-Vertex[1].y
  v1[2] := v[0][2] - v[1][2]; // Vector 1.z=Vertex[0].y-Vertex[1].z
  // Calculate The Vector From Point 2 To Point 1
  v2[0] := v[1][0] - v[2][0]; // Vector 2.x=Vertex[0].x-Vertex[1].x
  v2[1] := v[1][1] - v[2][1]; // Vector 2.y=Vertex[0].y-Vertex[1].y
  v2[2] := v[1][2] - v[2][2]; // Vector 2.z=Vertex[0].z-Vertex[1].z
  // Compute The Cross Product To Give Us A Surface Normal
  cross[0] := v1[1] * v2[2] - v1[2] * v2[1]; // Cross Product For Y - Z
  cross[1] := v1[2] * v2[0] - v1[0] * v2[2]; // Cross Product For X - Z
  cross[2] := v1[0] * v2[1] - v1[1] * v2[0]; // Cross Product For X - Y

  ReduceToUnit(cross); // Normalize The Vectors
end;

// Draws A Helix

procedure ProcessHelix;
const
  Twists = 5;
  MaterialColor: array[1..4] of glFloat = (0.4, 0.2, 0.8, 1.0);
  Specular: array[1..4] of glFloat = (1, 1, 1, 1);
var
  x, y, z: glFLoat;
  phi, theta: Integer;
  r, u, v: glFLoat;
begin
  glLoadIdentity(); // Reset The Modelview Matrix
  // Eye Position (0,5,50) Center Of Scene (0,0,0), Up On Y Axis
  gluLookAt(0, 5, 50, 0, 0, 0, 0, 1, 0);

  glPushMatrix(); // Push The Modelview Matrix
  glTranslatef(0, 0, -50); // Translate 50 Units Into The Screen
  glRotatef(angle / 2.0, 1, 0, 0); // Rotate By angle/2 On The X-Axis
  glRotatef(angle / 3.0, 0, 1, 0); // Rotate By angle/3 On The Y-Axis

  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, @MaterialColor);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, @specular);

  r := 1.5; // Radius

  glBegin(GL_QUADS); // Begin Drawing Quads
  phi := 0;
  while phi < 360 do
  begin
    theta := 0;
    while theta < 360 * twists do
    begin
      v := phi / 180 * pi; // Calculate Angle Of First Point ( 0 )
      u := theta / 180.0 * pi; // Calculate Angle Of First Point ( 0 )

      x := cos(u) * (2 + cos(v)) * r; // Calculate x Position (1st Point)
      y := sin(u) * (2 + cos(v)) * r; // Calculate y Position (1st Point)
      z := (u - (2 * pi) + sin(v)) * r; // Calculate z Position (1st Point)

      vertexes[0][0] := x; // Set x Value Of First Vertex
      vertexes[0][1] := y; // Set y Value Of First Vertex
      vertexes[0][2] := z; // Set z Value Of First Vertex

      v := (phi / 180 * pi); // Calculate Angle Of Second Point ( 0 )
      u := ((theta + 20) / 180 * pi); // Calculate Angle Of Second Point ( 20 )

      x := cos(u) * (2 + cos(v)) * r; // Calculate x Position (2nd Point)
      y := sin(u) * (2 + cos(v)) * r; // Calculate y Positio
      z := (u - (2 * pi) + sin(v)) * r; // Calculate z Position (2nd Point)

      vertexes[1][0] := x; // Set x Value Of Second Vertex
      vertexes[1][1] := y; // Set y Value Of Second Vertex
      vertexes[1][2] := z; // Set z Value Of Second Vertex

      v := (phi + 20) / 180 * pi; // Calculate Angle Of Third Point ( 20 )
      u := (theta + 20) / 180 * pi; // Calculate Angle Of Third Point ( 20 )

      x := cos(u) * (2 + cos(v)) * r; // Calculate x Position (3rd Point)
      y := sin(u) * (2 + cos(v)) * r; // Calculate y Position (3rd Point)
      z := (u - (2 * pi) + sin(v)) * r; // Calculate z Position (3rd Point)

      vertexes[2][0] := x; // Set x Value Of Third Vertex
      vertexes[2][1] := y; // Set y Value Of Third Vertex
      vertexes[2][2] := z; // Set z Value Of Third Vertex

      v := (phi + 20) / 180 * pi; // Calculate Angle Of Fourth Point ( 20 )
      u := theta / 180 * pi; // Calculate Angle Of Fourth Point ( 0 )

      x := cos(u) * (2 + cos(v)) * r; // Calculate x Position (4th Point)
      y := sin(u) * (2 + cos(v)) * r; // Calculate y Position (4th Point)
      z := (u - (2 * pi) + sin(v)) * r; // Calculate z Position (4th Point)

      vertexes[3][0] := x; // Set x Value Of Fourth Vertex
      vertexes[3][1] := y; // Set y Value Of Fourth Vertex
      vertexes[3][2] := z; // Set z Value Of Fourth Vertex

      calcNormal(vertexes, normal); // Calculate The Quad Normal

      glNormal3f(normal[0], normal[1], normal[2]); // Set The Normal

      // Render The Quad
      glVertex3f(vertexes[0][0], vertexes[0][1], vertexes[0][2]);
      glVertex3f(vertexes[1][0], vertexes[1][1], vertexes[1][2]);
      glVertex3f(vertexes[2][0], vertexes[2][1], vertexes[2][2]);
      glVertex3f(vertexes[3][0], vertexes[3][1], vertexes[3][2]);
      theta := theta + 20;
    end;
    phi := phi + 20;
  end;
  glEnd(); // Done Rendering Quads
  glPopMatrix(); // Pop The Matrix
end;

// Set Up An Ortho View

procedure ViewOrtho;
begin
  glMatrixMode(GL_PROJECTION); // Select Projection
  glPushMatrix(); // Push The Matrix
  glLoadIdentity(); // Reset The Matrix
  glOrtho(0, 640, 480, 0, -1, 1); // Select Ortho Mode (640x480)
  glMatrixMode(GL_MODELVIEW); // Select Modelview Matrix
  glPushMatrix(); // Push The Matrix
  glLoadIdentity(); // Reset The Matrix
end;

// Set Up A Perspective View

procedure ViewPerspective;
begin
  glMatrixMode(GL_PROJECTION); // Select Projection
  glPopMatrix(); // Pop The Matrix
  glMatrixMode(GL_MODELVIEW); // Select Modelview
  glPopMatrix(); // Pop The Matrix
end;

// Renders To A Texture

procedure RenderToTexture;
begin
  glViewport(0, 0, 128, 128); // Set Our Viewport (Match Texture Size)
  ProcessHelix(); // Render The Helix
  glBindTexture(GL_TEXTURE_2D, BlurTexture); // Bind To The Blur Texture

  // Copy Our ViewPort To The Blur Texture (From 0,0 To 128,128... No Border)
  glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 0, 0, 128, 128, 0);
  glClearColor(0.0, 0.0, 0.5, 0.5); // Set The Clear Color To Medium Blue
  // Clear The Screen And Depth Buffer
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
  glViewport(0, 0, 640, 480); // Set Viewport (0,0 to 640x480)
end;

// Draw The Blurred Image

procedure DrawBlur(const times: Integer; const inc: glFloat);
var
  spost, alpha, alphainc: glFloat;
  I: Integer;
begin
  alpha := 0.2;

  glEnable(GL_TEXTURE_2D); // Enable 2D Texture Mapping
  glDisable(GL_DEPTH_TEST); // Disable Depth Testing
  glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Set Blending Mode
  glEnable(GL_BLEND); // Enable Blending
  glBindTexture(GL_TEXTURE_2D, BlurTexture); // Bind To The Blur Texture
  ViewOrtho(); // Switch To An Ortho View

  alphainc := alpha / times; // alphainc=0.2f / Times To Render Blur

  glBegin(GL_QUADS); // Begin Drawing Quads
  // Number Of Times To Render Blur
  for I := 0 to times - 1 do
  begin
    glColor4f(1.0, 1.0, 1.0, alpha); // Set The Alpha Value (Starts At 0.2)
    glTexCoord2f(0 + spost, 1 - spost); // Texture Coordinate ( 0, 1 )
    glVertex2f(0, 0); // First Vertex ( 0, 0 )

    glTexCoord2f(0 + spost, 0 + spost); // Texture Coordinate ( 0, 0 )
    glVertex2f(0, 480); // Second Vertex ( 0, 480 )

    glTexCoord2f(1 - spost, 0 + spost); // Texture Coordinate ( 1, 0 )
    glVertex2f(640, 480); // Third Vertex ( 640, 480 )

    glTexCoord2f(1 - spost, 1 - spost); // Texture Coordinate ( 1, 1 )
    glVertex2f(640, 0); // Fourth Vertex ( 640, 0 )

    // Gradually Increase spost (Zooming Closer To Texture Center)
    spost := spost + inc;
    // Gradually Decrease alpha (Gradually Fading Image Out)
    alpha := alpha - alphainc;
  end;
  glEnd(); // Done Drawing Quads

  ViewPerspective(); // Switch To A Perspective View

  glEnable(GL_DEPTH_TEST); // Enable Depth Testing
  glDisable(GL_TEXTURE_2D); // Disable 2D Texture Mapping
  glDisable(GL_BLEND); // Disable Blending
  glBindTexture(GL_TEXTURE_2D, 0); // Unbind The Blur Texture
end;

{------------------------------------------------------------------}
{ Function to draw the actual scene }
{------------------------------------------------------------------}

procedure glDraw();
begin
  // Clear The Screen And The Depth Buffer
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
  glLoadIdentity(); // Reset The View
  RenderToTexture; // Render To A Texture
  ProcessHelix; // Draw Our Helix
  DrawBlur(25, 0.02); // Draw The Blur Effect

  angle := ElapsedTime / 5; // Update angle Based On The Clock
end;

{------------------------------------------------------------------}
{ Initialise OpenGL }
{------------------------------------------------------------------}

procedure glInit();
begin
  glClearColor(0.0, 0.0, 0.0, 0.5); // Black Background
  glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading
  glClearDepth(1.0); // Depth Buffer Setup
  glDepthFunc(GL_LESS); // The Type Of Depth Test To Do

  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
  file:
  //Realy Nice perspective calculations

  glEnable(GL_DEPTH_TEST); // Enable Depth Buffer
  glEnable(GL_TEXTURE_2D); // Enable Texture Mapping

  // Set The Ambient Light Model
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, @LmodelAmbient);

  // Set The Global Ambient Light Model
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, @GlobalAmbient);
  glLightfv(GL_LIGHT0, GL_POSITION, @light0Pos); // Set The Lights Position
  glLightfv(GL_LIGHT0, GL_AMBIENT, @light0Ambient); // Set The Ambient Light
  glLightfv(GL_LIGHT0, GL_DIFFUSE, @light0Diffuse); // Set The Diffuse Light
  // Set Up Specular Lighting
  glLightfv(GL_LIGHT0, GL_SPECULAR, @light0Specular);
  glEnable(GL_LIGHTING); // Enable Lighting
  glEnable(GL_LIGHT0); // Enable Light0

  BlurTexture := EmptyTexture(); // Create Our Empty Texture
  glShadeModel(GL_SMOOTH); // Select Smooth Shading
  glMateriali(GL_FRONT, GL_SHININESS, 128);
end;

{------------------------------------------------------------------}
{ Handle window resize }
{------------------------------------------------------------------}

procedure glResizeWnd(Width, Height: Integer);
begin
  if (Height = 0) then // prevent divide by zero exception
    Height := 1;
  glViewport(0, 0, Width, Height); // Set the viewport for the OpenGL window
  glMatrixMode(GL_PROJECTION); // Change Matrix Mode to Projection
  glLoadIdentity(); // Reset View
  gluPerspective(45.0, Width / Height, 2.0, 200.0);
  // Do the perspective calculations. Last value = max clipping depth

  glMatrixMode(GL_MODELVIEW); // Return to the modelview matrix
  glLoadIdentity(); // Reset View
end;

{------------------------------------------------------------------}
{ Processes all the keystrokes }
{------------------------------------------------------------------}

procedure ProcessKeys;
begin
end;

{------------------------------------------------------------------}
{ Determines the application's response to the messages received }
{------------------------------------------------------------------}

function WndProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM):
  LRESULT; stdcall;
begin
  case (Msg) of
    WM_CREATE:
      begin
        // Insert stuff you want executed when the program starts
      end;
    WM_CLOSE:
      begin
        PostQuitMessage(0);
        Result := 0
      end;
    // Set the pressed key (wparam) to equal true so we can check if its pressed
    WM_KEYDOWN:
      begin
        keys[wParam] := True;
        Result := 0;
      end;
    // Set the released key (wparam) to equal false so we can check if its pressed
    WM_KEYUP:
      begin
        keys[wParam] := False;
        Result := 0;
      end;
    WM_SIZE: // Resize the window with the new width and height
      begin
        glResizeWnd(LOWORD(lParam), HIWORD(lParam));
        Result := 0;
      end;
    WM_TIMER: // Add code here for all timers to be used.
      begin
        if wParam = FPS_TIMER then
        begin
          FPSCount := Round(FPSCount * 1000 / FPS_INTERVAL);
          // calculate to get per Second incase intercal is
          // less or greater than 1 second
          SetWindowText(h_Wnd, PChar(WND_TITLE + ' [' + intToStr(FPSCount)
            + ' FPS]'));
          FPSCount := 0;
          Result := 0;
        end;
      end;
  else
    // Default result if nothing happens
    Result := DefWindowProc(hWnd, Msg, wParam, lParam);
  end;
end;

{---------------------------------------------------------------------}
{ Properly destroys the window created at startup (no memory leaks) }
{---------------------------------------------------------------------}

procedure glKillWnd(Fullscreen: Boolean);
begin
  if Fullscreen then // Change back to non fullscreen
  begin
    ChangeDisplaySettings(devmode(nil^), 0);
    ShowCursor(True);
  end;

  // Makes current rendering context not current, and releases the device
  // context that is used by the rendering context.
  if (not wglMakeCurrent(h_DC, 0)) then
    MessageBox(0, 'Release of DC and RC failed!', 'Error',
      MB_OK or MB_ICONERROR);

  // Attempts to delete the rendering context
  if (not wglDeleteContext(h_RC)) then
  begin
    MessageBox(0, 'Release of rendering context failed!', 'Error',
      MB_OK or MB_ICONERROR);
    h_RC := 0;
  end;

  // Attemps to release the device context
  if ((h_DC = 1) and (ReleaseDC(h_Wnd, h_DC) < > 0)) then
  begin
    MessageBox(0, 'Release of device context failed!', 'Error',
      MB_OK or MB_ICONERROR);
    h_DC := 0;
  end;

  // Attempts to destroy the window
  if ((h_Wnd < > 0) and (not DestroyWindow(h_Wnd))) then
  begin
    MessageBox(0, 'Unable to destroy window!', 'Error', MB_OK or
      h_Wnd := 0;
  end;

  // Attempts to unregister the window class
  if (not UnRegisterClass('OpenGL', hInstance)) then
  begin
    MessageBox(0, 'Unable to unregister window class!', 'Error',
      MB_OK or MB_ICONERROR);
    hInstance := 0;
  end;
end;

{--------------------------------------------------------------------}
{ Creates the window and attaches a OpenGL rendering context to it }
{--------------------------------------------------------------------}

function glCreateWnd(Width, Height: Integer; Fullscreen: Boolean;
  PixelDepth: Integer): Boolean;
var
  wndClass: TWndClass; // Window class
  dwStyle: DWORD; // Window styles
  dwExStyle: DWORD; // Extended window styles
  dmScreenSettings: DEVMODE; // Screen settings (fullscreen, etc...)
  PixelFormat: GLuint; // Settings for the OpenGL rendering
  h_Instance: HINST; // Current instance
  pfd: TPIXELFORMATDESCRIPTOR; // Settings for the OpenGL window
begin
  h_Instance := GetModuleHandle(nil);
  file: //Grab An Instance For Our Window
  ZeroMemory(@wndClass, SizeOf(wndClass)); // Clear the window class structure

  with wndClass do // Set up the window class
  begin
    style := CS_HREDRAW or // Redraws entire window if length changes
    CS_VREDRAW or // Redraws entire window if height changes
    CS_OWNDC; // Unique device context for the window
    lpfnWndProc := @WndProc; // Set the window procedure to our func WndProc
    hInstance := h_Instance;
    hCursor := LoadCursor(0, IDC_ARROW);
    lpszClassName := 'OpenGL';
  end;

  if (RegisterClass(wndClass) = 0) then // Attemp to register the window class
  begin
    MessageBox(0, 'Failed to register the window class!', 'Error',
      MB_OK or MB_ICONERROR);
    Result := False;
    Exit
  end;

  // Change to fullscreen if so desired
  if Fullscreen then
  begin
    ZeroMemory(@dmScreenSettings, SizeOf(dmScreenSettings));
    with dmScreenSettings do
    begin // Set parameters for the screen setting
      dmSize := SizeOf(dmScreenSettings);
      dmPelsWidth := Width; // Window width
      dmPelsHeight := Height; // Window height
      dmBitsPerPel := PixelDepth; // Window color depth
      dmFields := DM_PELSWIDTH or DM_PELSHEIGHT or DM_BITSPERPEL;
    end;

    // Try to change screen mode to fullscreen
    if (ChangeDisplaySettings(dmScreenSettings, CDS_FULLSCREEN) =
      DISP_CHANGE_FAILED) then
    begin
      MessageBox(0, 'Unable to switch to fullscreen!', 'Error',
        MB_OK or MB_ICONERROR);
      Fullscreen := False;
    end;
  end;

  // If we are still in fullscreen then
  if (Fullscreen) then
  begin
    dwStyle := WS_POPUP or // Creates a popup window
    WS_CLIPCHILDREN // Doesn't draw within child windows
    or WS_CLIPSIBLINGS; // Doesn't draw within sibling windows
    dwExStyle := WS_EX_APPWINDOW; // Top level window
    ShowCursor(False); // Turn of the cursor (gets in the way)
  end
  else
  begin
    dwStyle := WS_OVERLAPPEDWINDOW or // Creates an overlapping window
    WS_CLIPCHILDREN or // Doesn't draw within child windows
    WS_CLIPSIBLINGS; // Doesn't draw within sibling windows
    dwExStyle := WS_EX_APPWINDOW or // Top level window
    WS_EX_WINDOWEDGE; // Border with a raised edge
  end;

  // Attempt to create the actual window
  h_Wnd := CreateWindowEx(dwExStyle, // Extended window styles
    'OpenGL', // Class name
    WND_TITLE, // Window title (caption)
    dwStyle, // Window styles
    0, 0, // Window position
    Width, Height, // Size of window
    0, // No parent window
    0, // No menu
    h_Instance, // Instance
    nil); // Pass nothing to WM_CREATE
  if h_Wnd = 0 then
  begin
    glKillWnd(Fullscreen); // Undo all the settings we've changed
    MessageBox(0, 'Unable to create window!', 'Error', MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Try to get a device context
  h_DC := GetDC(h_Wnd);
  if (h_DC = 0) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Unable to get a device context!', 'Error',
      MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Settings for the OpenGL window
  with pfd do
  begin
    // Size Of This Pixel Format Descriptor
    nSize := SizeOf(TPIXELFORMATDESCRIPTOR);
    nVersion := 1; // The version of this data structure
    dwFlags := PFD_DRAW_TO_WINDOW // Buffer supports drawing to window
    or PFD_SUPPORT_OPENGL // Buffer supports OpenGL drawing
    or PFD_DOUBLEBUFFER; // Supports double buffering
    iPixelType := PFD_TYPE_RGBA; // RGBA color format
    cColorBits := PixelDepth; // OpenGL color depth
    cRedBits := 0; // Number of red bitplanes
    cRedShift := 0; // Shift count for red bitplanes
    cGreenBits := 0; // Number of green bitplanes
    cGreenShift := 0; // Shift count for green bitplanes
    cBlueBits := 0; // Number of blue bitplanes
    cBlueShift := 0; // Shift count for blue bitplanes
    cAlphaBits := 0; // Not supported
    cAlphaShift := 0; // Not supported
    cAccumBits := 0; // No accumulation buffer
    cAccumRedBits := 0; // Number of red bits in a-buffer
    cAccumGreenBits := 0; // Number of green bits in a-buffer
    cAccumBlueBits := 0; // Number of blue bits in a-buffer
    cAccumAlphaBits := 0; // Number of alpha bits in a-buffer
    cDepthBits := 16; // Specifies the depth of the depth buffer
    cStencilBits := 0; // Turn off stencil buffer
    cAuxBuffers := 0; // Not supported
    iLayerType := PFD_MAIN_PLANE; // Ignored
    bReserved := 0; // Number of overlay and underlay planes
    dwLayerMask := 0; // Ignored
    dwVisibleMask := 0; // Transparent color of underlay plane
    dwDamageMask := 0; // Ignored
  end;

  // Attempts to find the pixel format supported by a device context that
  // is the best match to a given pixel format specification.
  PixelFormat := ChoosePixelFormat(h_DC, @pfd);
  if (PixelFormat = 0) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Unable to find a suitable pixel format', 'Error',
      MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Sets the specified device context's pixel format to the format
  // specified by the PixelFormat.
  if (not SetPixelFormat(h_DC, PixelFormat, @pfd)) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Unable to set the pixel format', 'Error',
      MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Create a OpenGL rendering context
  h_RC := wglCreateContext(h_DC);
  if (h_RC = 0) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Unable to create an OpenGL rendering context',
      'Error', MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Makes the specified OpenGL rendering context the calling
  // thread's current rendering context
  if (not wglMakeCurrent(h_DC, h_RC)) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Unable to activate OpenGL rendering context', 'Error',
      MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Initializes the timer used to calculate the FPS
  SetTimer(h_Wnd, FPS_TIMER, FPS_INTERVAL, nil);

  // Settings to ensure that the window is the topmost window
  ShowWindow(h_Wnd, SW_SHOW);
  SetForegroundWindow(h_Wnd);
  SetFocus(h_Wnd);

  // Ensure the OpenGL window is resized properly
  glResizeWnd(Width, Height);
  glInit();

  Result := True;
end;

{--------------------------------------------------------------------}
{ Main message loop for the application }
{--------------------------------------------------------------------}

function WinMain(hInstance: HINST; hPrevInstance: HINST;
  lpCmdLine: PChar; nCmdShow: Integer): Integer; stdcall;
var
  msg: TMsg;
  finished: Boolean;
  DemoStart, LastTime: DWord;
begin
  finished := False;

  // Perform application initialization:
  if not glCreateWnd(640, 480, FALSE, 32) then
  begin
    Result := 0;
    Exit;
  end;

  DemoStart := GetTickCount(); // Get Time when demo started

  // Main message loop:
  while not finished do
  begin
    // Check if there is a message for this window
    if (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) then
    begin
      // If WM_QUIT message received then we are done
      if (msg.message = WM_QUIT) then
        finished := True
      else
      begin // Else translate and dispatch the message to this window
        TranslateMessage(msg);
        DispatchMessage(msg);
      end;
    end
    else
    begin
      Inc(FPSCount); // Increment FPS Counter

      LastTime := ElapsedTime;
      ElapsedTime := GetTickCount() - DemoStart; // Calculate Elapsed Time
      // Average it out for smoother movement
      ElapsedTime := (LastTime + ElapsedTime) div 2;

      glDraw(); // Draw the scene
      SwapBuffers(h_DC); // Display the scene

      if (keys[VK_ESCAPE]) then // If user pressed ESC then set finised TRUE
        finished := True
      else
        ProcessKeys; // Check for any other key Pressed
    end;
  end;
  glKillWnd(FALSE);
  Result := msg.wParam;
end;

begin
  WinMain(hInstance, hPrevInst, CmdLine, CmdShow);
end.

Взято с





Описание протокола ARP (Address Resolution Protocol)


Описание протокола ARP (Address Resolution Protocol)




Для определения локального адреса по IP-адресу используется протокол разрешения адреса Address Resolution Protocol, ARP. Протокол ARP работает различным образом в зависимости от того, какой протокол канального уровня работает в данной сети - протокол локальной сети (Ethernet, Token Ring, FDDI) с возможностью широковещательного доступа одновременно ко всем узлам сети, или же протокол глобальной сети (X.25, frame relay), как правило не поддерживающий широковещательный доступ. Существует также протокол, решающий обратную задачу - нахождение IP-адреса по известному локальному адресу. Он называется реверсивный ARP - RARP (Reverse Address Resolution Protocol) и используется при старте бездисковых станций, не знающих в начальный момент своего IP-адреса, но знающих адрес своего сетевого адаптера.

В локальных сетях протокол ARP использует широковещательные кадры протокола канального уровня для поиска в сети узла с заданным IP-адресом.

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

В поле типа сети для сетей Ethernet указывается значение 1. Поле типа протокола позволяет использовать пакеты ARP не только для протокола IP, но и для других сетевых протоколов. Для IP значение этого поля равно 080016.

Длина локального адреса для протокола Ethernet равна 6 байтам, а длина IP-адреса - 4 байтам. В поле операции для ARP запросов указывается значение 1 для протокола ARP и 2 для протокола RARP.

Узел, отправляющий ARP-запрос, заполняет в пакете все поля, кроме поля искомого локального адреса (для RARP-запроса не указывается искомый IP-адрес). Значение этого поля заполняется узлом, опознавшим свой IP-адрес.

В глобальных сетях администратору сети чаще всего приходится вручную формировать ARP-таблицы, в которых он задает, например, соответствие IP-адреса адресу узла сети X.25, который имеет смысл локального адреса. В последнее время наметилась тенденция автоматизации работы протокола ARP и в глобальных сетях. Для этой цели среди всех маршрутизаторов, подключенных к какой-либо глобальной сети, выделяется специальный маршрутизатор, который ведет ARP-таблицу для всех остальных узлов и маршрутизаторов этой сети. При таком централизованном подходе для всех узлов и маршрутизаторов вручную нужно задать только IP-адрес и локальный адрес выделенного маршрутизатора. Затем каждый узел и маршрутизатор регистрирует свои адреса в выделенном маршрутизаторе, а при необходимости установления соответствия между IP-адресом и локальным адресом узел обращается к выделенному маршрутизатору с запросом и автоматически получает ответ без участия администратора.

Взято с






Описание протокола SSH (Secure Shell)


Описание протокола SSH (Secure Shell)





В настоящей статье рассматривается ssh (secure shell), одно из самых распространенных программных средств повышения компьютерной безопасности при работе Unix-систем в Internet.

Число как средств защиты, так и инструментов, способствующих взлому, в Internet огромно. Однако складывается впечатление, что по крайней мере два средства tcpwrapper и ssh входят в состав своеобразного джентльменского набора ОС Unix, который можно рекомендовать любому системному администратору. В частности, эти средства используют все отечественные Internet-провайдеры, с которыми мне приходилось иметь дело. Возможно, это связано и со стандартной_ комплектацией ssh операционной системы FreeBSD, весьма популярной среди наших провайдеров.

Причиной нашего внимания к этой теме является не только популярность ssh среди пользователей, работающих в самых разных областях. Хотя ssh появился лишь в 1995 г., в настоящее время он уже рассматривается как проект стандарта Internet, подготовкой которого занимается специальная рабочая группа secsh в IETF Security Area. Строго говоря, в этой рабочей группе стандартизуется, конечно, не ssh как программный продукт, а набор протоколов ssh.

Тем временем, ssh уже завоевал статус фактического стандарта Internet на безопасные терминальные соединения. Автором первоначального варианта ssh является Т. Ялонен. В настоящее время выпущена вторая версия ssh; разработкой программного продукта SSH2 занимается компания SSH Communication Security Кроме версии ssh, доступной бесплатно, имеется и усовершенствованный коммерчески доступный вариант. Кроме того, с использованием этой программы разработаны и некоторые другие коммерческие продукты. Поставками коммерческой версии ssh занимается Data Fellows; эта компания предлагает, в частности, программные продукты F-secure SSH Server и F-Secure SSH Tunell & Terminal.

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

Что касается типов обеспечиваемого удаленного доступа, ssh поддерживает возможности работы с telnet; безопасную работу с протоколом X11 благодаря возможности перенаправления соответствующих данных по надежным ssh-каналам; безопасную замену многим r_-командам Unix (rsh, rlogin и т.д.), с которыми, как известно, традиционно связаны проблемы обеспечения безопасности.

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

Протокол ssh

Проект стандарта ssh описывает протоколы ssh и состоит из нескольких документов, которые описывают общую архитектуру протокола, а также протоколы трех уровней: протокол транспортного уровня, протокол аутентификации и протокол соединения. Их задача _ обеспечивать безопасную сетевую службу наподобие удаленного login поверх небезопасной_ сети.

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

Протокол транспортного уровня предусматривает возможность сжатия данных. Этот протокол работает поверх соединения TCP/IP. Протокол аутентификации работает поверх протокола транспортного уровня, а протокол соединения ' поверх протокола аутентификации.

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

Клиент шлет запрос на обслуживание в первый раз, когда устанавливается безопасное соединение транспортного уровня ssh. Второй запрос направляется уже после завершения аутентификации пользователя (клиента).

Прежде чем анализировать протоколы ssh подробнее, следует определить понятие ключ хоста_. Каждый работающий с ssh хост, на котором может выполняться как клиент, так и сервер, может иметь не менее одного ключа, причем для шифрования допускаются различные криптографические алгоритмы. Несколько хостов могут иметь общий ключ хоста. Однако каждый хост должен иметь хотя бы один ключ, с которым работает каждый из требуемых алгоритмов работы с открытыми ключами. В проекте стандарта в настоящее время требуемый алгоритм только один _ DSS (Digital Signature Standard).

Ключ хоста-сервера используется при обмене открытыми ключами с целью проверки того, что клиент действительно общается с настоящим_ (а не подмененным) сервером. Для этого клиент должен знать открытый ключ хоста-сервера. Это знание реализуется в рамках одной из двух моделей.

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

Протоколом предусмотрена возможность отказа от проверки ключа хоста-сервера при самом первом обращении клиента к этому серверу. При этом соединение клиент-сервер будет защищено от пассивного прослушивания сети, но возникает опасность атаки типа человек в середине_ (man-in-the-middle), т. е. попытки временной подмены сервера. Если эта возможность используется, ключ хоста-сервера будет автоматически передан клиенту и сохранен в его локальном файле.

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

Интервал номеров Тип сообщений Протокол
Интервал номеров Тип сообщений Протокол
1-19 Транспортный уровень (общая часть) Транспортный
20-29 Переговоры клиента и сервера о выборе алгоритма
30-49 Специфические для метода обмена ключами
50-59 Протокол аутентификации (общая часть) Аутентификации
60-79 Протокол аутентификации (часть, специфическая для метода аутентификации)
80-89 Протокол соединения (общая часть) Cоединения
90-127 Cообщения, относящиеся к каналам
90-128 Резерв (для протоколов клиентов)
192-255 Локальные расширения



Таблица 1. Номера сообщений ssh и их назначение

Отдельного упоминания заслуживают вопросы увеличения трафика в связи с применением протоколов ssh. Ясно, что при передаче в сети больших пакетов дополнительная нагрузка, вызванная передачей управляющих заголовков ssh, невелика. Основное внимание следует обратить на приложения, для которых характерны короткие пакеты _ например, telnet. Минимальный размер заголовка TCP/IP равен 32 байта; минимальный же размер пакета при использовании ssh увеличится с 33 до (примерно) 51 байта.

Учитывая, что в Ethernet минимальная длина поля данных пакета равна 46 байт, дополнительный нагрузкой _ в 5 байт _ можно пренебречь. Наиболее существенным влияние ssh оказывается, вероятно, при использовании протокола PPP на низкоскоростных модемных соединениях, поскольку PPP сжимает заголовки TCP/IP. Однако существенный прогресс в скоростях передачи данных позволяет рассчитывать, что дополнительные задержки будут измеряться несколько миллисекундами и останутся незаметны человеку.

Наконец, несколько слов о кодировании сетевых адресов. Поскольку DNS ' ненадежный протокол, в ssh он не используется. При адресации применяются IP-адреса, причем в проекте заложена поддержка IPv6.

Все сообщения (пакеты) ssh содержат номер сообщения _ число от 1 до 255. Этот диапазон разбит на подинтервалы, причем разным уровням протокола ssh отвечают разные диапазоны.

Установка

При установке всякого бесплатно распространяемого по Internet программного продукта и ssh здесь не исключение ' часто приходится сталкиваться с тем, что используемая Вами версия ОС Unix чуть-чуть_ отличается от того, чего ждали от нее разработчики данного продукта. В результате программный продукт может оказаться не вполне работоспособен, или вовсе транслироваться с ошибками. Автор столкнулся с некоторыми особенностями установки ssh в средах SGI Irix и Linux; о них будет рассказано ниже. Формально наше изложение относится к ssh версии 1.2.26, если явно не оговорено противное (процедуры установки различных версий ничем кардинальным не отличаются).

После того, как дистрибутив ssh получен и разархивирован, процедура установки включает три простых стадии. На первой следует выполнить командный файл configure. Все основные особенности создаваемого варианта ssh задаются параметрами, указываемыми при запуске configure. Можно задать целый ряд ключей (операндов).

В частности, указывается, какие методы шифрования сеанса будут использованы при работе ssh (предлагается, в частности, IDEA, DES, тройной_ DES, ARCFOUR, BLOWFISH). Поскольку в ARCFOUR найдена дыра_, по умолчанию этот метод исключается, а остальные, наоборот, включены. Основным по умолчанию является IDEA, использующий 128-разрядные ключи. Если он исключен, основным алгоритмом становится 3DES, трехкратное последовательное DES-шифрование c 56-разрядным ключом. Учитывая несколько нашумевших вскрытий_ однократного DES-ключа, это выглядит разумной предосторожностью. Нужно отметить также метод BLOWFISH, который при той же длине ключа (от 32 до 448 разрядов) работает быстрее IDEA и DES. Используемый по умолчанию метод шифрования можно указать явно, установив нужное значение SSH_FALL BACK_CIPHER во включаемом файле ssh.h.

При запуске configure можно специфицировать поддержку Kerberos 5, cредств работы с сервером аутентификации TIS, с Secure Dynamics Secure ID card и т.д. Можно разрешить или запретить перенаправление портов TCP/IP клиентского и серверного хостов, а также ' отдельно _ перенаправление портов протокола X11.

Можно задать также замену стандартных команд rlogin и rsh соответствующими одноименными модулями из дистрибутива ssh. Тогда для соединений будет использоваться протокол ssh ' конечно, если удаленный компьютер его поддерживает (в противном случае после предупреждения будет осуществлен переход к обычным средствам rlogin/rsh).

Поскольку подразумеваемые значения ключей configure установлены весьма разумно, при вызове configure обычно не приходится задавать большого числа операндов и можно обойтись чем-то наподобие



./configure 'verbose
'with-libwrap=ПУТЬ 'with-X




где ПУТЬ задает путь к библиотеке libwarp.a, которая требуется при использовании tcp_wrapper (в этом случае кроме libwrap.a для установки ssh понадобится также файл tcp.h). Последний операнд указывает на работу с протоколом X11.

При работе configure используется Autoconf, известная программа с лицензией GNU, которая автоматически определяет массу характеристик установленного на локальном компьютере программного обеспечения. При наличии операнда verbose выполнение configure сопровождается выдачей информационных сообщений, позволяющих проконтролировать генерируемые параметры.

В результате выполнения сonfigure создает ряд новых файлов и каталогов, но главное для понимания процесса установки _ то, что при этом из файла-заготовки Makefile.in создается собственно Makefile.

На второй стадии установки запуск make приводит к компиляции всех основных модулей ssh. Наконец, третья стадия установки запускается по команде make install. При этом происходит запись порожденных программ в целевые_ каталоги (по умолчанию /usr/local/bin, /usr/local/sbin, /usr/local/man), а также создание нескольких файлов в каталоге /etc: ssh_config, sshd_config и ssh_host_key_pub.

В случае неудачи команда make distclean должна удалить все следы проделанного эксперимента_, кроме самого дистрибутива. Так написано в документации; на самом деле: в /usr/local/sbin и /usr/local/bin все остается.

В реальной жизни, конечно, возникают проблемы. Приведем некоторые конкретные примеры. Так, с ОС Irix не рекомендуется использовать версии ssh от 1.2.21 и более ранние из-за большого числа ошибок; желательно применять версии 1.2.25 и выше.

Поскольку применение ssh может вызвать задержки из-за загрузки процессора операциями шифрования, ssh желательно компилировать с оптимизацией. configure, кстати, имеет специальный операнд, разрешающий оптимизацию на уровне ассемблера. В качестве иллюстрации укажем, что для рабочей станции SGI O2 c микропроцессором R5000SC и ОС Irix 6.3, включение оптимизации Си-компилятора дает прирост производительности примерно на 25%. Однако есть сообщения, что такая оптимизация иногда вызывает ошибки при перенаправлении портов X11.

ОС ssh 1.2.26 ssh 2.0.9
SGI Irix 5.2, 5.3, 6.0.1 6.2-6.4 6.2 6.4
Digital Unix 4.0, 4.0a, 4.0b OSF/1: 3.0-3.2 4.0
Sun Solaris 2.3-2.6 SunOS 4.1 Solaris 2.5.1 2.6
HP-UX 9.x 10.x 10 20
IBM AIX 4.1 4.2 4.1 4.2
Linux Slackware 3.x RedHat 5.1 2.0.34
FreeBSD 1.x 2.x 3.0 2.x 3.0



Таблица 2. Некоторые поддерживаемые ssh операционные системы

В ssh 2.0.8 при трансляции в ОС Irix рекомендуется использовать Си-компилятор версии 7.2, поскольку он позволяет создать более производительную программу по сравнению с 7.1. Однако следует использовать cc 7.2.1, поскольку исходный_ Си-компилятор версии 7.2 дает неработоспособный ssh.

В качестве еще одной иллюстрации обсудим Linux. Для компиляции ssh не годится gcc популярной версии 2.7.2: нужна версия 2.7.2.3 или новее. В RedHat Linux для работы с аутентификацией RSA домашний каталог пользователя и его подкаталог .ssh не должны иметь разрешение на запись членами группы.

Файлы ssh

Обратимся сначала к образующимся после установки исполняемым файлам. Основных файлов два: cобственно демон sshd в /usr/local/sbin и клиент ssh в /usr/local/bin. В последнем каталоге располагается также модуль scp (ssh-аналог rcp) и ряд модулей с префиксом ssh_, среди которых отметим модули ssh_agent (аутентификационный агент, хранящий RSA-ключи аутентификации) и ssh_add, служащий для регистрации новых ключей в этом агенте. Кроме того, в /usr/local/bin имеется две важных вспомогательных утилиты: ssh-keygen и make-ssh-known-hosts.

Неисполняемые файлы ssh (кроме справочных файлов, помещаемых в /usr/local/man) располагаются в каталогe /etc и в домашних каталогах пользователей. В каталоге /etc расположены конфигурационные файлы sshd_config и ssh_config, задающие конфигурационные параметры соответственно sshd и ssh; эти файлы образуются автоматически по завершению установки.

В каталог /etc помещается информация о ключах. В файле ssh_host_key задается ключ хоста-сервеssh_host_key_pub) помещается в файл ssh_known_hosts. Естественно, его надо создавать самостоятельно.

В каталоге /etc образуется также рабочий файл ssh_random_seed, который автоматически модифицируется при запуске демона sshd. Наконец, в каталоге /etc могут располагаться еще два файла. Файл shosts.equiv является аналогом файла rhosts.equiv при работе с ssh. Файл sshrc выполняется при процедуре login перед вызовом оболочки.

Файлы, отражающие пользовательские настройки, располагаются в их домашних каталогах _ в подкаталоге .ssh. В этом подкаталоге располагается, в частности, RSA _ ключ пользователя (в файле identity) и соответствующий открытый ключ (в файле identity.pub). Кроме того, в каталоге .ssh может иметься коллекция открытых ключей удаленных_ пользователей (из их файлов identity.pub), находящаяся в файлe authorized.keys. Пользователь может создать также свой личный аналог файла /etc/ssh_known_hosts в файле known_hosts.

При первом вызове ssh (клиента) cоздается и автоматически корректируется при последующих вызовах файл random_seed. Собственные подразумеваемые параметры ssh можно задать в файле config. В файле rc можно указать действия, выполняемые при login до вызова пользовательской оболочки. Наконец, файл .shosts, аналог .rhosts при работе с ssh, располагается в домашнем каталоге пользователя.

Для генерации ключей заданной длины, как ключей хостов, так и ключей пользователей, применяется утилита ssh-keygen. Она автоматически вызывается в процессе установки для создания файлов, содержащих ключи хоста-сервера.

Типичный вызов ssh-keygen (квадратные скобки, как обычно, означают возможность опустить соответствующий операнд) выглядит так:

ssh-keygen [-b длина] [-N парольная_фраза] [-c комментарий]

позволяет создать файлы identity и identity.pub с RSA-ключами, длина которых задается операндом b. По умолчанию длина ключа равна 1024; она не должна быть меньше 512. Поле комментария по умолчанию генерируется в форме user@host. ПАРОЛЬНАЯ_ФРАЗА используется для шифрования личного ключа пользователя; ее рекомендуемая длина _ от 10 до 30 символов. При генерации ключа хоста ПАРОЛЬНАЯ_ФРАЗА должна отсутствовать; такой вызов автоматически происходит в процессе установки.

Утилита make-ssh-known-hosts представляет собой сценарий на языке Perl5 и служит для автоматизации создания файлов типа ssh_known_hosts. Если на компьютере Perl5 не установлен, подобные файлы придется создавать вручную.

Данная утилита обладает развитые средства взаимодействия с DNS-серверами и позволяет выполнять достаточно изощренный опрос этих серверов. Простейшая форма вызова имеет следующий вид:

make-ssh-known-hosts some.domain > /etc/ssh_known_hosts

Это позволяет найти и записать в файл ssh_known_hosts открытые ключи всех хостов домена some.domain. Утилита имеет, в частности, средства работы с WKS-записями DNS-сервера. Если в этих записях указан признак наличия ssh, то можно сразу отобрать только хосты, поддерживающие ssh. Аналогично можно отобрать хосты, поддерживающие telnet:

make-ssh-known-hosts some.domain Ё^wks=.*telnetё > our_hosts

Утилита может работать аналогичным образом с записями hinfo. Механизм ее работы следующий.

Получив от DNS-сервера список отобранных хостов, make-ssh-known-hosts пытается получить открытые ключи каждого из них. Для этого делается попытка соединиться на порт sshd (22 по умолчанию), и если соединение успешно, пытается выполнить на удаленном хосте команду cat /etc/ ssh_host_key.pub. Если это не удается, утилита проверяет, был ли получен ей в сеансе открытый ключ удаленного хоста; если да _ то он и используется (эту возможность можно отменить при вызове сценария).

Демон sshd

Запуск демона sshd обычно происходит автоматически при загрузке операционной системы из командного файла наподобие /etc/rc.local. Поскольку sshd должен сгенерировать ключ еще до установления соединения, что требует определенного времени, sshd не рекомендуют запускать через демон inetd. Однако, если процессор достаточно быстр, и длина ключа мала (меньше 512 разрядов), демон может периодически запускаться через inetd при каждой попытке соединиться на порт 22.

Напомним, что в целях безопасности не рекомендуют применять ключи короче 512 разрядов. При запуске sshd длину ключа можно указать в операнде -b (по умолчанию 768 разрядов). Подчеркнем, что здесь речь идет о ключе сервера (демона sshd), используемом в сеансе, а не о ключе хоста-сервера.

Запуск sshd с операндом -d включает режим отладки, что рекомендуется при проверке работоспособности, поскольку при этом выдается ряд информационных сообщений, позволяющих отслеживать происходящее. При нормальной эксплуатации этот режим следует отключить.

Параметры демон sshd читает из файла /etc/sshd_conf. В нем задаются, в частности, ссылки на файлы ssh_host_key и ssh_random_seed, длина ключа сервера, разрешенные методы шифрования, номер используемого sshd порта, уровень протоколирования работы sshd в системном журнале и др. Обычно этот файл является вполне подходящим, и необходимости его корректировать не возникает.

Клиент ssh

Клиент ssh cлужит в качестве замены командам rsh и rlogin. Типичная форма вызова ssh выглядит так:

ssh [-l имя_пользоваетля] ИМЯ_ХОСТА [команда]

Здесь ИМЯ_ПОЛЬЗОВАТЕЛЯ означает имя пользователя на удаленном хосте, с которым происходит соединение (задается операндом ИМЯ_ХОСТА). Операнд КОМАНДА, если он не опущен, указывает выполняемую на удаленном хосте команду.

Команда ssh имеет еще целый ряд операндов. В операнде -с можно указать метод шифрования (idea/blowfish/des/3des/arcfour); в операнде -p указывается номер порта. Операнд -v рекомендуется использовать для получения информации о том, что происходит в процессе установления сеанса, например, при возникновении каких-либо проблем, в первую очередь задержках в соединении.

Два операнда позволяют осуществлять перенаправление портов:

- L ПОРТ:ХОСТ:ПОРТХОСТА

позволяет перенаправить ПОРТ локального компьютера на ПОРТХОСТА удаленного компьютера, заданного в аргументе ХОСТ;

-R ПОРТ:ХОСТ:ПОРТХОСТА

осуществляет перенаправление ПОРТа на удаленном ХОСТе на порт локального хоста (последний аргумент).

Конфигурационные файлы клиента включают общий файл /etc/ssh_config и личные пользовательские config-файлы. Последние перекрывают_ действие общего файла, и их, в свою очередь, можно заменить явным заданием операндов ssh. В конфигурационном файле задаются такие параметры, как методы шифрования, возможность перенаправления портов, разрешение на использование обычных механизмов rsh/rlogin при невозможности ssh-аутентификации, номер используемого на удаленном сервере порта и др.

Взято с





Определение функции в DLL


Определение функции в DLL



Данная функция определяет присутствие нужной функции в библиотеке (dll) и, в случае нахождения искомой функции возвращает True, иначе False.

function FuncAvail (VLibraryname, VFunctionname: string; var VPointer: pointer): 
boolean; 
var 
  Vlib: tHandle; 
begin 
  Result := false; 
  VPointer := NIL; 
   if LoadLibrary(PChar(VLibraryname)) = 0 then 
      exit; 
   VPointer := GetModuleHandle(PChar(VLibraryname)); 
   if Vlib <> 0 then 
   begin 
    VPointer := GetProcAddress(Vlib, PChar(VFunctionname)); 
    if VPointer <> NIL then 
       Result := true; 
   end; 
end; 

Взято с Исходников.ru



Определение количества заданий в спулере печати


Определение количества заданий в спулере печати




Spooler печати Windows посылает WM_SPOOLERSTATUS каждый раз при добавлении и удалении заданий в очереди печати. В следующем примере показано как перехватить это сообщение:

type
TForm1= class(TForm)
    Label1: TLabel;
private
    { Private declarations }
    procedure WM_SpoolerStatus(var Msg : TWMSPOOLERSTATUS); message WM_SPOOLERSTATUS;
public
    { Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WM_SpoolerStatus(var Msg : TWMSPOOLERSTATUS);
begin
Lable1.Caption := IntToStr(msg.JobsLeft) +
            ' Jobs currenly in spooler';
msg.Result := 0;
end;

Автор:

Song

Взято из





Определение координат расположения TaskBar


Определение координат расположения TaskBar




uses..,ShellApi;

var
  AppBarData: TAppBarData;
  bAlwaysOnTop: Boolean; {Поверх окон}
  bAutoHide: boolean; {Авт. убирать с экрана}
  ClRect: TRect; {Клиентские области}
  Rect: TRect;
  Edge: UInt; {Местоположение TaskBar}

procedure DetectTaskBar;
begin
  AppBarData.hWnd := FindWindow('Shell_TrayWnd', nil);
  AppBarData.cbSize := sizeof(AppBarData);
  bAlwaysOnTop := (SHAppBarMessage(ABM_GETSTATE, AppBardata) and ABS_ALWAYSONTOP) < > 0;
  bAutoHide := (SHAppBarMessage(ABM_GETSTATE, AppBardata) and ABS_AUTOHIDE) < > 0;
  GetClientRect(AppBarData.hWnd, ClRect.rc);
  GetWindowRect(AppBarData.hwnd, rect);
  if (Rect.top > 0) then
    Edge := ABE_BOTTOM
  else if (Rect.Bottom < Screen.Height) then
    Edge := ABE_TOP
  else if Rect.Right < Screen.Width then
    Edge := ABE_LEFT
  else
    Edge := ABE_RIGHT;
end;


Взято с





Определение полного пути и имени файла DLL


Определение полного пути и имени файла DLL



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

uses Windows;

procedure ShowDllPath stdcall;
var
  TheFileName : array[0..MAX_PATH] of char;
begin
  FillChar(TheFileName, sizeof(TheFileName), #0);
  GetModuleFileName(hInstance, TheFileName, sizeof(TheFileName));
  MessageBox(0, TheFileName, 'The DLL file name is:', mb_ok);
end;


Взято с Исходников.ru



Определение типа базы данных


Определение типа базы данных




{usesдолжен включать в себя db, dbitypes, dbiprocs }

procedure TForm1.FormCreate(Sender: TObject);
var

  rDB: DBDesc;
begin

{ Первый аргумент DbiGetDatabaseDesc - имя псевдонима базы данных
типа PChar }
  Check(DbiGetDatabaseDesc('IBLOCAL', @rDB));
{ член szDbType структуры DBDesc содержит информацию о типе
базы данных и имеет тип PChar }
  ShowMessage('Database имеет тип: ' + StrPas(rDB.szDbType));

{ Совет: Если вам просто необходимо узнать -
SQL server это или нет, используйте свойсто TDatabase
IsSQLBased }
end;

OAmiry/Borland

Взято из

Советов по Delphi от


Сборник Kuliba