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

         

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


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



Each function listed below sets or retrieves date or time, or decodes/encodes date and time into or from a timestamp.



DbiBcdFromFloat:
Converts FLOAT data to binary coded decimal (BCD) format.

DbiBcdToFloat:
Converts binary coded decimal (BCD) data to FLOAT format.

DbiDateDecode:
Decodes DBIDATE into separate month, day and year components.

DbiDateEncode:
Encodes separate date components into date for use by DbiPutField and other functions.



DbiGetDateFormat:
Gets the date format for the current session.

DbiGetNumberFormat:
Gets the number format for the current session.

DbiGetTimeFormat:
Gets the time format for the current session.

DbiSetDateFormat:
Sets the date format for the current session.

DbiSetNumberFormat:
Sets the number format for the current session.

DbiSetTimeFormat:
Sets the time format for the current session.

DbiTimeDecode:
Decodes time into separate components (hours, minutes, milliseconds).

DbiTimeEncode:
Encodes separate time components into time for use by DbiPutField and other functions.

DbiTimeStampDecode:
Extracts separate encoded date and time components from the timestamp.

DbiTimeStampEncode:
Encodes the encoded date and encoded time into a timestamp.


Взято с

Delphi Knowledge Base




Работа с Foxpro


Работа с Foxpro



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







Работа с FTP


Работа с FTP



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







Работа с HTML, клиентскими скриптами


Работа с HTML, клиентскими скриптами



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











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


















Работа с HTTP


Работа с HTTP



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






Работа с ICQ


Работа с ICQ



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





Работа с IE, интерфейсами WebBrowser


Работа с IE, интерфейсами WebBrowser



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















































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




Работа с индексами


Работа с индексами



Each function listed below returns information about an index or indexes, or performs a task that affects an index, such as dropping it, deleting it, or adding it.



DbiAddIndex:
Creates an index on an existing table.

DbiCloseIndex:
Closes the specified index on a cursor.

DbiCompareKeys:
Compares two key values based on the current index of the cursor.

DbiDeleteIndex:
Drops an index on a table.

DbiExtractKey:
Retrieves the key value for the current record of the given cursor or from the supplied record buffer.

DbiGetIndexDesc:
Retrieves the properties of the given index associated with the cursor.

DbiGetIndexDescs:
Retrieves index properties.

DbiGetIndexForField:
Returns the description of any useful index on the specified field.

DbiGetIndexSeqNo:
Retrieves the ordinal number of the index in the index list of the specified cursor.

DbiGetIndexTypeDesc:
Retrieves a description of the index type.

DbiOpenIndex:
Opens the index for the table associated with the cursor.

DbiRegenIndex:
Regenerates an index to make sure that it is up-to-date (all records currently in the table
are included in the index and are in the index order).

DbiRegenIndexes:
Regenerates all out-of-date indexes on a given table.

DbiSwitchToIndex:
Allows the user to change the active index order of the given cursor.


Взято с

Delphi Knowledge Base




Работа с индексами Clipper'а


Работа с индексами Clipper'а




Посылаю кое-что из своих наработок:

NtxRO - Модуль чтения clipper-овских индексов. Удобен для доступа к данным
Clipper приложений. Предусмотрено, что программа может работать с
индексом даже если родное приложение производит изменение в индексе
NtxAdd - Средство формирования своих Clipper подобных индексов. Индексы
НЕ БУДУТ ЧИТАТЬСЯ Clipper-приложениями (кое-что не заполнил в
заголовке, очень было лениво, да и торопился)
До модуля удаления из Индекса ключей все никак не дойдут руки. Меня очень интересуют аналогичные разработки для индексов Fox-а Кстати реализация индексов Clipper наиболее близка из всех к тому, что описано у Вирта в "Алгоритмах и структурах данных"
Я понимаю, что мне могут возразить, что есть дескать Apollo и т.п., но я считаю что предлагаемая реализация наиболее удобна ТАК КАК ИНДЕКСЫ НЕ ПРИВЯЗАНЫ К НАБОРУ ДАННЫХ (а лишь поставляют физические номера записей) это позволяет делать кое-какие фокусы (например перед индексацией преобразовать значение какой нибудь функцией типа описанной ниже, не включать индексы для пустых ключевых значений в разреженных таблицах, строить индексы контекстного поиска, добавляя по нескольку значений на одну запись, строить статистики эффективности поиска различных ключевых значений (для фамилии Иванов например статистика будет очень плохой) и т.п.)

В файле Eurst.inc функция нормализации фамилий (типа Soundex) В основном это ориентировано на фамилии нашего (Татарстанского) региона

Файл Eurst.inc

varvrSynonm: integer = 0;
  vrPhFine: integer = 0;
  vrUrFine: integer = 0;
  vrStrSyn: integer = 0;

function fContxt(const s: ShortString): ShortString;
var i: integer;

  r: ShortString;
  c, c1: char;
begin r := '';
  c1 := chr(0);

  for i := 1 to length(s) do
    begin
      c := s[i];
      if c = 'Ё' then c := 'Е';
      if not (c in ['А'..'Я', 'A'..'Z', '0'..'9', '.']) then c := ' ';
      if (c = c1) and not (c1 in ['0'..'9']) then continue;
      c1 := c;
      if (c1 in ['А'..'Я']) and (c = '-') and (i < length(s)) and (s[i + 1] = ' ') then
        begin
          c1 := ' ';
          continue;
        end;
      r := r + c;
    end;

procedure _Cut(var s: ShortString; p: ShortString);
begin

  if Pos(p, s) = length(s) - length(p) + 1 then
    s := Copy(s, 1, length(s) - length(p));
end;

function _PhFace(const ss: ShortString): ShortString;
var r: ShortString;

  i: integer;
  s: ShortString;
begin r := '';
  s := ANSIUpperCase(ss);
  if length(s) < 2 then
    begin
      Result := s;
      exit;
    end;
  _Cut(s, 'ЕВИЧ');
  _Cut(s, 'ОВИЧ');
  _Cut(s, 'ЕВНА');
  _Cut(s, 'ОВНА');
  for i := 1 to length(s) do
    begin
      if length(r) > 12 then break;
      if not (s[i] in ['А'..'Я', 'Ё', 'A'..'Z']) then break;
      if (s[i] = 'Й') and ((i = length(s))
        or (not (s[i + 1] in ['А'..'Я', 'Ё', 'A'..'Z']))) then continue;
{ЕЯ-ИЯ Андриянов}
      if s[i] = 'Е' then
        if (i > length(s)) and (s[i + 1] = 'Я') then s[i] := 'И';
{Ж,З-С Ахметжанов}
      if s[i] in ['Ж', 'З'] then s[i] := 'С';
{АЯ-АЙ Шаяхметов}
      if s[i] = 'Я' then
        if (i > 1) and (s[i - 1] = 'А') then s[i] := 'Й';
{Ы-И Васылович}
      if s[i] in ['Ы', 'Й'] then s[i] := 'И';
{АГЕ-АЕ Зулкагетович, Шагиахметович, Шадиахметович}
      if s[i] in ['Г', 'Д'] then
        if (i > 1) and (i < length(s)) then
          if (s[i - 1] = 'А') and (s[i + 1] in ['Е', 'И']) then continue;
{О-А Арефьев, Родионов}
      if s[i] = 'О' then s[i] := 'А';
{ИЕ-Е Галиев}
      if s[i] = 'И' then
        if (i > length(s)) and (s[i + 1] = 'Е') then continue;
{Ё-Е Ковалёв}
      if s[i] = 'Ё' then s[i] := 'Е';
{Э-И Эльдар}
      if s[i] = 'Э' then s[i] := 'И';
{*ЯЕ-*ЕЕ Черняев}
{(И|С)Я*-(И|С)А* Гатиятуллин}
      if s[i] = 'Я' then
        if (i > 1) and (i < length(s)) then
          begin
            if s[i + 1] = 'Е' then s[i] := 'Е';
            if s[i - 1] in ['И', 'С'] then s[i] := 'А';
          end;
{(А|И|Е|У)Д-(А|И|Е|У)Т Мурад}
      if s[i] = 'Д' then
        if (i > 1) and (s[i - 1] in ['А', 'И', 'Е', 'У']) then s[i] := 'Т';
{Х|К-Г Фархат}
      if s[i] in ['Х', 'К'] then s[i] := 'Г';
      if s[i] in ['Ь', 'Ъ'] then continue;
{БАР-БР Мубракзянов}
      if s[i] = 'А' then
        if (i > 1) and (i > length(s)) then
          if (s[i - 1] = 'Б') and (s[i + 1] = 'Р') then continue;
{ИХО-ИТО Вагихович}
      if s[i] in ['Х', 'Ф', 'П'] then
        if (i > 1) and (i < length(s)) then
          if (s[i - 1] = 'И') and (s[i + 1] = 'О') then s[i] := 'Т';
{Ф-В Рафкат}
      if s[i] = 'Ф' then s[i] := 'В';
{ИВ-АВ Ривкат см. Ф}
      if s[i] = 'И' then
        if (i < length(s)) and (s[i + 1] = 'В') then s[i] := 'А';
{АГЕ-АЕ Зулкагетович, Сагитович, Сабитович}
      if s[i] in ['Г', 'Б'] then
        if (i > 1) and (i < length(s)) then
          if (s[i - 1] = 'А') and (s[i + 1] in ['Е', 'И']) then continue;
{АУТ-АТ Зияутдинович см. ИЯ}
      if s[i] = 'У' then
        if (i > 1) and (i < length(s)) then
          if (s[i - 1] = 'А') and (s[i + 1] = 'Т') then continue;
{АБ-АП Габдельнурович}
      if s[i] = 'Б' then
        if (i > 1) and (s[i - 1] = 'A') then s[i] := 'П';
{ФАИ-ФИ Рафаилович}
      if s[i] = 'А' then
        if (i > 1) and (i < length(s)) then
          if (s[i - 1] = 'Ф') and (s[i + 1] = 'И') then continue;
{ГАБД-АБД}
      if s[i] = 'Г' then
        if (i = 1) and (length(s) > 3) and (s[i + 1] = 'А') and (s[i + 2] = 'Б') and (s[i + 3] = 'Д') then continue;
{РЕН-РИН Ренат}
      if s[i] = 'Е' then
        if (i > 1) and (i < length(s)) then
          if (s[i - 1] = 'Р') and (s[i + 1] = 'Н') then s[i] := 'И';
{ГАФ-ГФ Ягофар}
      if s[i] = 'А' then
        if (i > 1) and (i < length(s)) then
          if (s[i - 1] = 'Г') and (s[i + 1] = 'Ф') then continue;
{??-? Зинатуллин}
      if (i > 1) and (s[i] = s[i - 1]) then continue;
      r := r + s[i];
    end;
  Result := r;
end;

Файл NtxAdd.pas

unit NtxAdd;

interface

uses classes, SysUtils, NtxRO;

type

  TNtxAdd = class(TNtxRO)
  protected
    function Changed: boolean; override;
    function Add(var s: ShortString; var rn: integer; var nxt: integer): boolean;
    procedure NewRoot(s: ShortString; rn: integer; nxt: integer); virtual;
    function GetFreePtr(p: PBuf): Word;
  public
    constructor Create(nm: ShortString; ks: Word);
    constructor Open(nm: ShortString);
    procedure Insert(key: ShortString; rn: integer);
  end;

implementation

function TNtxAdd.GetFreePtr(p: PBuf): Word;
var i, j: integer;

  r: Word;
  fl: boolean;
begin

  r := (max + 2) * 2;
  for i := 1 to max + 1 do
    begin fl := True;
      for j := 1 to GetCount(p) + 1 do
        if GetCount(PBuf(@(p^[j * 2]))) = r then fl := False;
      if fl then
        begin
          Result := r;
          exit;
        end;
      r := r + isz;
    end;
  Result := 0;
end;

function TNtxAdd.Add(var s: ShortString; var rn: integer; var nxt: integer): boolean;
var p: PBuf;

  w, fr: Word;
  i: integer;
  tmp: integer;
begin

  with tr do
    begin
      p := GetPage(h, (TTraceRec(Items[Count - 1])).pg);
      if GetCount(p) then
        begin
          fr := GetFreePtr(p);
          if fr = 0 then
            begin
              Self.Error := True;
              Result := True;
              exit;
            end;
          w := GetCount(p) + 1;
          p^[0] := w and $FF;
          p^[1] := (w and $FF00) shr 8;
          w := (TTraceRec(Items[Count - 1])).cn;
          for i := GetCount(p) + 1 downto w + 1 do
            begin
              p^[2 * i] := p^[2 * i - 2];
              p^[2 * i + 1] := p^[2 * i - 1];
            end;
          p^[2 * w] := fr and $FF;
          p^[2 * w + 1] := (fr and $FF00) shr 8;
          for i := 0 to length(s) - 1 do
            p^[fr + 8 + i] := ord(s[i + 1]);
          for i := 0 to 3 do
            begin
              p^[fr + i] := nxt mod $100;
              nxt := nxt div $100;
            end;
          for i := 0 to 3 do
            begin
              p^[fr + i + 4] := rn mod $100;
              rn := rn div $100;
            end;
          FileSeek(h, (TTraceRec(Items[Count - 1])).pg, 0);
          FileWrite(h, p^, 1024);
          Result := True;
        end
      else
        begin
          fr := GetCount(p) + 1;
          fr := GetCount(PBuf(@(p^[fr * 2])));
          w := (TTraceRec(Items[Count - 1])).cn;
          for i := GetCount(p) + 1 downto w + 1 do
            begin
              p^[2 * i] := p^[2 * i - 2];
              p^[2 * i + 1] := p^[2 * i - 1];
            end;
          p^[2 * w] := fr and $FF;
          p^[2 * w + 1] := (fr and $FF00) shr 8;
          for i := 0 to length(s) - 1 do
            p^[fr + 8 + i] := ord(s[i + 1]);
          for i := 0 to 3 do
            begin
              p^[fr + i + 4] := rn mod $100;
              rn := rn div $100;
            end;
          tmp := 0;
          for i := 3 downto 0 do
            tmp := $100 * tmp + p^[fr + i];
          for i := 0 to 3 do
            begin
              p^[fr + i] := nxt mod $100;
              nxt := nxt div $100;
            end;
          w := hlf;
          p^[0] := w and $FF;
          p^[1] := (w and $FF00) shr 8;
          fr := GetCount(PBuf(@(p^[(hlf + 1) * 2])));
          s := '';
          rn := 0;
          for i := 0 to ksz - 1 do
            begin
              s := s + chr(p^[fr + 8 + i]);
              p^[fr + 8 + i] := 0;
            end;
          for i := 3 downto 0 do
            begin
              rn := $100 * rn + p^[fr + i + 4];
              p^[fr + i + 4] := 0;
            end;
          nxt := FileSeek(h, 0, 2);
          FileWrite(h, p^, 1024);
          for i := 1 to hlf do
            begin
              p^[2 * i] := p^[2 * (i + hlf + 1)];
              p^[2 * i + 1] := p^[2 * (i + hlf + 1) + 1];
            end;
          for i := 0 to 3 do
            begin
              p^[fr + i] := tmp mod $100;
              tmp := tmp div $100;
            end;
          FileSeek(h, (TTraceRec(Items[Count - 1])).pg, 0);
          FileWrite(h, p^, 1024);
          Result := False;
        end;
    end;
end;

procedure TNtxAdd.NewRoot(s: ShortString; rn: integer; nxt: integer);
var p: PBuf;

  i, fr: integer;
begin

  p := GetPage(h, 0);
  for i := 0 to 1023 do
    p^[i] := 0;
  fr := (max + 2) * 2;
  p^[0] := 1;
  p^[2] := fr and $FF;
  p^[3] := (fr and $FF00) shr 8;
  for i := 0 to length(s) - 1 do
    p^[fr + 8 + i] := ord(s[i + 1]);
  for i := 0 to 3 do
    begin
      p^[fr + i] := nxt mod $100;
      nxt := nxt div $100;
    end;
  for i := 0 to 3 do
    begin
      p^[fr + i + 4] := rn mod $100;
      rn := rn div $100;
    end;
  fr := fr + isz;
  p^[4] := fr and $FF;
  p^[5] := (fr and $FF00) shr 8;
  nxt := GetRoot;
  for i := 0 to 3 do
    begin
      p^[fr + i] := nxt mod $100;
      nxt := nxt div $100;
    end;
  nxt := FileSeek(h, 0, 2);
  FileWrite(h, p^, 1024);
  FileSeek(h, 4, 0);
  FileWrite(h, nxt, sizeof(integer));
end;

procedure TNtxAdd.Insert(key: ShortString; rn: integer);
var nxt: integer;

  i: integer;
begin nxt := 0;
  if DosFl then key := WinToDos(key);
  if length(key) > ksz then key := Copy(key, 1, ksz);
  for i := 1 to ksz - length(key) do
    key := key + ' ';
  Clear;
  Load(GetRoot);
  Seek(key, False);
  while True do
    begin
      if Add(key, rn, nxt) then break;
      if tr.Count = 1 then
        begin
          NewRoot(key, rn, nxt);
          break;
        end;
      Pop;
    end;
end;

constructor TNtxAdd.Create(nm: ShortString; ks: Word);
var p: PBuf;

  i: integer;
begin

  Error := False;
  DeleteFile(nm);
  h := FileCreate(nm);
  if h > 0 then
    begin
      p := GetPage(h, 0);
      for i := 0 to 1023 do
        p^[i] := 0;
      p^[14] := ks and $FF;
      p^[15] := (ks and $FF00) shr 8;
      ks := ks + 8;
      p^[12] := ks and $FF;
      p^[13] := (ks and $FF00) shr 8;
      i := (1020 - ks) div (2 + ks);
      i := i div 2;
      p^[20] := i and $FF;
      p^[21] := (i and $FF00) shr 8;
      i := i * 2;
      max := i;
      p^[18] := i and $FF;
      p^[19] := (i and $FF00) shr 8;
      i := 1024;
      p^[4] := i and $FF;
      p^[5] := (i and $FF00) shr 8;
      FileWrite(h, p^, 1024);
      for i := 0 to 1023 do
        p^[i] := 0;
      i := (max + 2) * 2;
      p^[2] := i and $FF;
      p^[3] := (i and $FF00) shr 8;
      FileWrite(h, p^, 1024);
    end
  else
    Error := True;
  FileClose(h);
  FreeHandle(h);
  Open(nm);
end;

constructor TNtxAdd.Open(nm: ShortString);
begin

  Error := False;
  h := FileOpen(nm, fmOpenReadWrite or fmShareExclusive);
  if h > 0 then
    begin
      FileSeek(h, 12, 0);
      FileRead(h, isz, 2);
      FileSeek(h, 14, 0);
      FileRead(h, ksz, 2);
      FileSeek(h, 18, 0);
      FileRead(h, max, 2);
      FileSeek(h, 20, 0);
      FileRead(h, hlf, 2);
      DosFl := True;
      tr := TList.Create;
    end
  else
    Error := True;
end;

function TNtxAdd.Changed: boolean;
begin

  Result := (csize = 0);
  csize := -1;
end;

end.

Файл NtxRO.pas

unit NtxRO;

interface

uses Classes;

type TBuf = array[0..1023] of Byte;

  PBuf = ^TBuf;
  TTraceRec = class
  public
    pg: integer;
    cn: SmallInt;
    constructor Create(p: integer; c: SmallInt);
  end;
  TNtxRO = class
  protected
    fs: string[10];
    empty: integer;
    csize: integer;
    rc: integer; {Текущий номер записи}
    tr: TList; {Стек загруженных страниц}
    h: integer; {Дескриптор файла}
    isz: Word; {Размер элемента}
    ksz: Word; {Размер ключа}
    max: Word; {Максимальное кол-во элементов}
    hlf: Word; {Половина страницы}
    function GetRoot: integer; {Указатель на корень}
    function GetEmpty: integer; {Пустая страница}
    function GetSize: integer; {Возвращает размер файла}
    function GetCount(p: PBuf): Word; {Число элементов на странице}
    function Changed: boolean; virtual;
    procedure Clear;
    function Load(n: integer): PBuf;
    function Pop: PBuf;
    function Seek(const s: ShortString; fl: boolean): boolean;
    function Skip: PBuf;
    function GetItem(p: PBuf): PBuf;
    function GetLink(p: PBuf): integer;
  public
    Error: boolean;
    DosFl: boolean;
    constructor Open(nm: ShortString);
    destructor Destroy; override;
    function Find(const s: ShortString): boolean;
    function GetString(p: PBuf; c: SmallInt): ShortString;
    function GetRecN(p: PBuf): integer;
    function Next: PBuf;
  end;

function GetPage(h, fs: integer): PBuf;
procedure FreeHandle(h: integer);
function DosToWin(const ss: ShortString): ShortString;
function WinToDos(const ss: ShortString): ShortString;

implementation

uses Windows, SysUtils;

const MaxPgs = 5;
var Buf: array[1..1024 * MaxPgs] of char;

  Cache: array[1..MaxPgs] of record
    Handle: integer; {0-страница свободна}
    Offset: integer; {  смещение в файле}
    Countr: integer; {  счетчик использования}
    Length: SmallInt;
  end;

function TNtxRO.Next: PBuf;
var cr: integer;

  p: PBuf;
begin

  if h <= 0 then
    begin
      Result := nil;
      exit;
    end;
  while Changed do
    begin
      cr := rc;
      Find(fs);
      while cr > 0 do
        begin
          p := Skip;
          if GetRecN(p) = cr then break;
        end;
    end;
  Result := Skip;
end;

function TNtxRO.Skip: PBuf;
var cnt: boolean;

  p, r: PBuf;
  n: integer;
begin r := nil;

  cnt := True;
  with tr do
    begin
      p := GetPage(h, (TTraceRec(Items[Count - 1])).pg);
      while cnt do
        begin cnt := False;
          if (TTraceRec(Items[Count - 1])).cn > GetCount(p) + 1 then
            begin
              if Count <= 1 then
                begin
                  Result := nil;
                  exit;
                end;
              p := Pop;
            end
          else
            while True do
              begin
                r := GetItem(p);
                n := GetLink(r);
                if n = 0 then break;
                p := Load(n);
              end;
          if (TTraceRec(Items[Count - 1])).cn >= GetCount(p) + 1 then
            cnt := True
          else
            r := GetItem(p);
          Inc((TTraceRec(Items[Count - 1])).cn);
        end;
    end;
  if r <> nil then
    begin
      rc := GetRecN(r);
      fs := GetString(r, length(fs));
    end;
  Result := r;
end;

function TNtxRO.GetItem(p: PBuf): PBuf;
var r: PBuf;
begin

  with TTraceRec(tr.items[tr.Count - 1]) do
    r := PBuf(@(p^[cn * 2]));
  r := PBuf(@(p^[GetCount(r)]));
  Result := r;
end;

function TNtxRO.GetString(p: PBuf; c: SmallInt): ShortString;
var i: integer;

  r: ShortString;
begin r := '';

  if c = 0 then c := ksz;
  for i := 0 to c - 1 do
    r := r + chr(p^[8 + i]);
  if DosFl then r := DosToWin(r);
  Result := r;
end;

function TNtxRO.GetLink(p: PBuf): integer;
var i, r: integer;
begin r := 0;

  for i := 3 downto 0 do
    r := r * 256 + p^[i];
  Result := r;
end;

function TNtxRO.GetRecN(p: PBuf): integer;
var i, r: integer;
begin r := 0;

  for i := 3 downto 0 do
    r := r * 256 + p^[i + 4];
  Result := r;
end;

function TNtxRO.GetCount(p: PBuf): Word;
begin

  Result := p^[1] * 256 + p^[0];
end;

function TNtxRO.Seek(const s: ShortString; fl: boolean): boolean;
var r: boolean;

  p, q: PBuf;
  nx: integer;
begin r := False;

  with TTraceRec(tr.items[tr.Count - 1]) do
    begin
      p := GetPage(h, pg);
      while cn <= GetCount(p) + 1 do
        begin
          q := GetItem(p);
          if (cn > GetCount(p)) or (s < GetString(q, length(s))) or
            (fl and (s = GetString(q, length(s)))) then
            begin
              nx := GetLink(q);
              if nx <> 0 then
                begin
                  Load(nx);
                  r := Seek(s, fl);
                end;
              Result := r or (s = GetString(q, length(s)));
              exit;
            end;
          Inc(cn);
        end;
    end;
  Result := False;
end;

function TNtxRO.Find(const s: ShortString): boolean;
var r: boolean;
begin

  if h <= 0 then
    begin
      Result := False;
      exit;
    end;
  rc := 0;
  csize := 0;
  r := False;
  while Changed do
    begin
      Clear;
      Load(GetRoot);
      if length(s) > 10 then
        fs := Copy(s, 1, 10)
      else
        fs := s;
      R := Seek(s, True);
    end;
  Result := r;
end;

function TNtxRO.Load(N: integer): PBuf;
var it: TTraceRec;

  r: PBuf;
begin r := nil;

  if h > 0 then
    begin
      with tr do
        begin
          it := TTraceRec.Create(N, 1);
          Add(it);
        end;
      r := GetPage(h, N);
    end;
  Result := r;
end;

procedure TNtxRO.Clear;
var it: TTraceRec;
begin

  while tr.Count > 0 do
    begin
      it := TTraceRec(tr.Items[0]);
      tr.Delete(0);
      it.Free;
    end;
end;

function TNtxRO.Pop: PBuf;
var r: PBuf;

  it: TTraceRec;
begin r := nil;

  with tr do
    if Count > 1 then
      begin
        it := TTraceRec(Items[Count - 1]);
        Delete(Count - 1);
        it.Free;
        it := TTraceRec(Items[Count - 1]);
        r := GetPage(h, it.pg)
      end;
  Result := r;
end;

function TNtxRO.Changed: boolean;
var i: integer;

  r: boolean;
begin r := False;

  if h > 0 then
    begin
      i := GetEmpty;
      if i <> empty then r := True;
      empty := i;
      i := GetSize;
      if i <> csize then r := True;
      csize := i;
    end;
  Result := r;
end;

constructor TNtxRO.Open(nm: ShortString);
begin

  Error := False;
  h := FileOpen(nm, fmOpenRead or fmShareDenyNone);
  if h > 0 then
    begin
      fs := '';
      FileSeek(h, 12, 0);
      FileRead(h, isz, 2);
      FileSeek(h, 14, 0);
      FileRead(h, ksz, 2);
      FileSeek(h, 18, 0);
      FileRead(h, max, 2);
      FileSeek(h, 20, 0);
      FileRead(h, hlf, 2);
      empty := -1;
      csize := -1;
      DosFl := True;
      tr := TList.Create;
    end
  else
    Error := True;
end;

destructor TNtxRO.Destroy;
begin

  if h > 0 then
    begin
      FileClose(h);
      Clear;
      tr.Free;
      FreeHandle(h);
    end;
  inherited Destroy;
end;

function TNtxRO.GetRoot: integer;
var r: integer;
begin r := -1;

  if h > 0 then
    begin
      FileSeek(h, 4, 0);
      FileRead(h, r, 4);
    end;
  Result := r;
end;

function TNtxRO.GetEmpty: integer;
var r: integer;
begin r := -1;

  if h > 0 then
    begin
      FileSeek(h, 8, 0);
      FileRead(h, r, 4);
    end;
  Result := r;
end;

function TNtxRO.GetSize: integer;
var r: integer;
begin r := 0;

  if h > 0 then r := FileSeek(h, 0, 2);
  Result := r;
end;

constructor TTraceRec.Create(p: integer; c: SmallInt);
begin

  pg := p;
  cn := c;
end;

function GetPage(h, fs: integer): PBuf; {Протестировать отдельно}
var i, j, mn: integer;

  q: PBuf;
begin

  mn := 10000;
  j := 0;
  for i := 1 to MaxPgs do
    if (Cache[i].Handle = h) and
      (Cache[i].Offset = fs) then
      begin
        j := i;
        if Cache[i].Countr < 10000 then
          Inc(Cache[i].Countr);
      end;
  if j = 0 then
    begin
      for i := 1 to MaxPgs do
        if Cache[i].Handle = 0 then j := i;
      if j = 0 then
        for i := 1 to MaxPgs do
          if Cache[i].Countr <= mn then
            begin
              mn := Cache[i].Countr;
              j := i;
            end;
      Cache[j].Countr := 0;
      mn := 0;
    end;
  q := PBuf(@(Buf[(j - 1) * 1024 + 1]));
  if mn = 0 then
    begin
      FileSeek(h, fs, 0);
      Cache[j].Length := FileRead(h, q^, 1024);
    end;
  Cache[j].Handle := h;
  Cache[j].Offset := fs;
  Result := q;
end;

procedure FreeHandle(h: integer);
var i: integer;
begin

  for i := 1 to MaxPgs do
    if Cache[i].Handle = h then
      Cache[i].Handle := 0;
end;

function DosToWin(const ss: ShortString): ShortString;
var r: ShortString;

  i: integer;
begin r := '';

  for i := 1 to length(ss) do
    if ss[i] in [chr($80)..chr($9F)] then
      r := r + chr(ord(ss[i]) - $80 + $C0)
    else if ss[i] in [chr($A0)..chr($AF)] then
      r := r + chr(ord(ss[i]) - $A0 + $C0)
    else if ss[i] in [chr($E0)..chr($EF)] then
      r := r + chr(ord(ss[i]) - $E0 + $D0)
    else if ss[i] in [chr($61)..chr($7A)] then
      r := r + chr(ord(ss[i]) - $61 + $41)
    else if ss[i] in [chr($F0)..chr($F1)] then
      r := r + chr($C5)
    else
      r := r + ss[i];
  Result := r;
end;

function WinToDos(const ss: ShortString): ShortString;
var r: ShortString;

  i: integer;
begin r := '';

  for i := 1 to length(ss) do
    if ss[i] in [chr($C0)..chr($DF)] then
      r := r + chr(ord(ss[i]) - $C0 + $80)
    else if ss[i] in [chr($E0)..chr($FF)] then
      r := r + chr(ord(ss[i]) - $E0 + $80)
    else if ss[i] in [chr($F0)..chr($FF)] then
      r := r + chr(ord(ss[i]) - $F0 + $90)
    else if ss[i] in [chr($61)..chr($7A)] then
      r := r + chr(ord(ss[i]) - $61 + $41)
    else if ss[i] in [chr($D5), chr($C5)] then
      r := r + chr($F0)
    else
      r := r + ss[i];
  Result := r;
end;

end.


Взято из

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


Сборник Kuliba






Работа с INI файлами


Работа с INI файлами




Почему иногда лучше использовать INI-файлы, а не реестр?

1. INI-файлы можно просмотреть и отредактировать в обычном блокноте.
2. Если INI-файл хранить в папке с программой, то при переносе папки на другой компьютер настройки сохраняются. (Я еще не написал ни одной программы, которая бы не поместилась на одну дискету :)
3. Новичку в реестре можно запросто запутаться или (боже упаси), чего-нибудь не то изменить.
Поэтому для хранения параметров настройки программы удобно использовать стандартные INI файлы Windows. Работа с INI файлами ведется при помощи объекта TIniFiles модуля IniFiles. Краткое описание методов объекта TIniFiles дано ниже.

Constructor Create('d:\test.INI');
Создать экземпляр объекта и связать его с файлом. Если такого файла нет, то он создается, но только тогда, когда произведете в него запись информации.

WriteBool(const Section, Ident: string; Value: Boolean);
Присвоить элементу с именем Ident раздела Section значение типа boolean

WriteInteger(const Section, Ident: string; Value: Longint);
Присвоить элементу с именем Ident раздела Section значение типа Longint

WriteString(const Section, Ident, Value: string);
Присвоить элементу с именем Ident раздела Section значение типа String

ReadSection (const Section: string; Strings: TStrings);
Прочитать имена всех корректно описанных переменных раздела Section (некорректно описанные опускаются)

ReadSectionValues(const Section: string; Strings: TStrings);
Прочитать имена и значения всех корректно описанных переменных раздела Section. Формат :
имя_переменной = значение

EraseSection(const Section: string);
Удалить раздел Section со всем содержимым

ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
Прочитать значение переменной типа Boolean раздела Section с именем Ident, и если его нет, то вместо него подставить значение Default.

ReadInteger(const Section, Ident: string; Default: Longint): Longint;
Прочитать значение переменной типа Longint раздела Section с именем Ident, и если его нет, то вместо него подставить значение Default.

ReadString(const Section, Ident, Default: string): string;
Прочитать значение переменной типа String раздела Section с именем Ident, и если его нет, то вместо него подставить значение Default.

Free;
Закрыть и освободить ресурс. Необходимо вызвать при завершении работы с INI файлом

Property Values[const Name: string]: string;
Доступ к существующему параметру по имени Name

Пример :

Procedure TForm1.FormClose(Sender: TObject);
var
 IniFile:TIniFile;
begin
  IniFile := TIniFile.Create('d:\test.INI'); { Создали экземпляр объекта }
  IniFile.WriteBool('Options', 'Sound', True); { Секция Options: Sound:=true }
  IniFile.WriteInteger('Options', 'Level', 3); { Секция Options: Level:=3 }
  IniFile.WriteString('Options' , 'Secret password', Pass); 
   { Секция Options: в Secret password записать значение переменной Pass }
  IniFile.ReadSection('Options ', memo1.lines); { Читаем имена переменных}
  IniFile.ReadSectionValues('Options ', memo2.lines); { Читаем имена и значения }
  IniFile.Free; { Закрыли файл, уничтожили объект и освободили память }
end;

Источник: 

Примечание от Vit.
INI файлы имеют ограничение на размер (конкретно зависит от версии операционной системы), поэтому если нужна поддержка файлов более 64 Kb прийдётся воспользоваться сторонними библиотеками или самому работать с файлами как с текстом. Однако следует помнить, что для хранения больших массивов информации ini файлы представляют не самое удачное решение, при увеличении ini файлов до таких размеров следует подумать об альтернативных методах хранения информации: XML, файлы прямого доступа или базы данных.



Работа с Interbase


Работа с Interbase



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










































Работа с интернетом


Работа с интернетом



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


·
·  
·  








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





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




Работа с изображением в памяти


Работа с изображением в памяти




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

type 
   TarrRGBTriple=array[byte] of TRGBTriple; 
   ParrRGBTriple=^TarrRGBTriple; 
 
 
{организует битмэп размером SX,SY;true_color} 
procedure TMBitmap.Allocate(SX,SY:integer); 
var DC:HDC; 
begin 
  if BM<>0 then DeleteObject(BM);   {удаляем старый битмэп, если был} 
  BM:=0;  PB:=nil; 
  fillchar(BI,sizeof(BI),0); 
  with BI.bmiHeader do        {заполняем структуру с параметрами битмэпа} 
  begin 
    biSize:=sizeof(BI.bmiHeader); 
    biWidth:=SX;  biHeight:=SY; 
    biPlanes:=1;  biBitCount:=24; 
    biCompression:=BI_RGB; 
    biSizeImage:=0; 
    biXPelsPerMeter:=0;  biYPelsPerMeter:=0; 
 
    biClrUsed:=0;        biClrImportant:=0; 
 
    FLineSize:=(biWidth+1)*3 and (-1 shl 2); {размер строки(кратна 4 байтам)} 
 
    if (biWidth or biHeight)<>0 then 
     begin 
       DC:=CreateDC('DISPLAY',nil,nil,nil); 
{замечательная функция (см.HELP), возвращает HBITMAP, позволяет сразу разместить выделяемый битмэп в спроецированном файле,
что позволяет ускорять работу и экономить память при генерировании большого битмэпа} 
      BM:=CreateDIBSection(DC,BI, DIB_RGB_COLORS, pointer(PB), nil, 0); 
 
       DeleteDC(DC);  {в PB получаем указатель на битмэп-----^^} 
       if BM=0 then Error('error creating DIB'); 
     end; 
  end; 
end; 
 
{эта процедура загружает из файла true-color'ный битмэп} 
procedure TMBitmap.LoadFromFile(const FileName:string); 
var HF:integer; {file handle} 
    HM:THandle; {file-mapping handle} 
    PF:pchar;   {pointer to file view in memory} 
    i,j:integer; 
    Ofs:integer; 
begin 
{открываем файл} 
  HF:=FileOpen(FileName,fmOpenRead or fmShareDenyWrite); 
 
  if HF<0 then Error('open file '''+FileName+''''); 
  try 
{создаем объект-проецируемый файл} 
    HM:=CreateFileMapping(HF,nil,PAGE_READONLY,0,0,nil); 
    if HM=0 then Error('cannot create file mapping'); 
   try 
{собственно проецируем объект в адресное } 
       PF:=MapViewOfFile(HM,FILE_MAP_READ,0,0,0); 
{получаем указатель на область памяти, в которую спроецирован файл} 
       if PF=nil then Error('cannot create map view of file'); 
      try 
{работаем с файлом как с областью памяти через указатель PF} 
 
         if PBitmapFileHeader(PF)^.bfType<>$4D42 then  Error('file format'); 
         Ofs:=PBitmapFileHeader(PF)^.bfOffBits; 
         with PBitmapInfo(PF+sizeof(TBitmapFileHeader))^.bmiHeader do 
         begin 
           if (biSize<>40) or (biPlanes<>1) then Error('file format'); 
           if (biCompression<>BI_RGB) or 
              (biBitCount<>24) then Error('only true-color BMP supported'); 
{выделяем память под битмэп} 
           Allocate(biWidth,biHeight); 
         end; 
 
         for j:=0 to BI.bmiHeader.biHeight-1 do 
           for i:=0 to BI.bmiHeader.biWidth-1 do 
{Pixels - это property, возвр. указатель на соотв. RGBTriple в битмэпе} 
              Pixels[i,j]^.Tr:=ParrRGBTriple(PF+j*FLineSize+Ofs)^[i]; 
      finally 
        UnmapViewOfFile(PF); 
      end; 
   finally 
     CloseHandle(HM); 
   end; 
  finally 
    FileClose(HF); 
  end; 
end; 
 
{эта функция - реализация Pixels read} 
function TMBitmap.GetPixel(X,Y:integer):PRGB; 
 
begin 
  if (X>=0) and (Xand 
     (Y>=0) and (Ythen Result:=PRGB(PB+(Y)*FLineSize+X*3) 
  else Result:=PRGB(PB); 
end; 

Если у вас на форме есть компонент TImage, то можно сделать так:

var BMP:TMBitmap; 
    B:TBitmap; 
... 
    BMP.LoadFromFile(..); 
    B:=TBitmap.Create; 
    B.Handle:=BMP.Handle; 
    Image1.Picture.Bitmap:=B; 

и загруженный битмэп появится на экране.


Alexander Burnashov
E-mail alex@arta.spb.su
(2:5030/254.36)




Работа с клиентскими наборами данных (DBExpress)


Работа с клиентскими наборами данных (DBExpress)




Введение
В данной части будет рассмотрено применение клиентских наборов данных в dbExpress. Согласно иерархии классов в Kylix к клиентским наборам данных относятся классы TSQLClientDataSet и TClientDataSet. Последний из них является частью технологии MIDAS. Так как на сегодняшний день поддержка данной технологии в Kylix до конца не реализована, то основное внимание мы уделим рассмотрению TSQLClientDataSet.

Компоненты класса TSQLClientDataSet предназначены для создания двухзвенных приложений клиент сервер. Так же как и однонаправленные наборы данных, они используются для работы с сервером БД через TSQLConnection. С другой стороны многие из методов и событий класса TSQLClientDataSet характерны для клиентского датасета в технологии MIDAS. На самом деле TSQLClientDataSet - это гибрид, содержащий в себе объекты однонаправленного набора данных, клиентский набор данных и объект провайдера для применения внесенных изменений на сервере БД. "Запихивание под капот" этих объектов позволило существенно упростить разработку двухзвенных приложений баз данных в dbExpress.

Простейший проект
Работа с TSQLClientDataSet будет проиллюстрирована на примере простой базы данных служащих организации. В качестве сервера БД выбран Interbase 6, т.к он входит в поставку Kylix. Предварительно необходимо создать базу данных с таблицей EMPLOYEERS, описанной следующим образом:

/*Table: EMPLOYEERS, Owner: SYSDBA */
CREATE TABLE "EMPLOYEERS" 
(
  "ID"   INTEGER NOT NULL,
  "NAME"   VARCHAR(200) NOT NULL,
 PRIMARY KEY ("ID")
);
СREATE GENERATOR "EMP_GEN";
SET TERM ^ ;
/* Triggers only will work for SQL triggers */
CREATE TRIGGER "EMPLOYEERS_BEFORE_INS" FOR "EMPLOYEERS" 
ACTIVE BEFORE INSERT POSITION 0
AS
   BEGIN
          NEW.ID = GEN_ID(EMP_GEN,1);
   END
 ^
COMMIT WORK ^
SET TERM ;^

Вставим несколько записей в созданную таблицу. Текст запроса на вставку в таблицу выглядит так:
Insert into Employeers (Name) values 'Петов';
Insert into Employeers (Name) values 'Сидоров';

Далее запустим IDE Kylix и создадим новое приложение. На главной форме приложения разместим следующие компоненты с закладки dbExpress и установим для них нижеуказанные свойства
sc_conn:TDBConnection - настроить для соединения с созданной БД. (как это сделать см. "Коннект - есть коннект"). Св-во Connected - установить true.
scd_emp:TSQLClientDataSet
DBConnection - sc_conn
CommandText - select ID,NAME from EMPOYEERS


Двойным кликом мыши вызовем редактор полей. В редакторе полей правой кнопкой мыши вызовем всплывающее меню и в нем выберем пункт Add all fields. При этом поля набора данных будут определены явным образом. Выберем поле ID и установим его свойство Required в false, чтобы снять необходимость ручного ввода значения ID при вставке пользователем новой записи. После этого св-во Connected компонента sc_conn установим в false.

ds_src:TDataSource
      DataSet:scd_emp
DBNavigator1:TDBNavigator
      DataSource - ds_src
      Align - alTop
Panel1:TPanel
      Align - alBottom
      Caption - ""(пустая строка)
DBGrid1:TDBGrid
      DataSource - ds_src
      Align - alTop

На Panel1 разместим 4(Button) кнопки c именами b_connect, b_disconnect, b_count, b_fetch (заголовки - Caption - connect, disconnect, get count, fetch all соответсвтенно )и одну надпись (Label). На событие onClick кнопки b_connect навесим обработчик со следующим кодом

Sc_conn.Connected:=true;
Scd_emp.Active :=true;
На событие onClick кнопки b_disconnect навесим обработчик со следующим кодом
Sc_conn.Connected:=false;
Scd_emp.Active :=false;
Назначение размещенных компонентов следующее
Sc_conn - соединение с базой данных
Sc_emp - набор данных для работы с таблицей БД employers
Ds_src - представление данных sc_emp для компонентов пользовательского интерфейса "чувствительных" к данным.
Запустим на выполнение наш проект, при этом предполагается, что сервер interbase уже запущен. При нажатии кнопки b_connect в сетке данных (DBGrid) можно будет видеть записи таблицы employeers.

Навигация по записям
Методы навигации по записям аналогичны однонаправленным наборам данных.

Добавление, удаление и редактирование записей
Для добавления записей существуют четыре метода
Append -Добавление пустой записи в конец набора данных. Курсор помещается на добавленную запись и набор данных переходит в режим редактирования
Insert - Добавление пустой записи в текущую позицию набора данных. Курсор помещается на добавленную запись и набор данных переходит в режим редактирования.
AppendRecord(const Values: array of const) - Добавление записи в конец набора данных. Поля передаются через параметр Values
InsertRecord(const Values: array of const) Добавление записи в текущую позицию набора данных. Поля передаются через параметр Values
Примеры добавления записей:
// Использование Append
scd_emp.Append;
scd_emp.FieldByName('ID').Value:=-1;
scd_emp.FieldByName('Name').Value:='Петров';
scd_emp.Post;

// Использование AppendRecord
scd_emp.AppendRecord([1,'Петров']);

Определены несколько событий, связанных с вставкой новой записи

BeforeInsert - Событие, генерируемое перед вставкой новой записи в набор данных.
AfterInsert - Событие, генерируемое после вставкой новой записи в набор данных
OnNewRecord - Событие, генерируемое при вставке новой записи в набор данных

При необходимости отменить вставку записи внутри обработчика события можно вызвать метод Abort.
Порядок вызова событий

BeforeInsert
OnNewRecord
AfterInsert
Для удаления текущей записи предназначен метод Delete, события BeforeDelete и AfterDelete генерируются до и после удаления записи соответственно.
Пример:


scd_emp.Delete;
Перевод набора данных в режим редактирования осуществляется вызовом метода Edit. При этом проверить доступность редактирования можно, проанализировав свойство CanModify. Еще одним полезным методом является метод CheckBrowseMode. Данный метод автоматически подтверждает или отменяет сделанные изменения перед тем, как будет осуществлен переход на следующую запись в наборе данных.

События BeforeEdit и AfterEdit возникают соответственно перед и после редактирования записи.

Подтверждение и откат сделанных изменений
Так как клиентский набор данных буферизирует сделанные изменения, то применяется двухступенчатое подтверждение сделанных изменений. Первая ступень - это запись сделанных изменений в буфер набора данных, вторая - запись изменений из буфера в сервер БД.

Запись изменений в буфер осуществляется вызовом метода Post. События BeforePost и AfterPost генерируются перед и после подтверждения изменений. Многие из компонентов пользовательского интерфейса для работы с данными вызывают метод Post автоматически при переходе на следующую запись набора данных.

Отмена записи в буфер набора данных осуществляется вызовом метода Cancel. События BeforeCancel и AfterCancel генерируются перед и после подтверждения изменений.

Изменения, сделанные в буфере, SQLClientDataSet хранит в свойстве Delta. Количество изменений хранится в свойстве ChangeCount. Запись сделанных изменений из буфера в БД осуществляется вызовом ApplyUpdates. В качестве параметра функции передается максимальное количество ошибок, допустимых до завершения метода. Функция возвращает количество возникших ошибок. Если в результате применения изменений количество ошибок не превысило заданного, то успешно переданные записи удаляются из свойства Delta (т.е считаются переданными на сервер БД), иначе все записи считаются не переданными.
Пример:

// Передача изменений из буфера в БД
if scd_emp.ChargeCount > 0 then
  if scd_emp.ApplyUpdates(10) > 0 then
    Application.MessageBox('Обнаружены ошибки');

При вызове ApplyUpdates SQLClientDataSet генерирует набор SQL операторов для передачи каждой вставленной, удаленной и измененной записи в БД.

При передаче изменений на сервер БД возникает задача определения соответствия измененной записи из буфера набора данных и записи в БД (т.е формирования части where SQL запроса). Свойство UpdateMode определяет данный критерий. Возможные значения св-ва приведены ниже

upWhereAll - для поиска применяется вся совокупность полей набора - режим по умолчанию
upWhereChanged - только поля, отмеченные как ключевые и поля содержащие изменения применяются для поиска.
UpWhereKeyOnly - только поля, отмеченные как ключевые, применяются для поиска. Поля набора данных имеют свойство ProviderFlags, определяющее поведение поля при формировании текста запроса. Могут быть установлены следующие флаги:
pfInUpdate - поле включается в SQL предложение UPDATE - т.е может быть обновлено
pfInWhere - поле включается в в SQL предложение Where в режиме обновления upWhereAll или upWhereChanged
pfInKey - поле включается в в SQL предложение Where в режиме обновления UpWhereKeyOnly
pfHidden - Поле включается в пакет данных для обеспечения уникальности записи, оно не может использоваться набором данных.
Наличие события OnUpdateData позволяет установить параметры обновления для каждой записи, передаваемой на сервер БД.

Откат всех сделанных изменений осуществляется с помощью метода CancelUpdates. Данный метод очищает св-во Delta, таким образом, отменяя все изменения в буфере набора данных.

Откат последней выполненной операции выполняется вызовом UndoLastChange. Передача True в качестве параметра метода UndoLastChange заставляет курсор перемещаться на откатываемую запись.

Но и это еще не все! Можно откатывать назад на произвольное количество операций (здесь под операцией понимается вставка, редактирование, удаление). Для этого существуют так называемые точки сохранения (SavePoint).
Техника такая:

Сохраняем точку. SP:=Client.SavePoint; (здесь SP:integer)
Делаем все, что заблагорассудится - вставка, удаление, редактирование
Восстанавливаем Client.SavePoint:=SP; и как будто ничего не было :))
Если немного помозговать, то используя точки сохранения, можно организовать не только Undo, но и Redo.

Осталось внести некоторые доработки в наш проект, чтобы сделанные изменения были отправлены на сервер БД. Для этого выполним следующие действия:
1. Объявим глобальную переменную id типа integer. Делается это в секции var модуля главной формы, данная секция будет выглядеть так

var
  Form1:Tform1;
Id:integer; // Счетчик для поля id, объявленный нами

2. В обработчике события AfterPost scd_emp инициализируем переменную id
id:=-1;

3. В обработчике BeforePost scd_emp используем id для заполнения поля id фиктивным значением (реально значение присваивается на сервере).
If scd_empID.IsNull then
  Begin
    Scd_empID.Value:=id;
    Dec(id);
  End;

4. В обработчике события BeforeRefresh scd_emp организуем отправку данных на сервер.
if scd_emp.ChangeCount > 0 then
  if scd_emp.ApplyUpdates(0) > 0 then
    Abort
  else
    id:=-1;

Запустим полученное приложение, попробуем вводить или изменять записи - до нажатия кнопки обновления DBNavigator1 все наши изменения не будут отражаться на сервере БД. Закрытие приложения с изменениями, не отправленными на сервер, приводят к потере этих изменений.

Обработка ошибок
Обработка ошибок также делится на обработку ошибок работы с буфером и обработку ошибок передачи данных на сервер БД. Для обработки ошибок вставки, удаления и редактирования в компоненте TSQLDataSet существуют несколько видов событий

OnDeleteError - Возникает при наличии ошибок удаления записи
OnEditError - Возникает при наличии ошибок редактирования или вставки записи
OnPostError - Возникает при наличии ошибок записи сделанных изменений в буфер клиентского набора данных

Обработчики вышеперечисленных событий в качестве одного из параметров получают параметр Action типа TDataAction. Изменяя значение этого параметра в обработчике можно варьировать реакцию на произошедшую ошибку. Возможные значения

daFail - прервать операцию и выдать сообщение об ошибке (поведение по умолчанию)
daAbort - прервать операцию без выдачи сообщения об ошибке
daRetry - повторить попытку, предполагается, что обработчик события предварительно пытается скорректировать запись, вызвавшую ошибку.
Клонирование таблицы
Описано далее в разделе Работа с локальными базами данных в Kylix.
Работа с локальными базами данных в Kylix
Под локальными мы будем понимать базы данных, файлы которых расположены в файлах на локальном диске компьютера или в локальной сети. Доступ к этим файлам осуществляется приложением напрямую.

В Delphi 5, продолжателем которой является Kylix, для работы с локальными базами данных использовалось несколько подходов.

Использование библиотек BDE, ADO, ODBC для доступа к локальным базам формата DBase, Paradox.
Использование TСlientDataSet для работы с локальными базами данных формата cds или xml. Форматы данных файлов являются изобретением Borland.
В Kylix разработка компонентов для создания локальных баз данных первого типа отдана на откуп разработчикам сторонних фирм. Связано это прежде всего с тем, что данные форматы данных являются отмирающими, тем более что конвертировние их в формат xml не вызывает больших затруднений.

Второй тип баз данных, получивший наименование MyBase, предоставляет дополнительные возможности, такие как

Возможность сортировки данных по полям без создания дополнительных файлов индексов.
Возможность ведения списка изменений и отката сделанных изменений
Возможность создания агрегатов на основе данных таблицы.
Возможность совместного использования одних и тех же данных несколькими датасетами.
Совместимость с Delphi5 (не говоря уже о Delphi 6)
Для иллюстрации всего вышесказанного создадим приложение для просмотра и редактирования заказов.
Создание заготовки приложения. Меню File/New Application создаст проект с пустой формой. Добавим модуль данных - File/New. В открывшемся диалоге выбрать пункт DataModule.

Создание файла базы данных. В модуль данных поместим компонент ClientDataSet с закладки DataAccess. Св-во Name установим - Clients. Данный датасет будет хранить информацию о заказчиках. Для создания файла базы данных необходимо указать поля и их типы. Сделать это можно двумя способами:
a) Определить FieldDefs
b) Создать объекты полей явным образом.


Лично я предпочитаю определить FieldDefs, а затем на их основе создать объекты полей :))

Итак, двойной клик на св-ве FieldDefs компонента Clients откроет диалог работы с определениями полей. Добавим следующие определения полей

ID ftAutoInc 0
Name ftString 50

Правой кнопкой мышки кликнем на Clients и выберем в выпадающем меню пункт CreateDataSet, а затем Save To MyBase Xml UTF-8 table. В появившемся диалоге укажем имя xml файла, который будет хранить данные о клиентах - Clients.xml.

Было бы неплохо, чтобы при старте программы ClientDataSet читал данные из созданного нами xml файла. Для этого св-во FileName должно быть равно полному имени (с путем) xml файла. Для Clients это /путь к файлу/Clients.xml.

Теперь определим поля явно на основе FieldDefs. Двойной клик на Clients, в диалоге правой кнопкой мыши вызываем контекстное меню, выбираем пункт Add all fields. Затем DataSource - ds_Clients, разместим в модуле данных и свяжем c Clients (св-во DataSet компонента ds_Clients установим равным Clients).

Формат xml таблицы БД, откат изменений
Посмотрим, как внутри устроен xml файл базы данных. После создания датасета типичный файл БД выглядит так:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
<DATAPACKET Version="2.0">
 <METADATA>
<FIELDS>
<FIELD attrname="ID" fieldtype="i4" readonly="true" SUBTYPE="Autoinc" /> 
<FIELD attrname="Name" fieldtype="string" WIDTH="50" /> 
</FIELDS>
<PARAMS DEFAULT_ORDER="" AUTOINCVALUE="1" /> 
</METADATA>
<ROWDATA /> 
</DATAPACKET>

Строка 1 <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
В строке 1 расположен заголовочный тэг
Строка 2 <DATAPACKET Version="2.0">
Корневой тэг документа, а вот дальше и начинаются теги, на которые стоит обратить внимание, в частности на строки 3 и 10.
Строка 3 <METADATA>
Строка 10<ROWDATA />

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

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

Теперь давайте запустим наше приложение, вставим в таблицу новую запись, закроем приложение и посмотрим как изменился xml файл.

Изменился тег PARAMS, теперь он выглядит так:

<PARAMS CHANGE_LOG="1 0 4" AUTOINCVALUE="2" DEFAULT_ORDER="" />
и тег ROWDATA стал непустым:
<ROWDATA>
<ROW RowState="4" ID="1" Name="e?AI?" /> 
</ROWDATA>

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

Если же Вам совсем не нужно, чтобы журнал велся, в runtime установите св-во LogChanges := false.

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

Клонирование таблицы
Поскольку TClientDataSet держит данные из таблицы в памяти, появилась возможность совместного использования одних данных двумя датасетами. Клонирование осуществляется вызовом метода CloneCursor

Procedure CloneCursor(Source:TCustomClientDataSet;Reset:Boolean;KeepSettings:Boolean = false)
Параметр Source - источник клонированных данных
Параметры Reset и KeepSettings определяют установку св-в фильров, индексы, Read Only, MasterSource, MasterFields. Когда оба параметра fasle указанные св-ва копируются из датасета-источника, Reset:=true - данные св-ва сбрасываются, KeepSettings:=true - остаются без изменений, при этом совместимость их с данными источника клонирования остается на совести программиста.

Установка отношений главный - подчиненный (master-detail)
Первый из способов - это задание св-в MasterSource и MasterFields. Этот способ традиционен еще в Delphi и мы рассматривать его тут не будем - читайте книжки.

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

Сначала очистим датасет Clients - щелкнем правой кнопкой мыши и в контекстном меню выберем - Clear Data.

Введем дополнительное FieldDefs Orders - типа ftDataSet. Данный тип поля предназначен для хранения внутри себя датасетов. Набор полей вложенного датасета определяется в свойсвте ChildDefs. Определим в ChildDefs следующие поля Имя (Name) Тип(Type) Размер(Size)
ID FtAutoInc 0
OrderName ftString 20
Price ftCurrency 0
ID - счетчик, OrderName - описание заказа, Price - цена заказа .

Осталось только создать на основе созданных определений создать датасет (щелкнув правой кнопкой и выбрав Create DataSet), сохранить в файл (Save to MyBase xml table) и на основе этих определений явно создать поля (двойной клик на Clients, правая кнопка мыши - add all fields). Открыв созданный xml файл мы увидим следующее

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
- <DATAPACKET Version="2.0">
- <METADATA>
- <FIELDS>
  <FIELD attrname="ID" fieldtype="i4" readonly="true" SUBTYPE="Autoinc" /> 
  <FIELD attrname="Name" fieldtype="string" WIDTH="50" /> 
- <FIELD attrname="Orders" fieldtype="nested">
- <FIELDS>
  <FIELD attrname="ID" fieldtype="i4" SUBTYPE="Autoinc" /> 
  <FIELD attrname="OrderName" fieldtype="string" WIDTH="20" /> 
  <FIELD attrname="Price" fieldtype="r8" SUBTYPE="Money" /> 
  </FIELDS>
  <PARAMS AUTOINCVALUE="1" /> 
  </FIELD>
  </FIELDS>
  <PARAMS DEFAULT_ORDER="" AUTOINCVALUE="1" /> 
  </METADATA>
  <ROWDATA /> 
  </DATAPACKET>

Нетрудно убедиться в том, что поле Orders содержит в себе описание подчиненной таблицы. При этом в сетке данных DBGrid1, расположенной на главной форме, появился новый столбец Orders. При запуске приложения и попытке редактирования этого поля автоматически открывается форма для редактирования вложенного набора данных.

Другим способом организации взаимодействия с вложенным датасетом является размещение в модуле данных дополнительного ClientDataSet. Поместим в модуль данных еще один компонент типа TClientDataSet, установив его имя Orders. Св-ву DataSetField компонента Orders из выпадающего списка присвоим значение ClientsOrders. Все теперь пользуясь компонентом Orders можно просматривать и редактировать вложенный датасет.

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



Автор: Mike Goblin

Взято из

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






Работа с коллекциями


Работа с коллекциями



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






Работа с коллекциями - сохранение и загрузка


Работа с коллекциями - сохранение и загрузка




unitDelphiPt;

interface

uses
  Classes, Graphics;

type
  TDDHPoint = class (TCollectionItem)
  private
    fX, fY: Integer;
  public
    Text: string;
    procedure WriteText (Writer: TWriter);
    procedure ReadText (Reader: TReader);
    procedure DefineProperties (Filer: TFiler); override;
    procedure Paint (Canvas: TCanvas);
    procedure Assign (Pt: TPersistent); override;
  published
    property X: Integer read fX write fX;
    property Y: Integer read fY write fY;
  end;

  TWrapper = class (TComponent)
  private
    FColl: TCollection;
  published
    property MyColl: TCollection read FColl write FColl;
  public
    constructor Create (Owner: TComponent); override;
    destructor Destroy; override;
  end;

implementation

// TWrapper constructor and destructor

constructor TWrapper.Create (Owner: TComponent);
begin
  inherited Create (Owner);
  FColl := TCollection.Create (TDDHPoint);
end;

destructor TWrapper.Destroy;
begin
  FColl.Clear;
  FColl.Free;
  inherited Destroy;
end;


// class TDDHPoint methods

procedure TDDHPoint.WriteText (Writer: TWriter);
begin
  Writer.WriteString (Text);
end;

procedure TDDHPoint.ReadText (Reader: TReader);
begin
  Text := Reader.ReadString;
end;

procedure TDDHPoint.DefineProperties (Filer: TFiler);
begin
  Filer.DefineProperty (
    'Text', ReadText, WriteText, (Text <> ''));
end;

procedure TDDHPoint.Paint (Canvas: TCanvas);
begin
  Canvas.Ellipse (fX - 3, fY - 3, fX + 3, fY + 3);
  Canvas.TextOut (fX + 5, fY + 5, Text);
end;

procedure TDDHPoint.Assign (Pt: TPersistent);
begin
  if Pt is TDDHPoint then
  begin
    fx := TDDHPoint (Pt).fX;
    fY := TDDHPoint (Pt).fY;
    Text := TDDHPoint (Pt).Text;
  end
  else
    // raise an exception
    inherited Assign (pt);
end;

//initialization
//RegisterClass (TWrapper);
end.
 



unit PersForm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Buttons, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Label1: TLabel;
    Edit1: TEdit;
    SpeedButtonLoad: TSpeedButton;
    SpeedButtonSave: TSpeedButton;
    OpenDialog1: TOpenDialog;
    SaveDialog1: TSaveDialog;
    procedure FormCreate(Sender: TObject);
    procedure SpeedButtonSaveClick(Sender: TObject);
    procedure SpeedButtonLoadClick(Sender: TObject);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormDestroy(Sender: TObject);
    procedure FormPaint(Sender: TObject);
  private
    PtList: TCollection;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

uses
  DelphiPt;

procedure TForm1.FormCreate(Sender: TObject);
begin
  PtList := TCollection.Create (TDDHPoint);
end;

procedure TForm1.SpeedButtonSaveClick(Sender: TObject);
var
  Str1: TFileStream;
  Wrap: TWrapper;
begin
  if SaveDialog1.Execute then
  begin
    Str1 := TFileStream.Create (SaveDialog1.FileName,
      fmOpenWrite or fmCreate);
    try
      Wrap := TWrapper.Create (self);
      try
        Wrap.MyColl.Assign (ptList);
        Str1.WriteComponent (Wrap);
      finally
        Wrap.Free;
      end;
    finally
      Str1.Free;
    end;
  end;
end;

procedure TForm1.SpeedButtonLoadClick(Sender: TObject);
var
  Str1: TFileStream;
  Wrap: TWrapper;
begin
  if OpenDialog1.Execute then
  begin
    Str1 := TFileStream.Create (
      OpenDialog1.Filename, fmOpenRead);
    try
      Wrap := TWrapper.Create (self);
      try
        Wrap := Str1.ReadComponent (Wrap) as TWrapper;
        ptList.Assign (Wrap.MyColl);
      finally
        Wrap.Free;
      end;
    finally
      Str1.Free;
      Invalidate;
      Edit1.Text := 'Point ' + IntToStr (PtList.Count + 1);
    end;
  end;
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  Pt: TDDHPoint;
begin
  Pt := PtList.Add as TDDHPoint;
  Pt.X := X;
  Pt.Y := Y;
  Pt.Text := Edit1.Text;
  Edit1.Text := 'Point ' + IntToStr (PtList.Count + 1);
  Invalidate;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // empty and destroy the list
  PtList.Clear;
  PtList.Free;
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to PtList.Count - 1 do
    TDDHPoint (PtList.Items [I]).Paint (Canvas);
end;

end.


Взято с






Работа с листами книги


Работа с листами книги




Есть в VBA одна вещь, которая меня раздражает. Это ActiveSheet и ActiveWorkbook, а также возможность работы с Cells и Range без указания, к какому листу или книге они принадлежат. Одно время я боролся сам с собой, то применяя, то совсем отказываясь от подобных конструкций. Окончательно я отказался от этого лишь после обнаружения многочисленных ошибок в анализе «лога» моего Web-сервера, который я сделал на VBA. Благо, при работе в Delphi нет возможности написать Cells(x, y) = NewValue, подразумевая при этом какой-то неуловимый ActiveSheet. Поэтому прежде, чем работать с отдельными ячейками, я всегда получаю интерфейс на конкретный и вполне осязаемый лист книги. И делю это так:

var ISheet: Excel8TLB._Worksheet;

ISheet := IWorkbook.Worksheets.Item['Лист1'] as Excel8TLB._Worksheet;

Коллекция Worksheet подобна всем остальным коллекциям из Excel TLB. В ней вы можете удалять листы, вставлять новые, изменять их порядок. Но я практически никогда не делаю этого, поэтому всех нетерпеливых снова отсылаю к справке по Excel VBA.

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

Автор Евгений Старостин
Взято с сайта



Работа с массивами


Работа с массивами



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













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







Работа с матрицами и векторами


Работа с матрицами и векторами



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








Работа с метафайлами, векторыми изображениями


Работа с метафайлами, векторыми изображениями



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






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









Работа с MS Access


Работа с MS Access



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





















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




Работа с MS SQL Server


Работа с MS SQL Server



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












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




Работа с MS Word


Работа с MS Word



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




























Работа с MS Word из Delphi


Работа с MS Word из Delphi



1. Управление Word-ом из Delphi.
(опубликовано - 13 SoftWare)


Здесь мы рассмотрим пример того, как управлять объектами Word-а (Excel - аналогично) из программ на Delphi. Исходный код примера можно скачать на страничке 'DownLoad'
а). Для чего это нужно ?
Задачи могут быть самые разные, в общем случае это использование возможностей Word-а в своей программе, н-р: проверка текста на орфографию; печать текста, графики; экспорт отчетов в Word или изначальное создание их там и т.д.

б). Подготовительные работы. На самом деле существует несколько способов сделать это, мы рассмотрим только один (пример кроме Delphi 5, в Delphi5 для этого есть компоненты на закладке Servers переименуете в программе типы на соответствующие компоненты, дальше так же).
Для начала начнем новый проект File, New Application; File, Save All. Создадим отдельную папку для проекта и сохраним Unit1 как Main, а Project1 как WordWriter.
Далее для работы с Word-ом нам потребуется библиотека типов Word-а, это делается так:
Project, Import Type Library, Add, далее переходим в папку, где стоит Word ( у меня это - "c:\program files\microsoft office) , заходим в папку Office и выбираем файл - msword8.olb (цифра -? версии Word-а - у Вас может отличаться ) или excel8.olb (для Excel).Нажимаем Оk. Delphi создаст 2 файла - Word_tlb.pas и Office_tlb.pas, их надо включить в раздел uses модуля Main нашего проекта:

uses... ,Office_Tlb, word_tlb;

в). Теперь займемся непосредственно программированием.
В разделе var опишем следующие переменные:

// класс приложения ворда
WordApp:Word_tlb.Application_;
// класс чего-то типа выделения,
// т.е. говоришь - выделить ячейку с ... по, а результат скидываешь
// в эту перем и работаешь с этими ячейками как с 1 объектом
ARange,TempRange:Range;
// массив документов
Docs:documents;
// 1 документ
Doc:document;
// массив параграфов
pars:Paragraphs;
// 1 параграф
par:Paragraph;
// параметры для передачи
Template,temp,OpenAsTemplate:olevariant;
// массив таблиц
tabls:Tables;
// 1 таблица
tabl:Table;
// рабочая переменная 
i:integer;

Далее проектируем форму:
1. Поместим вверх нашей формы кнопку - button1 типа tbutton, поменяем заголовок (св-во Caption) на 'Старт'.
2. Под кнопкой разместим панель - panel1 типа tpanel. Внутри панели поместим компонент - bevel1 типа tbevel, поменяем св-во Align на alClient (при этом рамка растянется на всю панель).
3. Сверху панели (далее все компоненты будут размещаться внутри этой панели) разместим метку - label1 типа tlabel, поменяем значение св-ва Caption на 'Передать в ячейку':
4. Ниже слева поместим метку - label1 типа tlabel, св-во Caption поменяем на 'X='
5. Правее метки помещаем компонент Edit1 типа tEdit, св-во Text меняем на '1'
6. По правой границе Edit1 помещаем компонент UpDown1 типа tUpDown, в списке св-ва 'Associate' выбираем Edit1, св-во 'Position' приравниваем '1'
7. Чуть отступаем вправо и повторяем шаги 4-6 , заменив Edit1 на Edit2, UpDown1 на UpDown2, Label1 на Label2 соответственно.
8. Ниже размещаем еще одну метку - label4 типа tlabel, меняем св-во 'Caption' на 'Новое значение ячейки:'
9. Ниже размещаем компонент Edit3 типа tEdit, св-во Text меняем на '0'
10. И, наконец, в самом низу панели размещаем кнопку BitBtn1 типа tBitBtn, меняем св-во 'Kind' на 'bkOk'.
Теперь напашем обработчики - именно в них и заключается вся функциональность программы:

1. Назначим обработчик OnClick компоненту Button1 :

procedure TForm1.Button1Click(Sender: TObject);
begin
// если заголовок 'Выход', то закрываем программу
if button1.caption='Выход' then 
begin
Application.Terminate;
exit
end
// иначе (при первом начатии, когда у нас заголовок 'Старт') 
//переименовываем заголовок в 'Выход'
else button1.caption:='Выход';

panel1.Visible:=true;
// создаем экземпляр ворда
WordApp:=CoApplication_.Create;
// делаем его видимым
WordApp.Visible:=true;
// шаблон
template:='Normal';
// создать шаблон
OpenAsTemplate:=false;
// что-то типа оператора with, можно было и напрямую обратиться
Docs:=WordApp.Documents;
// добавляем документ
Doc:=Docs.Add(template,OpenAsTemplate);

// выделить все
ARange:=Doc.Range(EmptyParam,EmptyParam);
// массив параграфов
pars:=doc.Paragraphs;
// переменная - параметр
template:=arange;
// новый параграф
par:=pars.Add(template);
// цвет зеленный 
par.Range.Font.ColorIndex:=11;
// вставляем текст
par.Range.InsertBefore('Привет !!!');
// переменная - параметр
template:=par.Range;
// новый параграф, чтобы таблица не потерла текст
par:=pars.Add(template);
// цвет черный 
par.Range.Font.ColorIndex:=0;
// вставляем текст
par.Range.InsertBefore('Переключившись в программу, можно программно менять текст ячеек !');
// переменная - параметр
template:=par.Range;
// новый параграф, чтобы таблица не потерла текст
par:=pars.Add(template);
// выделяем параграф 
arange:=par.Range;

// шрифт - жирный
ARange.Font.Bold:=1;
// шрифт - рукописный
ARange.Font.Italic:=1;
// получить массив таблиц
tabls:=aRange.Tables;
// добавляем новую таблицу размером 5 на 5
tabl:=tabls.Add(arange,5,5);
// в цикле
for i:=1 to 5 do
// задаем значение ячеек
tabl.Cell(i,1).Range.Text:=inttostr(i);

end;

2. Зададим обработчик формы:

procedure TForm1.FormDestroy(Sender: TObject);
var
   // для параметров
   SaveChanges:olevariant; 
begin
  // если Word не закрыт
  if not VarIsEmpty(WordApp) then begin
   { а можно сохранить автоматом:
      // имя файла в оле
      template:='Имя.doc';
      // если не сохранен, то
      if doc.Saved=false then
          // сохраняем
          Doc.SaveAs(template, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);

      короче, пишешь имя объекта, ставишь точку и нажимаешь
     'ctrl'+' ' и изучаешь существующие методы и св-ва
      }
  //изменения не сохранять
  SaveChanges:=false;
  // то закрыть сначала документ 
  Doc.Close(SaveChanges,EmptyParam,EmptyParam);
  // а потом и ворд
  WordApp.Quit(SaveChanges,EmptyParam,EmptyParam)
end;
end;

3. Назначим обработчик OnClick компоненту Bitbtn1 :

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
// в соотв ячейку ставим соотв значение, 
// а можно и наоборот - получать значение из ячейки в переменную
tabl.Cell(UpDown2.Position,UpDown1.Position).Range.Text:=Edit3.Text;
end;

в). В общем-то, это все ...


г). Дополнительная информация:
· Справка Word-а (по Visual Basic, по умолчанию она не ставится - запустите заново Setup и поставте в соотв. месте галочку)
· Книги:
- Чарльз Калверт "Энциклопедия пользователя Delphi4"
(издательство - DiaSoft)
- Марко Кэнту "Delphi4 для профессионалов"
(издательство - Питер)
· Если у Вас другая версия Word-а:
Параметры ф-ций могут отличаться - обратитесь к справке (см выше) или если у Вас версия Delphi 3 и выше, то используем универсальный метод - пишешь имя объекта, ставишь точку (если нужно посмотреть список параметров у функции , то после открывающей скобки ) , нажимаешь 'ctrl'+'пробел' и изучаешь существующие методы и св-ва.

(c) 13 SoftWare. Статья взята с сайта www.vladimir13.narod.ru

В данный FAQ попала с Исходников.ru



Работа с MySQL


Работа с MySQL



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





Работа с NTFS


Работа с NTFS



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







Работа с очень большими числами


Работа с очень большими числами



Это модуль для работы с очень большими числами без потери точности. Модуль даёт возможность манипулирования с 10000 и более значащими цифрами в числах. В модуле реализованы сложение, вычитание, умножение, деление, возведение в целую степень и факториал. Все функции в качестве аргументов принимают длинные строки и результат выдают тоже в виде строки.

Автор модуля Vit ()


Просьба связаться со мной, если кто хочет доработать модуль и расширить функциональность.



unit UMathServices;
{Автор Vit}

interface


Type TProgress = procedure(Done:real);

{Собственно экспортные функции}
Function ulFact(First:String):string;
Function ulSum(First, Second :string):string;
Function ulSub(First, Second :string):string;
Function ulMPL(First, Second :string):string;
Function ulPower(First, Second :string):string;
function UlDiv(First, Second:String; Precision:integer):String;   {Precision - не истинная точность а количество знаков учитываемых после запятой сверх тех которые значимы. Все знаки уже существующие в делимом и делителе в любом случае учитываются}

{Call back function for long operations}
var OnProgress: TProgress;

implementation

Uses SysUtils;

type TMathArray=array of integer;

Type TNumber=record
               int, frac:TMathArray;
               sign:boolean;
             end;

var   n1, n2:TNumber;



Procedure Str2Number(s:string; var n:TNumber);
  var i, j, l:integer;
begin
  if s='' then
    begin
      setlength(n.int , 0);
      setlength(n.frac , 0);
      exit;
    end;
  l:=length(s);
  if s[1]='-' then
    begin
      s:=copy(s,2,l);
      l:=l-1;
      n.sign:=false;
    end
  else
    n.sign:=true;
  j:=pos('.', s);
  if j>0 then
    begin
      setlength(n.int , j-1);
      for i:=1 to j-1 do n.int[i-1]:=strtoint(s[j-i]);
      setlength(n.frac , l-j);
      for i:=1 to l-j do n.frac[i-1]:=strtoint(s[l-i+1]);
    end
  else
    begin
     setlength(n.int,l);
     for i:=1 to l do n.int[i-1]:=strtoint(s[l-i+1]);
     setlength(n.frac,0);
    end;
end;

Function Num2Array(Var n:TNumber; var a:TMathArray):integer;
  var i:integer;
begin
  result:=length(n.frac);
  setlength(a,length(n.int)+result);
  for i:=0 to length(a)-1 do if i<result then a[i]:=n.frac[i] else a[i]:=n.int[i-result];
end;

Procedure MultiplyArray(var a1, a2, a:TMathArray);
  var i, j:integer;
      b:boolean;
begin
{checking for zero, 1}
  for i:=length(a2)-1 downto 0 do
    begin
      for j:=length(a1)-1 downto 0 do
        begin
          a[j+i]:=a[j+i]+(a2[i]*a1[j]);
        end;
    end;
  repeat
    b:=true;
    for i:=0 to length(a)-1 do
      if a[i]>9 then
        begin
          b:=false;
          try
            a[i+1]:=a[i+1]+1;
          except
            setlength(a, length(a)+1);
            a[i+1]:=a[i+1]+1;
          end;
          a[i]:=a[i]-10;
        end;
  until b;
end;


Procedure Array2Num(Var n:TNumber; var a:TMathArray; frac:integer; sign:boolean);
  var i:integer;
begin
  setlength(n.frac,frac);
  setlength(n.int,length(a)-frac);
  for i:=0 to length(a)-1 do
    begin
      if i<frac then n.frac[i]:=a[i] else n.int[i-frac]:=a[i];
    end;
  n.sign:=sign;
end;

Function Number2Str(var n:TNumber):string;
  var i:integer;
      s:string;
begin
  result:='';
  for i:=0 to high(n.int) do result:=inttostr(n.int[i])+result;
  if length(n.frac)<>0 then
    begin
      for i:=0 to high(n.frac) do s:=inttostr(n.frac[i])+s;
      result:=result+'.'+s;
    end;
  while (length(result)>1) and (result[1]='0') do delete(result,1,1);
  if pos('.', result)>0 then while (length(result)>1) and (result[length(result)]='0') do delete(result,length(result),1);
  if not n.sign then result:='-'+result;
  setlength(n.int,0);
  setlength(n.frac,0);
end;

Procedure DisposeNumber(var n:TNumber);
begin
  setlength(n.int,0);
  setlength(n.frac,0);
end;


Function ulFact(First:String):string;
  var n1, n2:TNumber;
      i:integer;
      a, a1, a2:TMathArray;
      max:integer;
begin
  Str2Number('1', n1);
  Str2Number('1', n2);
  Num2Array(n1, a1);
  Num2Array(n2, a2);
  max:=strtoint(First);
  for i:=1 to strtoint(First) do
    begin
      if Assigned(OnProgress) then OnProgress((i/max)*100);
      setlength(a,length(a1)+length(a2)+1);
      MultiplyArray(a1, a2, a);
      setlength(a1,0);
      setlength(a2,0);
      a1:=a;
      Str2Number(inttostr(i), n2);
      Num2Array(n2, a2);
    end;
  Array2Num(n1, a1, 0, true);
  result:=Number2Str(n1);
  DisposeNumber(n1);
end;

Function ulPower(First, Second :string):string;
  var i, j, c:integer;
      a, a1, a2:TMathArray;
  var n1:TNumber;
      max:integer;
begin
  j:=strtoint(Second);
  if j=0 then
    begin
      result:='1';
      exit;
    end
  else
    if j=1 then
      begin
        result:=First;
        exit;
      end;


  max:=j-1;
  Str2Number(First, n1);
  c:=Num2Array(n1, a1);
  setlength(a,0);
  setlength(a2,0);
  a2:=a1;
  for i:=1 to j-1 do
    begin
      if Assigned(OnProgress) then OnProgress((i/max)*100);
      setlength(a,0);
      setlength(a,length(a1)+length(a2)+1);
      MultiplyArray(a1, a2, a);
      setlength(a2,0);
      a2:=a;
    end;
  setlength(a1,0);
  setlength(a2,0);
  c:=c*j;
  if n1.sign then
    Array2Num(n1, a, c, true)
  else
    if odd(j) then Array2Num(n1, a, c, false) else Array2Num(n1, a, c, true);
  setlength(a,0);
  result:=Number2Str(n1);
  DisposeNumber(n1);
end;




Procedure MultiplyNumbers(var n1, n2 :TNumber);
  var i:integer;
      a, a1, a2:TMathArray;
begin
  i:=Num2Array(n1, a1)+Num2Array(n2, a2);
  setlength(a,length(a1)+length(a2)+1);
  MultiplyArray(a1, a2, a);
  setlength(a1,0);
  setlength(a2,0);
  Array2Num(n1, a, i, n1.sign=n2.sign);
  DisposeNumber(n2);
  setlength(a,0);
end;


Function ulMPL(First, Second :string):string;
  var n1, n2:TNumber;
begin
  Str2Number(First, n1);
  Str2Number(Second, n2);
  MultiplyNumbers(n1, n2);
  result:=Number2Str(n1);
  DisposeNumber(n1);
end;


Procedure AlignNumbers(var n1, n2:TNumber);
  var i1, i2, i:integer;
begin
  i1:=length(n1.int);
  i2:=length(n2.int);
  if i1>i2 then setlength(n2.int, i1);
  if i2>i1 then setlength(n1.int, i2);

  i1:=length(n1.frac);
  i2:=length(n2.frac);

  if i1>i2 then
    begin
      setlength(n2.frac, i1);
      for i:=i1-1 downto 0 do
        begin
          if i-(i1-i2)>0 then n2.frac[i]:=n2.frac[i-(i1-i2)] else n2.frac[i]:=0;
        end;
    end;
  if i2>i1 then
    begin
      setlength(n1.frac, i2);
      for i:=i2-1 downto 0 do
        begin
          if i-(i2-i1)>0 then n1.frac[i]:=n1.frac[i-(i2-i1)] else n1.frac[i]:=0;
        end;
    end;
end;


Function SubInteger(a1,a2:TMathArray):integer;
  var i:integer;
      b:boolean;
begin
  result:=0;
  if length(a1)=0 then exit;
  for i:=0 to length(a1)-1 do a1[i]:=a1[i]-a2[i];
  repeat
    b:=true;
    for i:=0 to length(a1)-1 do
      if a1[i]<0 then
        begin
          b:=false;
          if i=length(a1)-1 then
            begin
              result:=-1;
              a1[i]:=a1[i]+10;
              b:=true;
            end
          else
            begin
              a1[i+1]:=a1[i+1]-1;
              a1[i]:=a1[i]+10;
            end;
        end;
  until b;
end;

Procedure AssignNumber(out n1:TNumber; const n2:TNumber);
  var i:integer;
begin
  Setlength(n1.int, length(n2.int));
  for i:=0 to length(n2.int)-1 do n1.int[i]:=n2.int[i];
  Setlength(n1.frac, length(n2.frac));
  for i:=0 to length(n2.frac)-1 do n1.frac[i]:=n2.frac[i];
  n1.sign:=n2.sign;
end;

Procedure SubNumber(var n1, n2 : TNumber);
  var i:integer;
      n:TNumber;
begin
  AlignNumbers(n1, n2);
  i:=subInteger(n1.frac, n2.frac);
  n1.int[0]:=n1.int[0]+i;
  DisposeNumber(n);
  AssignNumber(n, n1);
  i:=subInteger(n1.int, n2.int);
  if i<0 then
    begin
      subInteger(n2.int, n.int);
      AssignNumber(n1, n2);
    end
  else
    begin
      DisposeNumber(n2);
    end;
end;

Function SumInteger(a1,a2:TMathArray):integer;
  var i:integer;
      b:boolean;
begin
  result:=0;
  if length(a1)=0 then exit;
  for i:=0 to length(a1)-1 do a1[i]:=a1[i]+a2[i];
  repeat
    b:=true;
    for i:=0 to length(a1)-1 do
      if a1[i]>9 then
        begin
          b:=false;
          if i=length(a1)-1 then
            begin
              result:=1;
              a1[i]:=a1[i]-10;
              b:=true;
            end
          else
            begin
              a1[i+1]:=a1[i+1]+1;
              a1[i]:=a1[i]-10;
            end;
        end;
  until b;
end;

Procedure SumNumber(var n1, n2:TNumber);
  var i:integer;
begin
  AlignNumbers(n1, n2);
  i:=sumInteger(n1.frac, n2.frac);
  n1.int[0]:=n1.int[0]+i;
  i:=sumInteger(n1.int, n2.int);
  if i>0 then
    begin
      setlength(n1.int, length(n1.int)+1);
      n1.int[length(n1.int)-1]:=i;
    end;
  DisposeNumber(n2);
end;

Procedure SumNumbers(var n1, n2:TNumber);
begin
  if n1.sign and n2.sign then
    begin
      SumNumber(n1, n2);
      n1.sign:=true;
    end
  else
    if (not n1.sign) and (not n2.sign) then
      begin
        SumNumber(n1, n2);
        n1.sign:=False;
      end
    else
      if (not n1.sign) and n2.sign then
        begin
          SubNumber(n2, n1);
          AssignNumber(n1, n2);
        end
      else
        begin
          SubNumber(n1, n2);
        end;
end;

Function ulSum(First, Second :string):string;
begin
  Str2Number(First, n1);
  Str2Number(Second, n2);
  SumNumbers(n1, n2);
  result:=Number2Str(n1);
  DisposeNumber(n1);
end;

Function ulSub(First, Second :string):string;
begin
  Str2Number(First, n1);
  Str2Number(Second, n2);
  n2.sign:=not n2.sign;
  SumNumbers(n1, n2);
  result:=Number2Str(n1);
  DisposeNumber(n1);
end;









function  DupChr(const X:Char;Count:Integer):AnsiString;
begin
  if Count>0 then begin
    SetLength(Result,Count);
    if Length(Result)=Count then FillChar(Result[1],Count,X);
  end;
end;

function StrCmp(X,Y:AnsiString):Integer;
var
  I,J:Integer;
begin
  I:=Length(X);
  J:=Length(Y);
  if I=0 then begin
    Result:=J;
    Exit;
  end;
  if J=0 then begin
    Result:=I;
    Exit;
  end;
  if X[1]=#45 then begin
    if Y[1]=#45 then begin
      X:=Copy(X,2,I);
      Y:=Copy(Y,2,J);
    end else begin
      Result:=-1;
      Exit;
    end;
  end else if Y[1]=#45 then begin
    Result:=1;
    Exit;
  end;
  Result:=I-J;
  if Result=0 then Result:=CompareStr(X,Y);
end;



function StrDiv(X,Y:AnsiString):AnsiString;
var
  I,J:Integer;
  S,V:Boolean;
  T1,T2:AnsiString;
  R:string;
  max:integer;

begin
  Result:=#48;
  R:=#48;
  I:=Length(X);
  J:=Length(Y);
  S:=False;
  V:=False;
  if I=0 then Exit;
  if (J=0) OR (Y[1]=#48) then begin
    Result:='';
    R:='';
    Exit;
  end;
  if X[1]=#45 then begin
    Dec(I);
    V:=True;
    X:=Copy(X,2,I);
    if Y[1]=#45 then begin
      Dec(J);
      Y:=Copy(Y,2,J)
    end else S:=True;
  end else if Y[1]=#45 then begin
    Dec(J);
    Y:=Copy(Y,2,J);
    S:=True;
  end;
  Dec(I,J);
  if I<0 then begin
    R:=X;
    Exit;
  end;
  T2:=DupChr(#48,I);
  T1:=Y+T2;
  T2:=#49+T2;
  max:= Length(T1);
  while Length(T1)>=J do begin
    while StrCmp(X,T1)>=0 do begin
      X:=UlSub(X,T1);
      Result:=UlSum(Result,T2);
    end;
    SetLength(T1,Length(T1)-1);
    SetLength(T2,Length(T2)-1);
    if Assigned(OnProgress) then OnProgress(100-(Length(T1)/max)*100);
  end;
  R:=X;
  if S then if Result[1]<>#48 then Result:=#45+Result;
  if V then if R[1]<>#48 then R:=#45+R;
end;

Function Mul10(First:string; Second:integer):string;
  var s:string;
      i, j:integer;
begin
  if pos('.',First)=0 then
    begin
      s:='';
      For i:=0 to Second-1 do s:=s+'0';
      Result:=First+s;
    end
  else
    begin
      s:='';
      j:=length(First)-pos('.',First);
      if (second-j)>0 then For i:=0 to Second-j-1 do s:=s+'0';
      First:=First+s;
      j:=pos('.',First);
      First:=StringReplace(First,'.','',[]);
      insert('.',First,j+second);
      while (length(First)>0) and (First[length(First)]='0') do delete(First,length(First),1);
      while (length(First)>0) and (First[length(First)]='.') do delete(First,length(First),1);
      Result:=First;
    end;
end;

Function Div10(First:string; Second:integer):string;
  var s:string;
      i:integer;
begin
  s:='';
  For i:=0 to Second do s:=s+'0';
  s:=s+First;
  Insert('.', s, length(s)-Second+1);
  while (length(s)>0) and (s[1]='0') do delete(s,1,1);
  if pos('.',s)>0 then
    while (length(s)>0) and (s[length(s)]='0') do delete(s,length(s),1);
  if (length(s)>0) and (s[length(s)]='.') then delete(s,length(s),1);
  Result:=s;
end;

function UlDiv(First, Second:String; Precision:integer):String;
begin
  First:=Mul10(First, Precision);
  result:=Div10(StrDiv(First, Second), Precision);
end;



end.

Взято с Vingrad.ru



Работа с OpenGL - Минимальная программа


Работа с OpenGL - Минимальная программа




Содержимое контекста

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

То есть схема здесь такая - приложение отдает команды машине OpenGL, получая результат на поверхности своего окна. Такая архитектура называется "клиент - серверной", в роли клиента выступает наше приложение, в роли сервера - система OpenGL. Задача сервера - отрабатывать команды клиента. В принципе, сервер может располагаться и не на нашем компьютере, а удаленно, возможно и за тысячи километров от нас. Наше приложение должно передать серверу требуемые для работы данные - контексты и команды на языке сервера. Также важно понимать, что основную работу выполняет не наше приложение, а сервер. Наше приложение лишь создает окно - платформу для работы сервера, и передает команды серверу. При грамотном подходе нет разницы, какими средствами мы спроектировали приложение - С или Delphi, скорость воспроизведения целиком зависит от производительности сервера - машины OpenGL. Мы выбрали в качестве средства разработки приложений именно Delphi за выдающуюся скорость компиляции и обаятельность концепций.

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

Мы знаем, что ссылка на контекст устройства - величина типа HDC, для получения которой вызываем функцию GetDC. Ссылке на контекст устройства в Delphi соответствует свойство Canvas.Handle формы, принтера и некоторых компонентов. Теоретически всюду в наших примерах в строках, использующих величину DC типа HDC, вместо DC можно использовать Canvas.Handle. В первых примерах для начинающих это так и сделано. Каков же все-таки смысл контекста устройства, если он и так связан с однозначно определенным объектом - окном, областью памяти или принтером, и зачем передавать дополнительно какую-то информацию об однозначно определенном объекте?

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

Строки:



Form1.Canvas.Ellipse(0, 0, 100, 100);

и

Printer.BeginDoc;
Printer.Canvas.Ellipse (0,0,100,100);
Printer.EndDoc;




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

Win32 Programmer's Reference фирмы MicroSoft о контексте устройства сообщает следующее:

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

Термин "структура", встретившийся здесь, соответствует записи в терминологии Delphi. Контекст устройства Windows содержит информацию, относящуюся к графическим компонентам GDI, контекст воспроизведения содержит информацию, относящуюся к OpenGL, то есть играет такую же роль, что и контекст устройства для GDI. В частности, эти контексты являются хранилищами состояния системы, например, хранят информацию о текущем цвете карандаша.

Формат пикселя

Итак, ссылка на контекст устройства содержит характеристики устройства и средства отображения. Именно он знает, как выводить на конкретно это устройство. Упрощенно говоря, получив ссылку на контекст устройства, мы берем в руки простой либо цветной карандаш, или кисточку с палитрой в миллионы оттенков. Сервер OpenGL, прежде чем приступать к работе, также должен определиться, на каком оборудовании ему придется работать. Это может быть скромная персоналка, а может быть и мощная графическая станция. Прежде чем получить контекст воспроизведения, сервер OpenGL должен получить детальные характеристики используемого оборудования. Эти характеристики хранятся в специальной структуре, тип которой - TPixelFormatDescriptor (описание формата пикселя). Формат пикселя определяет конфигурацию буфера цвета и вспомогательных буферов.

Самый частый вопрос, который я получаю в связи с моими уроками, заключается в просьбе указать источники подробной информации об OpenGL на русском. К сожалению, если такие и есть, то мне они неизвестны. Главным нашим подручным станет поставляемый в составе Delphi файл помощи по OpenGL. Систему помощи Delphi для получения хороших результатов необходимо настраивать, если в помощи Delphi найти раздел по OpenGL, он не порадует обилием информации. В разных версиях Delphi настройка помощи выполняется по-разному, потребуются некоторые несложные манипуляции, но мы не будем тратить на это время. Будем использовать самый простой способ - контекстную помощь. Наберите в тексте модуля фразу "PixelFormatDescriptor", нажмите клавишу F1 и Вы получите подробную помощь об этом типе. Точно также мы будем получать помощь обо всех терминах, функциях и командах OpenGL.

Итак, мы получили обширное описание структуры PixelFormatDescriptor. Обращаю внимание, что мы видим раздел помощи MicroSoft, рассчитанной на программистов С и С++, поэтому описание использует термины и стилистику именно этих языков. Так, по традиции Delphi имена типов начинаются с префикса T, но нам не удастся найти помощь по термину TPixelFormatDescriptor. К сожалению, это не единственное неудобство, которое нам придется испытывать. Например, если сейчас мы заглянем в файл windows.pas и найдем описание записи TPixelFormatDescriptor, мы обнаружим, что в файле помощи не указаны некоторые константы, а именно: PFD_SWAP_LAYER_BUFFERS, PFD_GENERIC_ACCELERATED и PFD_DEPTH_DONTCARE. А константа, названная PFD_DOUBLE_BUFFER_DONTCARE, по-видимому, соответствует константе, описанной в модуле windows.pas как PFD_DOUBLEBUFFER_DONTCARE. Наверное, более поздние версии помощи и заголовочного файла исправят этот и другие неточности.

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

В каталоге Beginner/1 Вы найдете проект OpenGL_min.dpr, в котором я привел описание всех полей структуры TPixelFormatDescriptor на русском, в момент их первоначального заполнения. Делается это в процедуре SetDCPixelFormat, вызываемой между получением ссылки на контекст устройства и созданием контекста воспроизведения OpenGL. Посмотрим подробнее, что там делается. Полям структуры присваиваются желаемые значения, затем вызовом функции ChoosePixelFormat осуществляется запрос системе, поддерживается ли на данном рабочем месте выбранный формат пикселя, и вызовом функции SetPixelFormat устанавливаем формат пикселя в контексте устройства. Функция ChoosePixelFormat возвращает индекс формата пикселя, который нам нужен в качестве аргумента функции SetPixelFormat.

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

Обратим внимание на поле структуры "битовые флаги" - dwFlags. То, как мы зададим значение флагов, существенно может сказаться на работе нашего приложения, и наобум задавать эти значения не стоит. Тем более, что некоторые флаги совместно ужиться не могут, а некоторые могут присутствовать только в паре с другими. В этом примере флагам я присвоил значение PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL, то есть сообщаю системе, что я собираюсь осуществлять вывод в окно, и что моя система в принципе поддерживает OpenGL. Я ограничился всего двумя константами из обширного списка, приведенного в модуле windows.pas, по каждой из которых в файле помощи приведено детальное описание.

Так, константа PFD_DOUBLEBUFFER включает режим двойной буферизации, когда вывод осуществляется не на экран, а в память, затем содержимое буфера выводится на экран. Это очень полезный режим, если в любом примере на анимацию убрать режим двойной буферизации и все команды, связанные с этим режимом, хорошо будет видно мерцание при выводе кадра. Константу PFD_GENERIC_ACCELERATED имеет смысл устанавливать в случае, если компьютер оснащен графическим акселератором. Флаги, заканчивающиеся на "DONTCARE" , сообщают системе, что соответствующий режим может иметь оба значения, то есть PFD_DOUBLE_BUFFER_DONTCARE - запрашиваемый формат пикселя может иметь оба режима - одинарной и двойной буферизации. Со всеми остальными полями и константами я предоставляю Вам возможность разобраться самостоятельно, только замечу, что поле iLayerType, описанное в windows.pas типа Byte, может, согласно помощи, иметь три значения: PFD_MAIN_PLANE, PFD_OVERLAY_PLANE и PFD_UNDERLAY_PLANE, однако константа PFD_UNDERLAY_PLANE имеет значение -1, так что установить такое значение не удастся.

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

В примере битовым флагам задаем все возможные значения одновременно, числовым полям задаем заведомо нереальное значение 64, и смотрим на выбор формата пикселя, сделанным OpenGL. Результат, который Вы получите - выбранный формат пикселя, я предсказать не смогу - он индивидуален для каждой конкретной конфигурации машины и текущих настроек. Возможно, Вы получите в результате, что режим двойной буферизации не будет установлен - напоминаю, многие флаги устанавливаются только в комбинации с другими определенными. Наше приложение позволяет менять параметры формата пикселя и устанавливать его заново. Чтобы видеть, что происходит воспроизведение, небольшая площадка на экране при каждом тестировании окрашивается случайным цветом, используя функции OpenGL. Поэкспериментируйте с этим приложением, например, определите комбинацию флагов для установления режима двойной буферизации. Посмотрите значение числовых полей формата при различной палитре экрана - 16, 24, 32 бита, но не 256 цветов. О выводе при палитре экрана в 256 цветов - отдельный разговор. Это приложение, в частности, дает ответ на вопрос - как определить, оснащен ли компьютер графическим акселератором. Повозившись с этим приложением, Вы найдете ответ на вопрос, на который я Вам ответить не смогу - как надо заполнить структуру TPixelFormatDescriptor для Вашего компьютера. Обратите внимание, что в коде я установил несколько проверок на отсутствие контекста воспроизведения, который может быть потерян по ходу работы любого приложения, использующего OpenGL - редкая, но возможная ситуация в штатном режиме работы системы и очень вероятная ситуация если, например, по ходу работы приложения менять настройки экрана.

Минимальная программа OpenGL

Теперь мы знаем все, что необходимо для построения минимальной программы, использующей OpenGL. Я привел два варианта этой программы - одна построена исключительно на функциях Windows API, другая использует библиотеку классов Delphi (проекты каталогов Beginner/1 и Beginner/2 соответственно).

Взглянем на головной модуль второго проекта. При создании формы задаем формат пикселя, в качестве ссылки на контекст устройства используем значение Canvas.Handle формы. Создаем контекст воспроизведения OpenGL и храним в переменной типа HGLRC. При обработке события OnPaint устанавливаем контекст воспроизведения, вызываем функции OpenGL и освобождаем контекст. При завершении работы приложения удаляем контекст воспроизведения. Для полной академичности можно включить строки, проверяющие, получен ли контекст воспроизведения, и не теряется ли он по ходу работы. Признаком таких ситуаций является нулевое значение переменной hrc. В минимальной программе я просто окрашиваю окно в желтоватый оттенок. Получив помощь по команде glClearColor, Вы можете узнать, что аргументы ее - тройка вещественных чисел в интервале [0;1], задающих долю красного, зеленого и синего составляющих в цвете и еще один, четвертый аргумент, о котором мы поговорим чуть позднее. Этому аргументу я в примере задал значение 1.0. Вообще то, аргументы glClearColor, согласно помощи, имеют неведомый тип GLclampf. Для того, чтобы разобраться с этим типом, отсылаю к строке



GLclampf = Single;




модуля opengl.pas.

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

Строку нашей программы



glClear(GL_COLOR_BUFFER_BIT);




будем понимать как очистку экрана, фон при этом покрывается заданным цветом.

Проект, построенный только на функциях API, надеюсь, сейчас стал более понятным. Вместо Canvas.Handle используем собственную переменную dc, в обработчике события WM_PAINT реализуем действия, которые Delphi при обычном подходе выполняет за нас. Напоминаю, что для лучшей устойчивости работы обработчик WM_PAINT следовало бы написать так:



dc := BeginPaint (Window, MyPaint);
wglMakeCurrent (dc, hrc);
glClearColor (0.85, 0.75, 0.5, 1.0);
glClear (GL_COLOR_BUFFER_BIT);
wglMakeCurrent (dc, 0);
EndPaint (Window, MyPaint);
ReleaseDC (Window, dc);




А в обработчике WM_DESTROY следует перед PostQuitMessage добавить строку:



DeleteDC (dc);




То есть все используемые ссылки необходимо освобождать, а после того, как они стали не нужны - удалять.

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

Во всех своих примерах я приписал рекомендацию не запускать проекты, использующие OpenGL под управлением среды Delphi. Дело в том, что часто в таких ситуациях программа аварийно прерывается, выдавая сообщение "access violation -". Это происходит и в случае самой аккуратной работы с контекстами, и не связано с небрежностью работы программы. Некоторые программисты вину за это возлагают на софтверные драйверы и рекомендуют обновить их. Некоторые утверждают, что дело в Windows 9X, и под NT этого не происходит. Возможно, Вы тоже ничего такого не замечали и не можете взять в толк, о чем я сейчас веду речь. У меня такие окошки вылетают через раз на одном и том же проекте, хотя откомпилированный модуль работает превосходно. Я полагаю, что если драйверы не "глюкуют", когда приложение работает без среды Delphi, дело не только в драйверах.

Вывод на поверхность компонентов

Теоретически функциями OpenGL возможно осуществлять вывод не только на поверхность формы, а и на поверхность любого компонента, если у него имеется свойство Canvas.Handle, для чего при получении контекста воспроизведения необходимо указывать именно его ссылку на контекст устройства, например, Image1.Canvas.Handle. Однако чаще всего это приводит к неустойчивой работе, вывод "то есть, то нет", хотя контекст воспроизведения присутствует и не теряется. Я советую Вам всегда пользоваться выводом исключительно на поверхность окна. OpenGL прекрасно уживается с визуальными компонентами, как видно из примера TestPFD, если же необходимо ограничить размер области вывода, для этого есть стандартные методы, о которых мы обязательно будем беседовать в будущем.

Просто ради интереса приведу пример, когда вывод OpenGL осуществляется на поверхность панели, то есть компонента, не имеющего свойства Canvas. Для этого мы пользуемся тем, что панель имеет отдельное окно, вызываем функцию GetDC с аргументом Panel1.Handle.

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

Для вывода на компонент класса TImage можете записать:



dc := Image1.Canvas.Handle;




и удалить строки BeginPaint и EndPaint, поскольку TImage не имеет свойства Handle, то есть не создает отдельного окна. Однако вывод на такие компоненты как раз отличается полной неустойчивостью, так что я не гарантирую Вам надежного положительного результата.

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

В конце сегодняшнего разговора я хочу привести еще несколько проектов, появившихся за это время из под моего пера и дополняющих "ЖиЛистую Delphi".

Взято с





Работа с OpenGL - Введение


Работа с OpenGL - Введение




Введение

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

Когда я начинал изучение этого, у меня не было ни одного примера использования OpenGL в Delphi, только ворох программ на C и C++, поэтому пришлось начинать с того, чтобы перекладывать эти программы на Delphi. Затем появились и полностью собственные проекты. Моя основная работа связана с преподаванием в вузе, после того, как я включил в учебные курсы изучение основ OpenGL, студенты с моей помощью смогли создать ряд интересных проектов.

Я решил опубликовать некоторые из проектов моей коллекции, озаглавил набор "ЖиЛистая Delphi" и предложил сайту "Королевство Delphi". На сайте мне предложили дополнить эти проекты серией статей по вопросам использования OpenGL в Delphi. Данная статья является первой статьей этого цикла.

Статьи я предполагаю писать на уровне, доступном для самой широкой аудитории - от новичков в программировании для Windows до умудренных профессионалов. Я постараюсь придерживаться краткости в своих рассуждениях, освещая только суть рассматриваемых вопросов. Многие вопросы, освещаемые здесь, ясно проиллюстрированы в проектах "ЖиЛистой Delphi".

OpenGL - стандартный для большинства платформ и операционных систем набор низкоуровневых функций двумерной и трехмерной графики, библиотека, широко используемая в промышленных CAD-системах и играх.

Поставляется в составе операционной системы Windows, начиная с версии OSR2 в виде двух DLL-файлов - opengl32.dll и glu32.dll. Первая из этих библиотек и есть собственно набор функций OpenGL, вторая содержит дополнительный набор функций, упрощающих кодирование, но построенных и выполняемых с подключением opengl32.dll и являющаяся надстройкой.

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

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

Для более подробной информации о OpenGL Вы можете обратиться на сайт http://www.opengl.org

Вместе с Delphi, начиная с третьей версии, поставляется файл помощи по OpenGL фирмы MicroSoft и заголовочный файл opengl.pas, позволяющий использовать эту графическую библиотеку в приложениях, написанных на Delphi.

Есть также альтернативные версии заголовочных файлов независимых разработчиков и компоненты, использующие OpenGL, упрощающие доступ к его функциям и использующие ООП подход. Некоторые из этих файлов и компонентов могут использовать версию OpenGL для Windows фирмы SGI, имеющую собственное расширение функций и имеющую более высокие скоростные показатели. Одна из самых полных систем, реализующая набор функций всех версий OpenGL - это библиотека разработчика MGL фирмы SciTechSoft.

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

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

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

Для понимания смысла этих действий желательно понимать смысл основных понятий операционной системы Windows - ссылка, контекст, сообщение, в проектах Delphi не всегда активно используемых программистами. Желательно иметь хотя бы минимальные знания о роли динамических библиотек в этой операционной системе. Хотя, конечно, можно успешно использовать OpenGL и без глубоких знаний в этой области, используя готовые шаблоны приложений и сосредоточившись собственно на функциях OpenGL.

Важно также отметить то, что чаще всего приложения, активно использующие графику, нуждаются от Delphi только в создании окна приложения, таймере и обработчике манипуляций с клавиатурой и мышью. Для таких приложений чаще всего и не требуется богатство библиотеки VCL. и крайне важны скорость работы и "профессиональная" миниатюрность откомпилированного модуля. Поскольку мы вынуждены с самого начала рассматривать и разбирать темы уровнем ниже RAD-технологий, то нам становится по силам и написание программ без визуальных средств вообще, программ, использующих только функции Windows API, стремительно компилируемых и занимающих после компиляции миниатюрные размеры (порядка двух десятков килобайт).

Итак, наш разговор приходится начинать с вопросов, напрямую вроде бы не связанных с OpenGL.

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

Событие. Сообщение. Контекст.

Начнем наш разговор с понятий "событие" и "сообщение".

Очень часто это синонимы одного и того же термина операционной системы, общающейся с приложениями посредством посылки сообщений. Код, написанный в проекте Delphi как обработчик события OnCreate, выполняется при получении приложением сообщения WM_CREATE, сообщению WM_PAINT соответствует событие OnPaint, и т.д..Такие события использует мнемонику, сходную с мнемоникой сообщений.

Как операционная система различает окна для осуществления диалога с ними? Все окна при своем создании регистрируются в операционной системе и получают уникальный идентификатор, называемый "ссылка на окно". Тип этой величины в Delphi - HWND (WiNDow Handle, ссылка на окно).

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

Попробуем проиллюстрировать смысл ссылки на окно на несложном примере.

Откомпилируйте минимальное приложение Delphi и начните новый проект. Форму назовите Form2, разместите на форме кнопку, обработчик события OnClick кнопки приведите к следующему виду:



procedureTForm2.Button1Click(Sender: TObject);
var
  H: HWND;
begin
  H := FindWindow ('TForm1', 'Form1');
  if H <> 0 then
    ShowMessage ('Есть Form1!')
  else
    ShowMessage ('Нет Form1!')
end;




Теперь при щелчке на кнопке выдается сообщение, есть ли запущенное приложение, класс окна которого зарегистрирован в операционной системе как 'TForm1', в заголовке которого записано 'Form1'. То есть если одновременно запустить обе наши программы, при нажатии на кнопку выдается одно сообщение, если окно с заголовком 'Form1' закрыть, при щелчке на кнопку выдается другое сообщение.

Здесь мы используем функцию API FindWindow, возвращающую величину типа HWND - ссылку на найденное окно либо ноль, если такое окно не найдено.

Итак, ссылка на окно однозначно определяет окно. Свойство Handle формы и есть эта ссылка, значение которой форма получает при выполнении функции API CreateWindow - создании окна. Имея ссылку на окно, операционная система общается с окном путем посылки сообщений-сигналов о том, что произошло какое-либо событие, имеющее отношение именно к этому окну. Если окно имеет намерение отреагировать на это событие, операционная система имеет это в виду и вместе с окном осуществляет эту реакцию. Окно может и не имея фокус получать сообщения и реагировать на них.

Проиллюстрируем это на примере.

Обработчик события OnMouseMove формы приведите к виду:



procedure TForm2.FormMouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
  Caption := 'x=' + IntToStr (X) + ', y=' + IntToStr (Y);
end;




В заголовок формы выводятся координаты указателя мыши.

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

Имея ссылку на окно, приложение может производить с ним любые действия, путем посылки ему сообщений.

Изменим код обработки щелчка кнопки:



procedure TForm2.Button1Click(Sender: TObject);
var
  H: HWND;
begin
  H := FindWindow ('TForm1', 'Form1');
  if H <> 0 then
    SendMessage(H, WM_CLOSE, 0, 0);
end;




Если имеется окно класса 'TForm1' с заголовком 'Form1', наше приложение посылает ему сообщение WM_CLOSE - пытается закрыть окно.

Точно также, если необходимо нарисовать что-либо на поверхности чужого окна, необходимо получить ссылку на это окно.

Для начала попробуем рисовать на поверхности родного окна.

Разместите еще одну кнопку, обработку щелчка которой приведите к виду:



procedure TForm2.Button2Click(Sender: TObject);
var
  dc: HDC;
begin
  dc := GetDC (Handle);
  Rectangle (dc, 10, 10, 110, 110);
  ReleaseDC (Handle, dc);
end;




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

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



procedure TForm2.Button2Click(Sender: TObject);
var
  dc: HDC;
  Window: HWND;
begin
  Window := FindWindow ('TForm1', 'Form1');
  if Window <> 0 then
  begin
    dc := GetDC (Window);
    Rectangle (dc, 10, 10, 110, 110);
    ReleaseDC (Handle, dc);
  end
end;




Теперь во время работы приложения, если в системе зарегистрировано окно класса 'TForm1' с заголовком 'Form1', вывод будет осуществляться на него. Запустите параллельно откомпилированные модули минимального и только что созданного приложений. При щелчке на кнопке прямоугольник рисуется на поверхности чужого окна.

Замечу, что если закрыть Project1.exe и загрузить в Delphi соответствующий ему проект, при щелчке на кнопке прямоугольник будет рисоваться на поверхности окна формы, что будет выглядеть необычно.

Функции Windows для воспроизведения нуждаются в специальной величине типа HDC (Handle Device Context, ссылка на контекст воспроизведения), для задания значения которой необходимо иметь величину типа HWND - ссылка на окно, уникальный идентификатор всех зарегистрированных в системе окон. В зависимости от версии Delphi ссылки имеют тип либо Integer, либо LongWord.

Графическая система OpenGL, как и любое другое приложение Windows, также нуждается в ссылке на окно, на котором будет осуществляться воспроизведение - специальной ссылке на контекст воспроизведения - величина типа HGLRC (Handle openGL Rendering Context, ссылка на контекст воспроизведения OpenGL). Для получения этого контекста OpenGL нуждается в величине типа HDC (контекст воспроизведения) окна, на который будет осуществляться вывод.

Поэтому наши примеры имеют следующие строки в разделе private описания формы:



DC: HDC;
hrc: HGLRC;




А обработчик события OnCreate формы начинается со следующих строк:



DC := GetDC(Handle);
SetDCPixelFormat;
hrc := wglCreateContext(DC);
wglMakeCurrent(DC, hrc);




То есть мы получаем контекст воспроизведения Windows, задаем желаемый формат пикселей, создаем контекст воспроизведения OpenGL и делаем его текущим, чтобы вызываемые функции OpenGL могли работать с этим окном.

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

Во-первых, величину типа HDC мы получаем при создании окна, в обработчике события OnCreate, или, другими словами, в обработчике сообщения WM_CREATE. Это является обычным и традиционном для Windows-программ.

Некоторые программисты сделали мне замечание, что получение контекста воспроизведения при создании окна является несколько некорректным для Windows 9X и более правильным было бы получение контекста в обработчике событий OnShow или OnPaint. Возможно, это так и есть, и в некоторых ситуациях может сказаться на корректности работы приложения. Вы должны учитывать это при написании ответственных приложений.

Во-вторых, контекст воспроизведения Windows и контекст воспроизведения OpenGL обычно освобождаются приложением. То есть, команды вывода OpenGL обычно обрамляются следующими строками:



dc := BeginPaint(Window, ps);
wglMakeCurrent(DC, hrc);

wglMakeCurrent(0, 0);
EndPaint (Window,ps);
ReleaseDC (Window, dc);




Повторяю, это обычные последовательности действий для Windows-программ, контекст воспроизведения должен быть доступен системе и другим приложениям. Я же во многих примерах пренебрегаю этим правилом для сокращения кода. Вы можете убедиться, что программы работают в общем случае корректно, хотя мы отдаем себе отчет, что в некоторых ситуациях такой подход может привести к "глюковатости" работы приложения. Это также надо учесть при написании ответственных приложений.

В наших примерах контекст воспроизведения OpenGL мы занимаем сразу же при его получении, в обработчике события OnCreate, а освобождаем в конце работы приложения, в обработчике события OnDestroy.

Еще одно замечание - команды и функции OpenGL имеют префикс gl для размещенных в библиотеке opengl32.dll и glu для размещенных в библиотеке glu32.dll. Прототипы этих функций находятся в модуле opengl.pas. Функции OpenGL, имеющие отношение только к реализации OpenGL под Windows, имеют префикс wgl, как, например, wglCreateContext, а некоторые вообще не имеют префикса, например, SwapBuffers. Их прототипы описаны в модуле windows.pas.

Если понятия "сообщение" и "контекст" Вами поняты, сейчас Вы можете разобрать проекты WinMin.dpr и Paint.dpr в каталоге Beginer/0. В списке uses данных проектов перечислены всего два модуля - Windows и Messages (SysUtils в проекте Paint не используется). Это означает, что данные проекты не используют библиотеку VCL Delphi. После компиляции этих проектов Вы получите 16-ти килобайтные приложения. Приложения эти иллюстративные, умеют делать немногое, но для нас важен код проектов, возвращающий во времена старого доброго Borland Pascal-я, громоздкий, плохочитаемый, но эффективный для наших задач. Эти проекты помогают понять новичкам, какую каторожную работу выполняет за нас Delphi, и как в действительности работают Windows-приложения. Проекты я постарался хорошо откомментировать, чтобы Вам было легче разобраться.

Если Вы разберетесь, как рисовать функциями GDI на поверхности своего окна, Вы яснее сможете понять, как машина OpenGL рисует на поверхности чужого окна.

Взято с





Работа с Oracle


Работа с Oracle



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


















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




Работа с Outlook


Работа с Outlook



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







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




Работа с Paradox


Работа с Paradox



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

























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








Работа с печатью в TWebBrowser


Работа с печатью в TWebBrowser





  TWebBrowser can use native IE API to print and do other things. 
  Implement on a Form a TWebBrowser component, and a button to print. 
  The code attached to this button is as follow : 


//-------------------------------------------- 

procedure TForm.OnClickPrint(Sender: TObject); 
begin 
  WebBrowser.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER); 
end; 

//-------------------------------------------- 

You can replace "OLECMDID_PRINT" by other possibilities : 

OLECMDID_OPEN OLECMDID_NEW OLECMDID_SAVE 
OLECMDID_SAVEAS OLECMDID_SAVECOPYAS OLECMDID_PRINT 
OLECMDID_PRINTPREVIEW OLECMDID_PAGESETUP OLECMDID_SPELL 
OLECMDID_PROPERTIES OLECMDID_CUT OLECMDID_COPY 
OLECMDID_PASTE OLECMDID_PASTESPECIAL OLECMDID_UNDO 
OLECMDID_REDO OLECMDID_SELECTALL OLECMDID_CLEARSELECTION 
OLECMDID_ZOOM OLECMDID_GETZOOMRANGE OLECMDID_UPDATECOMMANDS 

OLECMDID_REFRESH OLECMDID_STOP OLECMDID_HIDETOOLBARS 
OLECMDID_SETPROGRESSMAX OLECMDID_SETPROGRESSPOS 
OLECMDID_SETPROGRESSTEXT 

OLECMDID_SETTITLE OLECMDID_SETDOWNLOADSTATE OLECMDID_STOPDOWNLOAD 

OLECMDID_FIND OLECMDID_ONTOOLBARACTIVATED OLECMDID_DELETE 

OLECMDID_HTTPEQUIV OLECMDID_ENABLE_INTERACTION OLECMDID_HTTPEQUIV_DONE 

OLECMDID_ONUNLOAD OLECMDID_PROPERTYBAG2 OLECMDID_PREREFRESH 




Взято с сайта



Работа с перечисляемыми типами


Работа с перечисляемыми типами



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






Работа с Photoshop


Работа с Photoshop





uses
ComObj, ActiveX, PhotoShopTypeLibrary_TLB;

var
  PS: IPhotoShopApplication;
  Unknown: IUnknown;
begin
  Result := GetActiveObject(CLASS_PhotoshopApplication, nil, Unknown);
  if (Result = MK_E_UNAVAILABLE) then
    PS := CoPhotoshopApplication.Create
  else
  begin
    { make sure no other error occurred }
    OleCheck(Result);
    OleCheck(Unknown.QueryInterface(IPhotoShopApplication, PS));
  end;
  PS.Visible := True;
end;

Взято с

Delphi Knowledge Base






Работа с Ping


Работа с Ping



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





Работа с полями


Работа с полями



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










Работа с портами микропроцессора


Работа с портами микропроцессора




Автор: Pavlo Zolotarenki

Модуль для работы с портами микропроцессора с сохранением синтаксиса.
Работает под Win9x.
НЕ работает под WinNT.

//Copyright(c)1998 Zolotarenko P.V pvz@mail.univ.kiev.ua

unit Ports;
interface
type

  TPort = class
  private
    procedure Set_(index_: word; value: byte); register;
    function Get_(index_: word): byte; register;
  public
    property Element[index_: word]: byte read Get_ write Set_; default;
  end;

  TPortW = class
  private
    procedure Set_(index_: word; value: Word); register;
    function Get_(index_: word): word; register;
  public
    property Element[index_: word]: word read Get_ write Set_; default;
  end;

var
  Port: TPort;

  PortW: TportW;

implementation

procedure TPort.Set_(index_: word; value: byte);
begin

  asm
mov dx,index_
mov al,value
out dx,al
  end;
end;

function TPort.Get_(index_: word): byte;
begin

  asm
mov dx,index_
in al,dx
mov @Result,al
  end;
end;

procedure TPortW.Set_(index_: word; value: word);
begin

  asm
mov dx,index_
mov ax,value
out dx,ax
  end;
end;

function TPortW.Get_(index_: word): word;
begin

  asm
mov dx,index_
in ax,dx
mov @Result,ax
  end;
end;

initialization

  Port := TPort.Create;
  PortW := TPortW.Create;

finalization

  Port.free;
  PortW.free;
end.



Взято с





Работа с последовательными портами


Работа с последовательными портами




//{$DEFINECOMM_UNIT}

//Простой пример работы с последовательными портами
//Код содержит интуитивно понятные комментарии и строки на шведском языке,
//нецелесообразные для перевода.
//Compiler maakt Simple_Comm.Dll of Simple_Com.Dcu afhankelijk van 1e Regel
(COMM_UNIT)

{$IFNDEF COMM_UNIT}
library Simple_Comm;
{$ELSE}
unit Simple_Comm;
interface
{$ENDIF}

uses Windows, Messages;

const
  M_BaudRate = 1;
const
  M_ByteSize = 2;
const
  M_Parity = 4;
const
  M_Stopbits = 8;

{$IFNDEF COMM_UNIT}
{$R Script2.Res} //versie informatie
{$ENDIF}

{$IFDEF COMM_UNIT}
function Simple_Comm_Info: PChar; StdCall;
function
  Simple_Comm_Open(Port: PChar; BaudRate: DWORD; ByteSize, Parity, StopBits:
    Byte; Mas
  k: Integer; WndHandle: HWND; WndCommand: UINT; var Id: Integer): Integer;
    StdCall;
function Simple_Comm_Close(Id: Integer): Integer; StdCall;
function
  Simple_Comm_Write(Id: Integer; Buffer: PChar; Count: DWORD): Integer; StdCall;
function Simple_Comm_PortCount: DWORD; StdCall;

const
  M_None = 0;
const
  M_All = 15;

implementation
{$ENDIF}

const
  InfoString = 'Simple_Comm.Dll (c) by E.L. Lagerburg 1997';
const
  MaxPorts = 5;

const
  bDoRun: array[0..MaxPorts - 1] of boolean
  = (False, False, False, False, False);
const
  hCommPort: array[0..MaxPorts - 1] of Integer = (0, 0, 0, 0, 0);
const
  hThread: array[0..MaxPorts - 1] of Integer = (0, 0, 0, 0, 0);
const
  dwThread: array[0..MaxPorts - 1] of Integer = (0, 0, 0, 0, 0);
const
  hWndHandle: array[0..MaxPorts - 1] of Hwnd = (0, 0, 0, 0, 0);
const
  hWndCommand: array[0..MaxPorts - 1] of UINT = (0, 0, 0, 0, 0);
const
  PortCount: Integer = 0;

function Simple_Comm_Info: PChar; stdcall;
begin

  Result := InfoString;
end;

//Thread functie voor lezen compoort

function Simple_Comm_Read(Param: Pointer): Longint; stdcall;
var
  Count: Integer;

  id: Integer;
  ReadBuffer: array[0..127] of byte;
begin

  Id := Integer(Param);
  while bDoRun[id] do
  begin
    ReadFile(hCommPort[id], ReadBuffer, 1, Count, nil);
    if (Count > 0) then
    begin
      if ((hWndHandle[id] <> 0) and
        (hWndCommand[id] > WM_USER)) then

        SendMessage(hWndHandle[id], hWndCommand[id], Count,
          LPARAM(@ReadBuffer));

    end;
  end;
  Result := 0;
end;

//Export functie voor sluiten compoort

function Simple_Comm_Close(Id: Integer): Integer; stdcall;
begin

  if (ID < 0) or (id > MaxPorts - 1) or (not bDoRun[Id]) then
  begin
    Result := ERROR_INVALID_FUNCTION;
    Exit;
  end;
  bDoRun[Id] := False;
  Dec(PortCount);
  FlushFileBuffers(hCommPort[Id]);
  if not
    PurgeComm(hCommPort[Id], PURGE_TXABORT + PURGE_RXABORT + PURGE_TXCLEAR +
      PURGE_RXCL
    EAR) then

  begin
    Result := GetLastError;
    Exit;
  end;
  if WaitForSingleObject(hThread[Id], 10000) = WAIT_TIMEOUT then
    if not TerminateThread(hThread[Id], 1) then
    begin
      Result := GetLastError;
      Exit;
    end;

  CloseHandle(hThread[Id]);
  hWndHandle[Id] := 0;
  hWndCommand[Id] := 0;
  if not CloseHandle(hCommPort[Id]) then
  begin
    Result := GetLastError;
    Exit;
  end;
  hCommPort[Id] := 0;
  Result := NO_ERROR;
end;

procedure Simple_Comm_CloseAll; stdcall;
var
  Teller: Integer;
begin

  for Teller := 0 to MaxPorts - 1 do
  begin
    if bDoRun[Teller] then
      Simple_Comm_Close(Teller);
  end;
end;

function GetFirstFreeId: Integer; stdcall;
var
  Teller: Integer;
begin

  for Teller := 0 to MaxPorts - 1 do
  begin
    if not bDoRun[Teller] then
    begin
      Result := Teller;
      Exit;
    end;
  end;
  Result := -1;
end;

//Export functie voor openen compoort

function
  Simple_Comm_Open(Port: PChar; BaudRate: DWORD; ByteSize, Parity, StopBits:
    Byte; Mas
  k: Integer; WndHandle: HWND; WndCommand: UINT; var Id: Integer): Integer;
    stdcall;

var
  PrevId: Integer;
  ctmoCommPort: TCOMMTIMEOUTS; //Lees specificaties voor de compoort
  dcbCommPort: TDCB;
begin

  if (PortCount >= MaxPorts) or (PortCount < 0) then
  begin
    result := error_invalid_function;
    exit;
  end;
  result := 0;
  previd := id;
  id := getfirstfreeid;
  if id = -1 then
  begin
    id := previd;
    result := error_invalid_function;
    exit;
  end;
  hcommport[id] := createfile(port, generic_read or
    generic_write, 0, nil, open_existing, file_attribute_normal, 0);

  if hcommport[id] = invalid_handle_value then
  begin
    bdorun[id] := false;
    id := previd;
    result := getlasterror;
    exit;
  end;
  //lees specificaties voor het comm bestand
  ctmocommport.readintervaltimeout := maxdword;
  ctmocommport.readtotaltimeoutmultiplier := maxdword;
  ctmocommport.readtotaltimeoutconstant := maxdword;
  ctmocommport.writetotaltimeoutmultiplier := 0;
  ctmocommport.writetotaltimeoutconstant := 0;
  //instellen specificaties voor het comm bestand
  if not setcommtimeouts(hcommport[id], ctmocommport) then
  begin
    bdorun[id] := false;
    closehandle(hcommport[id]);
    id := previd;
    result := getlasterror;
    exit;
  end;
  //instellen communicatie
  dcbcommport.dcblength := sizeof(tdcb);
  if not getcommstate(hcommport[id], dcbcommport) then
  begin
    bdorun[id] := false;
    closehandle(hcommport[id]);
    id := previd;
    result := getlasterror;
    exit;
  end;
  if (mask and m_baudrate <> 0) then
    dcbCommPort.BaudRate := BaudRate;
  if (Mask and M_ByteSize <> 0) then
    dcbCommPort.ByteSize := ByteSize;
  if (Mask and M_Parity <> 0) then
    dcbCommPort.Parity := Parity;
  if (Mask and M_Stopbits <> 0) then
    dcbCommPort.StopBits := StopBits;
  if not SetCommState(hCommPort[Id], dcbCommPort) then
  begin
    bDoRun[Id] := FALSE;
    CloseHandle(hCommPort[Id]);
    Id := PrevId;
    Result := GetLastError;
    Exit;
  end;
  //Thread voor lezen compoort
  bDoRun[Id] := TRUE;

  hThread[Id] := CreateThread(nil, 0, @Simple_Comm_Read, Pointer(Id), 0,
    dwThread[Id]
    );

  if hThread[Id] = 0 then
  begin
    bDoRun[Id] := FALSE;
    CloseHandle(hCommPort[Id]);
    Id := PrevId;
    Result := GetLastError;
    Exit;
  end
  else
  begin
    SetThreadPriority(hThread[Id], THREAD_PRIORITY_HIGHEST);
    hWndHandle[Id] := WndHandle;
    hWndCommand[Id] := WndCommand;
    Inc(PortCount);
    Result := NO_ERROR;
  end;
end;

//Export functie voor schrijven naar compoort;

function
  Simple_Comm_Write(Id: Integer; Buffer: PChar; Count: DWORD): Integer; stdcall;
var
  Written: DWORD;
begin

  if (Id < 0) or (id > Maxports - 1) or (not bDoRun[Id]) then
  begin
    Result := ERROR_INVALID_FUNCTION;
    Exit;
  end;
  if not WriteFile(hCommPort[Id], Buffer, Count, Written, nil) then
  begin
    Result := GetLastError();
    Exit;
  end;
  if (Count <> Written) then
    Result := ERROR_WRITE_FAULT
  else
    Result := NO_ERROR;
end;

//Aantal geopende poorten voor aanroepende applicatie

function Simple_Comm_PortCount: DWORD; stdcall;
begin

  Result := PortCount;
end;

{$IFNDEF COMM_UNIT}
exports

  Simple_Comm_Info Index 1,
  Simple_Comm_Open Index 2,
  Simple_Comm_Close Index 3,
  Simple_Comm_Write Index 4,
  Simple_Comm_PortCount index 5;

procedure DLLMain(dwReason: DWORD);
begin

  if dwReason = DLL_PROCESS_DETACH then
    Simple_Comm_CloseAll;
end;

begin

  DLLProc := @DLLMain;
  DLLMain(DLL_PROCESS_ATTACH); //geen nut in dit geval
end.

{$ELSE}
initialization
finalization

  Simple_Comm_CloseAll;
end.
{$ENDIF}

Другое решение: создание модуля I / O(ввода / вывода)под Windows 95 / NT.Вот он:
  )

(с TDCB в SetCommStatus вы можете управлять DTR и т.д.)
(Примечание: XonLim и XoffLim не должны быть больше 600, иначе под NT это
  работает неправильно)

unit My_IO;

interface

function OpenComm(InQueue, OutQueue, Baud: LongInt): Boolean;
function SetCommTiming: Boolean;
function SetCommBuffer(InQueue, OutQueue: LongInt): Boolean;
function SetCommStatus(Baud: Integer): Boolean;
function SendCommStr(S: string): Integer;
function ReadCommStr(var S: string): Integer;
procedure CloseComm;

var

  ComPort: Word;

implementation

uses Windows, SysUtils;

const

  CPort: array[1..4] of string = ('COM1', 'COM2', 'COM3', 'COM4');

var

  Com: THandle = 0;

function OpenComm(InQueue, OutQueue, Baud: LongInt): Boolean;
begin

  if Com > 0 then
    CloseComm;
  Com := CreateFile(PChar(CPort[ComPort]),
    GENERIC_READ or GENERIC_WRITE,
    0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  Result := (Com > 0) and SetCommTiming and
    SetCommBuffer(InQueue, OutQueue) and
    SetCommStatus(Baud);
end;

function SetCommTiming: Boolean;
var

  Timeouts: TCommTimeOuts;

begin

  with TimeOuts do
  begin
    ReadIntervalTimeout := 1;
    ReadTotalTimeoutMultiplier := 0;
    ReadTotalTimeoutConstant := 1;
    WriteTotalTimeoutMultiplier := 2;
    WriteTotalTimeoutConstant := 2;
  end;
  Result := SetCommTimeouts(Com, Timeouts);
end;

function SetCommBuffer(InQueue, OutQueue: LongInt): Boolean;
begin

  Result := SetupComm(Com, InQueue, OutQueue);
end;

function SetCommStatus(Baud: Integer): Boolean;
var

  DCB: TDCB;

begin

  with DCB do
  begin
    DCBlength := SizeOf(Tdcb);
    BaudRate := Baud;
    Flags := 12305;
    wReserved := 0;
    XonLim := 600;
    XoffLim := 150;
    ByteSize := 8;
    Parity := 0;
    StopBits := 0;
    XonChar := #17;
    XoffChar := #19;
    ErrorChar := #0;
    EofChar := #0;
    EvtChar := #0;
    wReserved1 := 65;
  end;
  Result := SetCommState(Com, DCB);
end;

function SendCommStr(S: string): Integer;
var

  TempArray: array[1..255] of Byte;
  Count, TX_Count: Integer;

begin

  for Count := 1 to Length(S) do
    TempArray[Count] := Ord(S[Count]);
  WriteFile(Com, TempArray, Length(S), TX_Count, nil);
  Result := TX_Count;
end;

function ReadCommStr(var S: string): Integer;
var

  TempArray: array[1..255] of Byte;
  Count, RX_Count: Integer;

begin

  S := '';
  ReadFile(Com, TempArray, 255, RX_Count, nil);
  for Count := 1 to RX_Count do
    S := S + Chr(TempArray[Count]);
  Result := RX_Count;
end;

procedure CloseComm;
begin

  CloseHandle(Com);
  Com := -1;
end;

end.

Взято с





Работа с приложениями MS Office


Работа с приложениями MS Office



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


·
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  



·
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·

 



·  
·  
·  







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





Работа с принтером.


Работа с принтером.



Delphi имеет стандартный объект для доступа к принтеру - TPRINTER,
находящийся в модуле PRINTERS. В этом модуле имеется
переменная Printer:Tpinter, что избавляет от необходимости описывать свою.
Он позволяет выводить данные на печать и управлять процессом печати.
Правда, в некоторых версиях Delphi 1 он имеет "глюк" - не работают
функции Draw и StrethDraw. Но эта проблема поправима - можно
использовать функции API. Далее приведены основные поля и методы объекта Printers :
PROPERTY
Aborted:boolean - Показывает, что процесс печати прерван
Canvas:Tcanvas - Стандартный Canvas, как у любого графического объекта.
Он позволяет рисовать на листе бумаге графику, выводить текст ... .
Тут есть несколько особенностей, они описаны после описания объекта.
Fonts:Tstrings - Возвращает список шрифтов, поддерживаемых принтером
Handle:HDS - Получить Handle на принтер для использования функций API (см. Далее)
Orientation:TprinterOrientation - Ориентация листа при печати : (poPortrait, poLandscape)
PageHeight:integer - Высота листа в пикселах
PageNumber:integer - Номер страницы, увеличивается на 1 при каждом NewPage
PageWidth:integer - Ширина листа в пикселах
PrinterIndex:integer - Номер используемого принтера по списку доступных принтеров Printers
Printers:Tstrings - Список доступных принтеров
Printing:boolean - Флаг, показывающий, что сейчас идет процесс печати
Title:string - Имя документа или приложения. Под этим именем задание на печать
регистрируется в диспетчере печати

METODS
AssignPrn(f:TextFile) - Связать текстовый файл с принтером.
Далее вывод информации в этот файл приводит к ее печати.
Удобно в простейших случаях.
Abort - Сбросить печать
BeginDoc - Начать печать
NewPage - Начать новую страницу
EndDoc - Завершить печать.

Пример :




Procedure TForm1.Button1Click(Sender: TObject);
Begin
 With Printer do Begin
  BeginDoc; { Начало печати }
  Canvas.Font:=label1.font; { Задали шрифт }
  Canvas.TextOut(100,100,'Это тест принтера !!!'); { Печатаем текст }
  EndDoc; { Конец печати }
 end;
end;


Особенности работы с TPrinter


1. После команды BeginDoc шрифт у Canvas принтера сбрасывается и
его необходимо задавать заново
2. Все координаты даны в пикселах, а для нормальной работы необходимы
миллиметры (по двум очевидным причинам: очень трудно произвести
разметку страницы в пикселах (особенно если необходима точность), и , главное,
при изменении разрешающей способности принтера будет изменяться число точек
на дюйм, и все координаты "поедут".
3. У TPrinter информация о принтере, по видимому, определяются один раз
- в момент запуска программы (или смены принтера). Поэтому изменение настроек
принтера в процессе работы программы может привести к некорректной работе,
например, неправильной печать шрифтов True Type.
Определение параметров принтера через API
Для определения информации о принтере (плоттере, экране) необходимо
знать Handle этого принтера, а его можно узнать объекта TPrinter - Printer.Handle.
Далее вызывается функция API (unit WinProcs) : GetDevice(Handle:HDC; Index:integer):integer;
Index - код параметра, который необходимо вернуть.
Для Index существует ряд констант :
DriverVersion - вернуть версию драйвера
Texnology - Технология вывода, их много, основные
dt_Plotter - плоттер
dt_RasPrinter - растровый принтер
dt_Display - дисплей
HorzSize - Горизонтальный размер листа (в мм)
VertSize - Вертикальный размер листа (в мм)
HorzRes - Горизонтальный размер листа (в пикселах)
VertRes - Вертикальный размер листа (в пикселах)
LogPixelX - Разрешение по оси Х в dpi (пиксел /дюйм)
LogPixelY - Разрешение по оси Y в dpi (пиксел /дюйм)
Кроме перечисленных еще около сотни, они позволяют узнать о принтере практически все.
Параметры, возвращаемые по LogPixelX и LogPixelY очень важны - они
позволяют произвести пересчет координат из миллиметров в пиксели
для текущего разрешения принтера. Пример таких функций:

Procedure TForm1.GetPrinterInfo; { Получить информацию о принтере }
begin
  PixelsX:=GetDeviceCaps(printer.Handle,LogPixelsX);
  PixelsY:=GetDeviceCaps(printer.Handle,LogPixelsY);
end;

Function TForm1.PrinterCoordX(x:integer):integer; { переводит координаты из мм в пиксели }
begin
 PrinterCoordX:=round(PixelsX/25.4*x);
end;

Function TForm1.PrinterCoordY(Y:integer):integer; { переводит координаты из мм в пиксели }
begin
 PrinterCoordY:=round(PixelsY/25.4*Y);
end;



GetPrinterInfo;

Printer.Canvas.TextOut(PrinterCoordX(30), PrinterCoordY(55),

'Этот текст печатается с отступом 30 мм от левого края и '+

'55 мм от верха при любом разрешении принтера');

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


P.S. Мой комментарий.
Я производил печать следующим образом:

procedure TForm6.SpeedButton1Click(Sender: TObject);
var
  PRect: Trect;
  PBitMap: TBitmap;
begin
  PBitmap := TBitMap.Create;
  PBitmap.LoadFromFile('C:\1.bmp');
  with PRect do
    begin
      left := 0;
      top := 0;
      right := Printer.PageWidth;
      Bottom := Printer.PageHeight;
    end;
  with printer do
    begin
      BeginDoc;
      font.name := 'Times New Roman';
      Canvas.StretchDraw(PRect, Bitmap);
      EndDoc;
    end;
  PBitmap.Free;

end;

Удачи!
DenKop@mail.ru

Взято с сайта



Работа с различными приложениями (не MS Office)


Работа с различными приложениями (не MS Office)



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










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





Работа с ресурсами


Работа с ресурсами



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












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








Работа с Sender


Работа с Sender




unitTestInputForm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, DdhInpuB;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Label1: TLabel;
    DdhInputButton1: TDdhInputButton;
    DdhInputButton2: TDdhInputButton;
    DdhInputButton3: TDdhInputButton;
    procedure DdhInputButtonClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.DdhInputButtonClick(Sender: TObject);
begin
  ShowMessage ('You have clicked the ' +
    (Sender as TButton).Name + ','#13 +
    'having the caption ' +
    (Sender as TButton).Caption);
end;

end.

Взято с





Работа с сессиями


Работа с сессиями



Each function listed below returns information about a session or performs a task that affects the session, such as starting a session or adding a password.



DbiAddPassword:
Adds a password to the current session.

DbiCheckRefresh:
Checks for remote updates to tables for all cursors in the current session, and refreshes the cursors
if changed.

DbiCloseSession:
Closes the session associated with the given session handle.

DbiDropPassword:
Removes a password from the current session.

DbiGetCallBack:
Returns a pointer to the function previously registered by the client for the given callback type.

DbiGetCurrSession:
Returns the handle associated with the current session.

DbiGetDateFormat:
Gets the date format for the current session.

DbiGetNumberFormat:
Gets the number format for the current session.

DbiGetSesInfo:
Retrieves the environment settings for the current session.

DbiGetTimeFormat:
Gets the time format for the current session.

DbiRegisterCallBack:
Registers a callback function for the client application.

DbiSetCurrSession:
Sets the current session of the client application to the session associated with hSes.

DbiSetDateFormat:
Sets the date format for the current session.

DbiSetNumberFormat:
Sets the number format for the current session.

DbiSetPrivateDir:
Sets the private directory for the current session.

DbiSetTimeFormat:
Sets the time format for the current session.

DbiStartSession:
Starts a new session for the client application.


Взято с

Delphi Knowledge Base




Работа с сетью, интернетом, протоколами


Работа с сетью, интернетом, протоколами


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




·

(раздел)

·

(раздел)

·

(раздел)

·

(раздел)

·

(раздел)

·

(раздел)

·

(раздел)

·

(раздел)




·

(раздел)
·  
·  


·

(раздел)
·  
·  
·  
·  
·  


·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  


·  
·  
·  
·  
·  
·  
·  


·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  



·

(раздел)

·  
·  
·  
·  


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





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





Работа с сокетами


Работа с сокетами



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

















Работа с сотовыми телефонами


Работа с сотовыми телефонами





Взаимодействие с мобильными телефонами
Вы, наверное, не раз видели или даже пользовались программами, которые отображают любую информацию о вашем телефоне. Сейчас вы узнаете, как самим сделать такую программу!

Для начала положим на форму Memo, CheckBox "Соединиться», кнопку «Послать команду», Edit.

А) Подключение

Итак, в «Public declarations» объявляем 2 функции и 2 процедуры, потом объявляем 4 глобальные переменные:


public
{ Public declarations }

function OpenCOMPort: Boolean;
  function SetupCOMPort: Boolean; //для настройки порта
    procedure Connect;
      procedure Disconnect;
        …
        var
        Form1: TForm1;
        ComFile: THandle; //Хэндл создаваемого нами файла
        ComString: string; //(COM1, COM2 или COM3)
        ComSpeed: Integer; //Скорость взаимодействия с COM-портом
        Status: Boolean; //подключен или не подключен (чтобы в дальнейшем проверять статус)

Жмём Ctrl+C и записываем дальше:

procedure TForm1.Connect;
begin
  ComString := 'COM2';
  ComSpeed := 19200;
  if OpenCOMPort = true then //Открываем порт…
    if SetupCOMPort = true then //…и конфигурируем его
      Memo1.Lines.Add('Подключились...');
  Sleep(1500); //засыпаем на полторы секунды чтобы дать время на соединение
end;

procedure TForm1.Disconnect;
begin
  CloseHandle(ComFile);
  Memo1.Lines.Add('Отключились.');
end;

function TForm.OpenCOMPort: Boolean;
var DeviceName: array[0..80] of Char;
  Device: string;
begin
  Device := ComString;
  StrPCopy(DeviceName, Device);
  ComFile := CreateFile(DeviceName,
    GENERIC_READ or GENERIC_WRITE,
    0,
    nil,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    0);
  if ComFile = INVALID_HANDLE_VALUE then
    begin
      Result := False;
      Status := Result;
    end
  else
    begin
      Result := True;
      Status := Result;
    end;

end;

function TForm1.SetupCOMPort: Boolean;
const RxBufferSize = 256;
  TxBufferSize = 256;
var DCB: TDCB;
  Config: string;
  CommTimeouts: TCommTimeouts;
begin
  Result := True;
  if not SetupComm(ComFile, RxBufferSize, TxBufferSize) then
    Result := False;
  if not GetCommState(ComFile, DCB) then
    Result := False;

  Config := 'baud=' + IntToStr(ComSpeed) + ' parity=n data=8 stop=1'; //Устанавливаем скорость
  if not BuildCommDCB(@Config[1], DCB) then
    Result := False;
  if not SetCommState(ComFile, DCB) then
    Result := False;

  with CommTimeouts do
    begin
      ReadIntervalTimeout := 0;
      ReadTotalTimeoutMultiplier := 0;
      ReadTotalTimeoutConstant := 1000;
      WriteTotalTimeoutMultiplier := 0;
      WriteTotalTimeoutConstant := 1000;
    end;
  if not SetCommTimeouts(ComFile, CommTimeouts) then
    Result := False;
end;

Теперь два раза щёлкаем по CheckBox и записываем код:

procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  if CheckBox1.Checked then
    Connect
  else
    Disconnect;
end;

В событии формы OnDestroy записываем:

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if Status = true then //При выходе из программы отключаемся
    Disconnect;
end;

Б) Шлём команды и принимаем ответы

Щёлкаем два раза по кнопке «Послать команду» и записываем код:

procedure TForm1.Button1Click(Sender: TObject);
var BytesWritten: DWORD;
  s: string;
  d: array[1..1500] of Char;
  BytesRead: DWORD;
  i: Integer;
  Result: string;
begin
  s := Edit1.Text; //Берём команды из Edit1…
  s := s + #13 + #10;
  WriteFile(ComFile, s[1], Length(s), BytesWritten, nil); //…и посылаем их телефону
  Result := '';
  if not ReadFile(ComFile, d, SizeOf(d), BytesRead, nil) then
    begin
      MessageDlg('Ошибка чтения!', mtError, [mbOK], 0);
        exit;
    end;

  s := '';
  for i := 1 to BytesRead do //Считываем ответ от телефона
    s := s + d[I];
  Result := s;
  Memo1.Lines.Add(Result); //Выводим ответ от телефона в Memo
end;

Вот и всё! Теперь подключите телефон, запускайте программу, ставьте галку в CheckBox'е, и, после того, как вам написали в Memo, что вы подключились вводите в Edit любую AT-команду и жмите «Послать команду». Удачи!

В) Некоторые полезные команды AT

Этими командами вы можете воспользоваться, для посылки телефону (из поля Edit):

AT+CGMI ? производитель
AT+CGMM ? модель телефона
AT+CPAS ? состояние
AT+COPS? ? оператор
AT+CGSN ? номер IMEI
AT+CGMR ? версия прошивки
AT+CBC ? степень зарядки телефона
AT+CREG? ? статус сети
AT^SCID ? номер SIM-карты
AT+CIMI - номер IMSI
AT^SPIC ? попыток до блокировки SIM-карты

Более подробно о командах вы сможете узнать из pdf-инструкции s35i_c35i_m35i_atc_commandset_v01.pdf (можно утащить по адресу: http://www.like.e-technik.uni-erlangen.de/...andset_v01.pdf)

Примечания: Автор: Лазуткин Алексей (alessio19@mail.ru), помощь в написании: av3nger (av3nger@hakep.com)




Работа с SQL


Работа с SQL


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









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





Работа с SSH


Работа с SSH



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