Часто задаваемые общие вопросы по Borland Delphi


Часть 3


Object Pascal и Windows API
  1. Как работает информация времени выполнения (RTTI)?
  2. Как работает обработка исключительных ситуаций в Delphi?
  3. Есть ли простой способ перехватить exception?
  4. Delphi используют строки в стиле Pascal или C?
  5. Есть ли в Delphi битовые множества?
  6. Проблема с числом типа Single в DLL.
  7. Как заставить приложение Delphi отвечать на сообщения Windows?
  8. Как обработать события от других приложений?
  9. Как перехватить сообщения Windows и обработать их перед тем, как выполнится строка Application.Run?
  10. Проблема с DragDrop для внешних программ.
  11. Как обрабатывать WM_DROPFILES (Drag/Drop)?
  12. Как может выделить время CPU другим задачам , подобно "DoEvents" в VB?
  13. В каком порядке происходят события при создании и показе окна?
  14. UpCase для русского языка.
  15. Приложение, написанное на Delphi, не запускается минимизированным.
  16. Объясните разницу в помещении uses в секцию interface или implementation.
  17. Как спрятать окна MDI Child?
  18. Как убрать заголовок у формы MDIChild?
  19. Сохранение данных в Clipboard.
  20. Что означает Key<>#0 ?
  21. Аналог процедуры TP/BP Delay.
  22. Каким образом создать форму, которую можно таскать за поле?
  23. Как программно спрятать или показать заголовок у формы?
  24. Как сделать приложение модальным?
  25. Как изменить шрифт у Application.Title (заголовка приложения)?
  26. Каким образом (желательно не специфичным для Delphi) узнать, открыто меню или нет?
Разное 
  1. Передача переменной в отчет ReportSmith.
  2. Как получить русские буквы в DBD?
  3. Как печатать отчеты из приложения Delphi без использования ReportSmith?
  4. Как узнать количество точек на дюйм для принтера?
  5. Как определить, приложение запущено из под Delphi IDE или как отдельный файл?
  6. Что нужно предусмотреть при разработке приложения, которое будет работать при различном разрешении дисплея?
  7. Конвертация ICO в BMP.
  8. Когда используется свойство Glyph, как узнать, какой цвет прозрачный?
  9. Как отобразить bitmap в 256 цветах?
  10. Если я хочу рассылать EXE файл, созданный в Delphi, какие еще файлы нужно посылать с ним?
Полезные хитрости
  1. Может ли редактор текстов в Delphi вырезать и вставлять прямоугольные фрагменты текста?
  2. Редактирование файлов SQL в Delphi IDE.
  3. Встроенный отладчик/дизассемблер.

Object Pascal и Windows API


1. Как работает информация времени выполнения (RTTI)?

Имеются два новых оператора : as и is. as  - оператор защищенного преобразования типов (typecasting). Вы можете использовать его, чтобы заставить компилятор преобразовать объект из одного типа в другой, но, если в во время выполнения эти типы окажутся несовместимыми, то вы получите ошибку. Hапример, если вы имеете класс TSport, с потомоками TBasketball и TFootball, вам может потребоваться переменная типа TSport; далее может так случиться, что в программе эта переменная будет фактически содержать экземпляр типа TFootball. Тогда вы можете обратиться к этой переменной

    (MySport as TFootball)
чтобы получить доступ к специфическим свойствам из типа TFootball. Однако, если вы ошиблись и на самом деле это экземпляр типа TBasketball, то при обращении к несуществующим свойствам будет возникать ошибка. Оператор is определяет, принадлежит ли экземпляр объекта к данному классу, либо к классу одного из его предков, и используется для проверки, сработает ли преобразование типов с данным объектом. Если вы имеете переменную MySport типа TSport, и в настоящее время она содержит экземпляр TBasketball, тогда следующие выражения истинны:
    (MySport is TSport)
    (MySport is TBasketball)
    not (MySport is TFootball)
Следует иметь ввиду, что компилятор разрешает использовать данные конструкции только для выполнения преобразования типов, связанных родственными отношениями. Так, конструкция
    (Button1 as TEdit)
(переменная Button1 имеет тип TButton) вызовет ошибку компиляции, так как ни при каких условиях не может быть выполнено преобразование типов от TButton к TEdit или наоборот. Комбинация двух операторов может привести к выражению типа следующего :
 
function PlayerGoodness(var MySport: TSport): Integer; 
begin 
  if (MySport is TBasketball) then 
    Result := (MySport as TBasketball).ReboundShots 
  else if (MySport is TFootball) then 
    Result := (MySport as TFootball).TotalYardage; 
end; 
 
     
Также, базовый класс TObject имеет набор методов, которые возвращают информацию, созданную компилятором в момент компиляции текста для поддержки RTTI. Hапример, метод TObject.ClassName возвращает имя класса любого объекта, наследованного от TObject. Hапример, TButton.ClassName вернет значение 'TButton'
2. Как работает обработка исключительных ситуаций в Delphi?

Основная структура выглядит примерно так:
 
P := New(BigThing); 
try 
  try 
    Proc1(P); 
    Proc2(P); 
  except 
    Handle(P); 
    raise; 
  end; 
finally 
  Dispose(P); 
end; 
 

     
Первая строка распределяет большой блок памяти. Затем, в блоке try, выполняется несколько операторов, каждый из которых может вызвать ошибку, или, другими словами, "вызвать исключительную ситуацию". Если возникает ошибка, оставшаяся часть блока try пропускается, и выполняются блоки except и finally. Если ошибок нет, то после выполнения всех операторов в блоке try выполнится блок finally. В любом случае, блок памяти будет освобожден. Блок try ... finally ловит все, включая Windows GPF или Access Violation. Обратите внимание на вызов raise в блоке try ... except. Он снова вызывает исключительную ситуацию, которая вызовет сообщение об ошибке после того, когда закончится блок finally. Если не вызвать raise, то считается, что вы обработали исключительную ситуацию самостоятельно в пределах блока except.

3. Есть ли простой способ перехватить exception?

Создайте метод для формы, перехватывающий исключения. Этот метод будет вызываться обработчиком OnException объекта Application. В вашем методе проверьте, тот ли это исключение, что вы ожидаете, например EDatabaseError. Почитайте on-line help для события OnException. Там есть информация, как вызвать собственный метод для события.
 
procedure TForm1.MyExcept(Sender: TObject; E: Exception); 
begin 
  if E is EDatabaseError then MessageDlg('Поймали exception', mtInformation, [mbOk], 0) 
{ это не то, сделать raise } 
  else raise E; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
  Application.OnException := MyExcept; 
{ здесь вы указываете, что событие OnException выполнит ваш метод } 
end; 


4. Delphi используют строки в стиле Pascal или C?

И те и другие. Delphi имеет два различных набора функций манипулирования строками, один - для PChar; но в Delphi также есть функция MessageDlg, которая принимает строки типа Pascal.

2.0Delphi 2.0 добавляет так называемые длинные строки (AnsiString), которыми можно манипулировать как обычными строками в Pascal, но они имеют динамически изменяющийся размер и могут быть размером до 4Гбайт. Можно выполнять преобразования от PChar к AnsiString и наоборот. Старый строковый тип теперь называется ShortString. По умолчанию кличевое слово string соответствует типу AnsiString.

5. Есть ли в Delphi битовые множества?

В явном виде битовых множеств в языке Object Pascal нет. Но вместо этого можно использовать обычные множества, которые на самом деле и хранятся как битовые. Если множество вам нужно для проверки, установлен ли какой то бит в слове (байте и т.д.) можно попробовать такую конструкцию:
 
type 
  PByteSet = ^TByteSet; 
  TByteSet = set of Byte; 

var 
  W: Word; 
... 
{ если бит 3 в слове W установлен, тогда ... } 
  if 3 in PByteSet(@W)^ then ... 
... 

 

     
2.0В Delphi 2.0 есть специальный класс TBitSet, который ведет себя как битовое множество.Для Delphi 1.0 вы можете написать такой класс самостоятельно.

6. Проблема с числом типа Single в DLL.

Q:Я написал на C++ DLL, в которой у меня функция использует число типа float, передал из Delphi число типа Single и получил GPF 'Invalid Opcode'. Что неправильно?

A:Если вы используете числа с плавающей точкой, лучше передавать их не по значению, а по ссылке (указатель в C++). Вероятно DLL написана на MS Visual C++, так как Microsoft и Borland используют разные соглашения о передаче параметров при работе с сопроцессором. В случае Borland C++ и Delphi должны использовать одинаковый способ передачи параметров и значений (через стек сопроцессора). В любом случае вместо Single лучше использовать Double (double или long float в C++), так как вообще говоря, реальный тип, который соответствует типу Single точно не определен и может измениться в будущем.
7. Как заставить приложение Delphi отвечать на сообщения Windows?

Используем сообщение WM_WININICHANGED в качестве примера. Объявление метода в TForm позволит вам обрабатывать сообщение WM_WININICHANGED:

    procedure WMWinIniChange(var Message: TMessage); message WM_WININICHANGE;
Код в implementation может выглядеть так:
 
procedure TForm1.WMWinIniChange(var Message: TMessage); 
begin 
  inherited; 
{ ... ваша реакция на событие ... } 
end; 
 
     
Вызов inherited метода очень важен. Обратите внимание также на то, что для функций, объявленных с директивой message (обработчиков событий Windows) после inherited нет имени наследуемой процедуры, потому что она может быть неизвестна или вообще отсутствовать (в этом случае вы в действительности вызываете процедуру DefaultHandler).

8. Как обработать события от других приложений?

Попробуйте сделать это следующим образом:
 
type 
  TForm1 = class(TForm) 
  ... 
  private 
    procedure WMNCActivate(var Msg: TMessage); message WM_NCACTIVATE; 
  end; 

procedure TForm1.WMNCActivate(var Msg: TMessage); 
begin 
{ здесь обработка принятых событий } 
end; 


9. Как перехватить сообщения Windows и обработать их перед тем, как выполнится строка Application.Run?

Пример проекта показывает, как получить сообщения Windows в данном случае. Это редкий случай, в большинстве случаев переопределение процедуры Application.OnMessage будет делать то же самое.
 
program Project1; 

uses 
  Forms, 
  Unit1 in 'UNIT1.PAS' { Form1 }, 
  Messages, WinTypes, WinProcs, 

{$R *.RES} 

var 
  OldWndProc: TFarProc; 

function NewWndProc(hWndAppl: HWnd; Msg, wParam: Word; lParam: Longint): Longint; export; 
begin 
{ default WndProc return value } 
  Result := 0; 
{ handle messages here; the message number is in Msg } 
  Result := CallWindowProc(OldWndProc, hWndAppl, Msg, wParam, lParam); 
end; 

begin 
  Application.CreateForm(TForm1, Form1); 
  OldWndProc := TFarProc(GetWindowLong(Application.Handle, GWL_WNDPROC)); 
  SetWindowLong(Application.Handle, GWL_WNDPROC, Longint(@NewWndProc)); 
  Application.Run; 
end. 


10. Проблема с DragDrop для внешних программ.

Q:Я пишу небольшую программку - "мусорную корзину". В FormCreate вызывается DragAcceptFiles(HANDLE, True). Проблема в том, что когда размер окна восстанавливается и затем минимизируется Drag and Drop перестает работать. Я безуспешно пробовал помещать DragAcceptFiles в разные методы формы. Однако если сделать вызов DragAcceptFiles(Application.Handle, True) в MainForm.Create, то все работает. Как перехватить событие WM_DROPFILES ?

A:Это можно сделать так:
 
type 
  TMainForm = class(TForm) 
  ... 
    procedure FormCreate(Sender: TObject); 
  private 
    procedure DropFiles(var Msg : TWMDropFiles); message WM_DROPFILES; 
  end; 
 

procedure TMainForm.DropFiles(var Msg : TWMDropFiles); 
begin 
  DragQueryPoint(Msg.Drop, Point); 
  NrOfFiles := DragQueryFile(Msg.Drop, Word(-1), FileName, BufSize); 
  DragQueryFile(Msg.Drop, 0, FileName, BufSize); 
end; 

procedure TMainForm.FormCreate(Sender: TObject); 
begin 
  DragAcceptFiles(Handle, True); 
end; 

 

     
Подробнее о перехвате событий Windows см. Главу 7 руководства Component Writers Guide.

11. Как обрабатывать WM_DROPFILES (Drag/Drop)?

Следующий код показывает как обрабатывать это событие. Обрабатываются имена всех "брошенных" файлов. Для загрузки каждого файла вызывается CreateChild(FName). В обработчике OnCreate данной формы вы должны вызвать DragAcceptFiles.
 
type 
  TFrameForm = class(TForm) 
  ... 
  protected 
    procedure WMDropFiles(var Msg: TMessage); message WM_DROPFILES; 
  end; 

procedure TFrameForm.WMDropFiles(var Msg : TMessage); 
var 
  I, N, Size: Word; 
  FName: string; 
  HDrop: Word; 
begin 
  HDrop := Msg.WParam; 
  N := DragQueryFile(HDrop, $FFFF, nil, 0); 
  for I := 0 to (N-1) do 
  begin 
    Size := DragQueryFile(HDrop, I, nil, 0); 
    if Size < 255 then { 255 char. string limit - not really a problem } 
    begin 
      FName[0] := Chr(Size); 
      DragQueryFile(HDrop, I, @FName[1], Size+1); 
      CreateChild(FName); 
    end; 
  end; 
  Msg.Result := 0; 
  inherited; 
end; 

 

     

12. Как может выделить время CPU другим задачам , подобно "DoEvents" в VB?

Эквивалент в Delphi - Application.ProcessMessages.

Если вы выполняете долгие вычисления, то вызов данного метода позволит в Win 16 выполняться параллельно другим приложениям, а в Win 32 - корректно перерисовываться вашему приложению.

13. В каком порядке происходят события при создании и показе окна?

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

  • OnCreate
  • OnShow
  • OnPaint
  • OnActivate
  • OnResize
  • OnPaint (снова)
14. UpCase для русского языка.

Данная функция (UpCase) производит преобразование только латинских символов в верхний регистр. Для правильного преобразования необходимо использовать функции Windows API, поскольку именно Windows должна "знать" о кодировке национальных символов. Причем к конфигурации BDE кодровка Windows не имеет никакого отношения - имея английские Windows без русификатора и выставив в BDE кодировку Paradox ANSII Cyrillic нормальных русских букв получить не удастся.

А функции для преобразования следующие - OemToAnsi, AnsiToOem, OemToAnsiBuf, AnsiToOemBuf в Win16 (модуль WinProcs) и OemToChar, CharToOem, OemToCharBuf и CharToOemBuf в Win32 (модуль Windows)..

15. Приложение, написанное на Delphi, не запускается минимизированным.

Проверьте глобальную переменную CmdShow для того чтобы определить, в каком состоянии запускается приложение, и модифицируйте ее как вам необходимо:
 
    procedure TForm1.FormCreate(Sender: TObject); 
    begin 
      if CmdShow = SW_SHOWMINNOACTIVE then WindowState := wsMinimized; 
    end; 
Например, если необходимо запускать приложение либо минимизированным, либо максимизированным, используйте следующий код: 
    procedure TForm1.FormCreate(Sender: TObject); 
    begin 
      if CmdShow = SW_SHOWMINNOACTIVE then WindowState := wsMinimized 
      else WindowState := wsMaximized; 
    end; 
 

     

16. Объясните разницу в помещении uses в секцию interface или implementation.

Секция interface - интерфейсная. Туда попадают объявления констант, типов (в т.ч. и объектов или классов) переменных, процедур и функций. Поэтому для этой части uses должен содержать ссылки на те модули, которые используются для объявлений в этой части.

Секция implementation - описание реализации интерфейсной части, здесь в uses должны быть упомянуты те модули, которыми вы пользуетесь для написания кода. Например, Вы хотите в модуле пользоваться функциями API Windows, для этого добавьте в объявлении implementation строку uses WinTypes, WinProcs; или uses Windows;. Таким образом, вы явно указываете что данными модулями будете пользоваться только в секции реализации.

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

17. Как спрятать окна MDI Child?

Q:Я пытаюсь это сделать, выставляя Form1.Visible := False, но это не помогает.

A:Windows не позволяет прятать окна MDI Child.

18. Как убрать заголовок у формы MDIChild?

Q:Как убрать заголовок (Caption) из MDIChild?

A:Для MDIChild установка свойства BorderStyle := bsNone не убирает заголовок. Это можно сделать так:
 
procedure TMDIChildForm.CreateParams(var Params: TCreateParams); 
begin 
  inherited CreateParams(Params); 
  Params.Style := Params.Style and (not WS_CAPTION); 
end; 
 

     
19. Сохранение данных в Clipboard.

Q:Мне нужно использовать clipboard для сохранения данных в собственном формате и я хочу для этого написать набор процедур ввода/вывода с использованием потоков (streams). Возможно ли создать объект TMemoryStream, эаполнить его и поместить в Clipboard?

A:Не только возможно, именно так поступают функции Clipboard.GetComponent и Clipboard.SetComponent. Сначала вы должны зарегистрировать свой собственный формат данных для Clipboard с помощью функции RegisterClipboardFormat:

    CF_MYFORMAT := RegisterClipboardFormat('My Format Description');
Далее вы должны выполнить шаги:
  1. Создать поток (memory stream) и записать туда данные.
  2. Создать глобальный буфер в памяти и скопировать поток туда.
  3. Вызвать Clipboard.SetAsHandle(), чтобы поместить буфер в Clipboard.

Пример:
 
var 
  hBuf: THandle; 
  Bufptr: Pointer; 
  MStream: TMemoryStream; 
begin 
  MStream := TMemoryStream.Create; 
  try 
  { write your data to the stream } 
    hBuf := GlobalAlloc(GMEM_MOVEABLE, MStream.Size); 
    try 
      BufPtr := GlobalLock(hBuf); 
      try 
        Move(MStream.Memory^, BufPtr^, MStream.Size); 
        Clipboard.SetAsHandle(CF_MYFORMAT, hBuf); 
      finally 
        GlobalUnlock(hBuf); 
      end; 
    except 
      GlobalFree(hBuf); 
      raise; 
    end; 
  finally 
    MStream.Free; 
  end; 
end; 

Внимание: не уничтожайте буфер, созданный с GlobalAlloc. Поскольку вы поместили его в Clipboard, это уже дело clipboard'а его уничтожить. Опять же, получая буфер из Clipboard, не уничтожайте этот буфер - просто сделайте копию содержимого.

Для обратного получения потока и данных, сделайте что-нибудь вроде этого:
 
var 
  hBuf: THandle; 
  BufPtr: Pointer; 
  MStream: TMemoryStream; 
begin 
  hBuf := Clipboard.GetAsHandle(CF_MYFORMAT); 
  if hBuf <> 0 then 
  begin 
    BufPtr := GlobalLock(hBuf); 
    if BufPtr <> nil then 
    try 
      MStream := TMemoryStream.Create; 
      try 
        MStream.WriteBuffer(BufPtr^, GlobalSize(hBuf)); 
        MStream.Position := 0; 
      { read your data from the stream } 
      finally 
        MStream.Free; 
      end; 
    finally 
      GlobalUnlock(hBuf); 
    end; 
  end; 
end; 

20. Что означает Key<>#0 ? 

Q:В исходном тексте одного из компонентов третьих фирм я увидел строку:

    if Key <> #0 then inherited KeyPress(#0);
В Windows виртуальные коды находятся в диапазоне 1-145 (Dec). Зачем нужна такая проверка?

A:В соответствии с соглашением Windows код клавиши #0 означает отсутствие реального нажатия. Управление в данную точку программы могло попасть, например вследствие прямого вызова, а не нажатия клавиши или же нажатие уже было обработано предком, вследствие чего код нажатой клавиши был сброшен в 0.

21. Аналог процедуры TP/BP Delay.
 
procedure TForm1.Delay(MSecs: Longint); 
var 
  FirstTick: Longint; 
begin 
  FirstTick := GetTickCount; 
  repeat 
    Application.ProcessMessages; 
  until GetTickCount - FirstTick >= MSecs; 
end; 

В Win32 API существуют также функции Sleep и SleepEx.

22. Каким образом создать форму, которую можно таскать за поле?

Q:Как сделать форму (окно), которое перетаскивается не за заголовок (Сaption), а за все поле ?

A:Нужно обрабатывать сообщение WM_NCHITTEST:
 
type 
  TForm1 = class(TForm) 
  ... 
  private 
    procedure WMNCHitTest(var M: TWMNCHitTest); message WM_NCHITTEST; 
  end; 

procedure TForm1.WMNCHitTest(var M: TWMNCHitTest); 
begin 
  inherited;                  { вызов унаследованного обработчика      } 
  if M.Result = htClient then { Мышь сидит на окне?                    } 
     M.Result := htCaption;   { Если да - то пусть Windows думает, что } 
                              { мышь на caption bar                    } 
end; 

Примечание: окно можно сделать вообще без Сaption.

23. Как программно спрятать или показать заголовок у формы?

Q:Как программно спрятать или показать заголовок (Caption) у формы?

A:Вы можете попробовать следующее:
 
procedure TForm1.HideTitlebar; 
var 
  Save: Longint; 
begin 
  if BorderStyle=bsNone then Exit; 
  Save := GetWindowLong(Handle, GWL_STYLE); 
  if (Save and WS_CAPTION) = WS_CAPTION then 
  begin 
    case BorderStyle of 
      bsSingle, bsSizeable: 
        SetWindowLong(Handle, GWL_STYLE, Save and (not WS_CAPTION) or WS_BORDER); 
     bsDialog: 
        SetWindowLong(Handle, GWL_STYLE, Save and (not WS_CAPTION) or DS_MODALFRAME or WS_DLGFRAME); 
    end; 
    Height := Height-GetSystemMetrics(SM_CYCAPTION); 
    Refresh; 
  end; 
end; 

procedure TForm1.ShowTitlebar; 
var 
  Save: Longint; 
begin 
  if BorderStyle = bsNone then Exit; 
  Save := GetWindowLong(Handle, GWL_STYLE); 
  if (Save and WS_CAPTION) <> WS_CAPTION then 
  begin 
    case BorderStyle of 
      bsSingle, bsSizeable: 
        SetWindowLong(Handle, GWL_STYLE, Save or WS_CAPTION or WS_BORDER); 
      bsDialog: 
        SetWindowLong(Handle, GWL_STYLE, Save or WS_CAPTION or DS_MODALFRAME or WS_DLGFRAME); 
    end; 
    Height := Height + GetSystemMetrics(SM_CYCAPTION); 
   Refresh; 
  end; 
end; 

24. Как сделать приложение модальным?

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

A:Ok, пара предложений на эту тему:

  1. Создайте форму, занимающую весь экран (maximized) без системных кнопок (Maximize, Minimize, System)
  2. В обработчике FormDeactivate для формы вызовите метод SetFocus - это предотвратит Ctrl+Esc:

  3.  

    Form1.SetFocus;

  4. В обработчике события FormActivate, нужно присвоить метод Deactivate для приложения:

  5.  

    Application.OnDeactivate := FormDeactivate;

  6. Создайте всплывающее меню TPopupMenu с единственным пунктом. В свойствах данного компонента нужно установить Visible=False. Создайте процедуру для этого пункта меню, и в теле поставьте две фигурные скобки {} (для того, чтобы Delphi не удалил эту процедуру)
  7. Присвойте созданное Popup-меню форме (св-во PopupMenu)
  8. Задайте горячую клавишу (shortcut) для Popup-меню в методе FormActivate как показано ниже:

  9.  

    NullItem1.ShortCut := ShortCut(VK_Tab, [ssAlt]);

    (NullItem1 нужно заменить на название созданного вами объекта - пункта меню)

Шаги 4-6 предотвращают переход на приложение по Alt-Tab.

25. Как изменить шрифт у Application.Title (заголовка приложения)?

Никак. Это ограничение Windows - вы не можете изменить шрифт ни у одного заголовка ни у приложения, ни у окна. Для окна можно предложить следующее - создать свое окно без заголовка (Caption) и рамки, которое будет само выводить нужную надпись нужным шрифтом и одновременно будет способно изменять свои размеры.

26. Каким образом (желательно не специфичным для Delphi) узнать, открыто меню или нет?

Вот так:
 
type 
  TForm1 = class(TForm) 
    MainMenu1: TMainMenu; 
    Item01: TMenuItem; 
    Item11: TMenuItem; 
    Item21: TMenuItem; 
  private 
    { Private declarations } 
  public 
    procedure WMMenuSelect(var M: TWMMenuSelect); message WM_MENUSELECT; 
  end; 

implementation 

{$R *.RES} 

procedure TForm1.WMMenuSelect(var M: TWMMenuSelect); 
begin 
  inherited; 
{ Этот Beep сигнализирует вообще об открытии меню } 
  MessageBeep(MB_ICONASTERISK); 
{ А зтот Beep - только о выборе в меню нового Item } 
  if M.Menu = MainMenu1.Handle then MessageBeep(MB_ICONASTERISK); 
end; 

end. 

 

     

Разное


1. Передача переменной в отчет ReportSmith.

Следующий код показывает, как передать переменную в отчет.

В примере строковой переменной отчета 'City' присваивается значение 'Bombey'. Подразумевается, что есть готовый отчет с данной переменной. Поместите компонент TReport на форму и установите требуемые свойства для вызова печати отчета. Напишите обработчик OnClick для кнопки Button1 на форме (кнопка - для простоты) :
 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  Report1.InitialValues.Clear; 
  Report1.InitialValues.Add('@City=<Bombey>'); 
  Report1.Run; 
end; 
 

     

2. Как получить русские буквы в DBD?

1.0Имя шрифта для отображения русских букв берется из файла PDOXWIN.INI секция [Properties] строка SystemFont. Если очень хочется, то можно исправить имя 'PDOXWIN.INI' на 'DBD.INI' в файле DBSRV.DLL (он лежит там же где и DBD.EXE) по смещению $E9D8 (не забудьте после 'DBD.INI' поставить шестнадцатеричный ноль), и в секции [Properties] файла DBD.INI добавить строку типа

    SystemFont = Courier New Cyr
По умолчанию имя фонта для отображения русских букв - Arial.

2.0Действительно, если у Вас Pan Euro или русская версия Windows95, то DBD не будет показывать шрифты Cyr в Preferences/General/Default system font.
Решить эту проблему можно двумя способами:

  1. записать в каталог WINDOWS/FONTS шрифты Arial Cyr от русских Windows и сделать ShutDown. После загрузки Arial Cyr будет доступен для выбора.
  2. поменять шрифт в Registry вручную например на MS Sans Serif - HKEY_CURRENT_USER/SOFTWARE/Borland/DBD/7.0/Preferences/Properties ключ SystemFont.
3. Как печатать отчеты из приложения Delphi без использования ReportSmith?
  1. Лучше всего использовать специализированные генераторы отчетов в виде компонентов, например QuickReport или Ace Reporter.
  2. Можно использовать печать формы, например: Form1.Print.
  3. Можно использовать свойство Canvas объекта Printer.

4. Как узнать количество точек на дюйм для принтера?
    VertPixelsPerInch := GetDeviceCaps(Printer.Handle, LogPixelsX);
    HorzPixelsPerInch := GetDeviceCaps(Printer.Handle, LogPixelsY);

5. Как определить, приложение запущено из под Delphi IDE или как отдельный файл?

Для этого следует проверить существование определенных окон:

1.0Delphi 1.0
 
function DelphiLoaded: Boolean; 

  function WindowExists(ClassName, WindowName: string): Boolean; 
  var 
    PClassName, PWindowName: PChar; 
    AClassName, AWindowName: array [0..63] of Char; 
  begin 
    if ClassName = '' then PClassName := nil 
    else PClassName := StrPCopy(@AClassName[0], ClassName); 
    if WindowName = '' then PWindowName := nil 
    else PWindowName := StrPCopy(@AWindowName[0], WindowName); 
    Result :=  FindWindow(PClassName, PWindowName) <> 0; 
  end; 

begin 
  Result := WindowExists('TPropertyInspector', 'Object Inspector') 
        and WindowExists('TMenuBuilder', 'Menu Designer') 
        and WindowExists('TApplication', 'Delphi') 
        and WindowExists('TAlignPalette', 'Align') 
        and WindowExists('TAppBuilder', ''); 
end; 

 

     
2.0Delphi 2.0
 
function DelphiLoaded: Boolean; 

  function WindowExists(ClassName, WindowName: string): Boolean; 
  begin 
    Result :=  FindWindow(PChar(ClassName), PChar(WindowName)) <> 0; 
  end; 

begin 
  Result := WindowExists('TPropertyInspector', 'Object Inspector') 
        and WindowExists('TMenuBuilder', 'Menu Designer') 
        and WindowExists('TApplication', 'Delphi') 
        and WindowExists('TAlignPalette', 'Align') 
        and WindowExists('TAppBuilder', ''); 
end; 

 
     
1.0Другой вариант для Delphi 1.0, работает только в EXE файлах (не в DLL).
 
function InIDE: Boolean; 
begin 
  Result := Bool(PrefixSeg) and Bool(PWordArray(MemL[DSeg:36])^[8])); 
;end
 
     

6. Что нужно предусмотреть при разработке приложения, которое будет работать при различном разрешении дисплея?

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

  1. Если вы не собираетесь делать форму масштабируемой, установите свойство Scaled=False и дальше не читайте.
  2. В противном случае Scaled=True.
    1. Установите AutoScroll=False. AutoScroll = True означает 'не менять размер окна формы при выполнении' что не очень хорошо выглядит, когда содержимое формы размер меняет.
    2. Установите шрифты в форме на самые распространенные TrueType шрифты, например Arial, Times New Roman, Courier. Если вдруг выбранного шрифта не окажется на пользовательском компьютере, то Windows выберет альтернативный шрифт из того же семейства. Этот шрифт может не совпадать по размерус исходным, что вызовет проблемы.
    3. Установите св-во Position в любое значение, отличное от poDesigned. poDesigned оставляет форму там, где она была во время дизайна, и, например, при разрешении 1280x1024 форма может оказаться в левом верхнем углу и совершенно за экраном при 640x480.
    4. Оставляйте по-крайней мере 4 точки между компонентами, чтобы при смене положения границы на одну позицию компоненты не "наезжали" друг на друга.
    5. Для однострочных меток TLabel с выравниванием alLeft или alRight установите AutoSize=True. Иначе AutoSize=False.
    6. Убедитесь, что достаточно пустого места у TLabel для изменения ширины фонта - 25% пустого места многовато, зато безопасно. При AutoSize=False Убедитесь, что ширина метки правильная, при AutoSize=True убедитесь, что есть свободное место для роста метки.
    7. Для многострочных меток (word-wrapped labels), оставьте хотя бы одну пустую строку снизу.
    8. Будьте осторожны при открытии проекта в среде Delphi при разных разрешениях. Свойство PixelsPerInch меняется при открытии формы. Лучше тестировать приложения при разных разрешениях, запуская готовый скомпилированный проект, а редактировать его при одном разрешении. Иначе это вызовет проблемы с размерами. Не изменяйте свойство PixelsPerInch самостоятельно!
    9. В общем, нет необходимости тестировать приложение для каждого разрешения в отдельности, но стоит проверить его на 640x480 с маленькими и большими шрифтами и на более высоком разрешении перед продажей.
    10. Уделите пристальное внимание принципиально однострочным компонентам типа TDBLookupCombo. Многострочные компоненты всегда показывают только целые строки, а TEdit покажет урезанную снизу строку. Каждый компонент лучше сделать на несколько точек больше.
Даже при выполнении перечисленных инструкций, у вас могут возникнуть проблемы при переходе, например от Large fonts к Small fonts в Windows 95 при одном и том же разрешении. Бороться с этим помогают специально для этого разработанные компоненты. Если же вы решите самостоятельно изменять размеры компонентов, лежащих на форме, то вам могут помочь методы TCanvas.TextWidth и TCanvas.TextHeight.

7. Конвертация ICO в BMP.

Q:Я создают toolbar, у меня есть иконки, но нет картинок в виде bitmap. Помогите!

A:Для преобразования файлов из одного формата в другой лучше всего иметь что-нибудь вроде HiJaak, который может преобразовывать форматы напрямую. Однако, будем считать, что у вас нет ничего, кроме Windows и Delphi. Следующая процедура может использоваться чтобы преобразовывать иконку в формат Windows Bitmap:

  1. Покажите на экране иконку. Не имеет значения, как вы это сделаете.
  2. Нажмите Alt-PrintScreen, чтобы скопировать текущее окно в буфер Clipboard.
  3. Загрузите Paintbrush и сделайте Edit/Paste.
  4. Выберите нужный кусок изображения и сделайте Edit/Copy. Перейдите к пункту Options/Image Attributes и установите размер области 32x32 точки.
  5. Снова сделайте Edit/Paste.
  6. Сохраните результат как BMP файл.
Лучше всего для редактирования и создания ресурсов (икон, картинок и т.п.) подходит Resource Workshop. Он включен в состав пакетов Borland Pascal 7.0 или Borland C++ 4.5, а также интегрирован в Borland C++ 5.0.

1.0В Delphi 1.0 есть специальный файл (X:\DELPHI\BIN\WORKOPT.DOS) который необходимо поместить в каталог, где находится Workshop - в этом случае последний будет "понимать" ресурсы, создаваемые Delphi 1.0 (например *.DCR).

8. Когда используется свойство Glyph, как узнать, какой цвет прозрачный?

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

9. Как отобразить bitmap в 256 цветах?

Q:Как подгрузить 256 цветный bitmap из ресурса и отобразить его в нормальной палитре?

A:Обычно это делается следующим образом. Код Вадима Пузанова (Красноярск).

    Image1.Bitmap.Handle := LoadBitmap(hInstance, 'BMP_NAME');
LoadBitmap загружает только картинку, без палитры. Если палитра у картинки отличается от системной, то ее надо устанавливать "вручную". Могут возникнуть проблемы, если на одной форме расположены две картинки с разными палитрами.
 
procedure XLoadBitmap(Instance: THandle; BitmapName: PChar; var HB: HBitmap; var HP: Palette); 
var 
  DC: HDC; 
  BI: PBitMapInfo; 
  Pal: PLogPalette; 
  I: Integer; 
  ResIdHandle: THandle; 
  ResDataHandle: THandle; 
  Bitmap: HBitmap; 
  C: HWnd; 
  OldPalette, Palette: HPalette; 

begin 
  Bitmap := 0; 
  Palette := 0; 
  HB := 0; 
  HP := 0; 
{ Получить ресурс из модуля } 
  ResIDHandle := FindResource(Instance, BitmapName, RT_BITMAP); 
  if ResIDHandle <> 0 then 
  begin 
    ResDataHandle := LoadResource(Instance, ResIDHandle); 
    if ResDataHandle <> 0 then  
    begin 
      BI := LockResource(ResDataHandle); 
      if BI <> nil then 
      begin 
      { 256-цветный битмап? } 
        if BI^.bmiHeader.biBitCount = 8 then 
        begin 
        { Создать палитру } 
          GetMem(Pal, SizeOf(TLogPalette) + 256*SizeOf(TPaletteEntry)); 
          for I := 0 to 255 do with Pal^.palPalEntry[I] do 
          begin 
            peRed  := BI^.bmiColors[I].rgbRed; 
            peGreen:= BI^.bmiColors[I].rgbGreen; 
            peBlue := BI^.bmiColors[I].rgbBlue; 
            peFlags:= 0; 
          end; 
          Pal^.palNumEntries := 256; 
          Pal^.palVersion := $300; 
          Palette := CreatePalette(Pal^); 
          FreeMem(Pal, SizeOf(TLogPalette) + 256 * SizeOf(TPaletteEntry)); 
        { Привести цвета палитры в системные } 
          DC := CreateDC('Display', nil, nil, nil); 
          OldPalette := SelectPalette(DC, Palette, False); 
          UnrealizeObject(Palette); 
          RealizePalette(DC); 
        { Создать битмап } 
          BitMap:= CreateDIBitmap(DC, BI^.bmiHeader, CBM_INIT, 
            @PByteArray(BI)^[SizeOf(TBitMapInfo) + SizeOf(TRGBQuad) * 256 - 4], BI^, DIB_RGB_COLORS); 
        { Освободить ресурсы } 
          UnlockResource(ResDataHandle); 
          FreeResource(ResDataHandle); 
          SelectPalette(DC, OldPalette, False); 
          DeleteDC(DC); 
        end else  
        begin 
        { Не 256-цветный битмап } 
          UnlockResource(ResDataHandle); 
          FreeResource(ResDataHandle); 
          BitMap := LoadBitmap(Instance, BitmapName); 
        end; 
        HB := Bitmap; 
        HP := Palette; 
      end; 
    end; 
  end; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
var 
  HB: HBitmap; 
  HP: HPalette; 
begin 
  XLoadBitmap(hInstance, 'PHOTO', HB, HP); 
  Image1.Picture.Bitmap.Handle  := HB; 
  Image1.Picture.Bitmap.Palette := HP; 
end; 

 
     

10. Если я хочу рассылать EXE файл, созданный в Delphi, какие еще файлы нужно посылать с ним?

Hикакие. Все компилируется в .EXE файл. Конечно, если вы разработали другие файлы (HLP, данные и т.д. ), или если вы используете VBX/OCX файлы, тогда вы должны распространять и их заодно. Если вы используете файлы VBX, то в поставку нужно также включать BIVBX11.DLL.

Если приложение использует функции BDE, вы также должны включать Borland DataBase Engine.

Полезные хитрости



1. Может ли редактор текстов в Delphi вырезать и вставлять прямоугольные фрагменты текста?

Конечно, может: Нажмите кроме Shift еще и Alt и режьте на здоровье. Alt можно сразу отпустить. Чтобы вернуться в старый режим, нужно выделить что-либо мышкой.

2. Редактирование файлов SQL в Delphi IDE.

2.0Если вы в Delphi 2.0 IDE редактируете файл с расширением SQL, то, хотя это нигде не документировано, происходит автоматический Syntax Highlighting. Наибольший недостаток - не отслеживается конец комментария '*/'.

3.0В Delphi 3.0 комментарии отрабатываются нормально.

3. Встроенный отладчик/дизассемблер.

2.0Если вы создадите в ключе

    HKEY_CURRENT_USER\Software\Borland\Delphi\2.0\Debugging
строковое значение EnableCPU = "1", то после перезапуска среды у вас появится пункт меню View|CPU, которые вызывает появление простейшего отладчика/дизассемблера.

3.0Для Delphi 3.0 справедливо тоже самое ( ...\Delphi\3.0\Debugging, естественно), причем отладчик там по возможностям сравним с Turbo Debugger.

[Назад][Содержание][Вперед]

Delphi32 Delphi32 - Всё о Дельфи. WWW.delphi32.narod.ru
Сайт управляется системой uCoz