InfoCity
InfoCity - виртуальный город компьютерной документации
Реклама на сайте







Размещение сквозной ссылки

 

Часто задаваемые общие вопросы по 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 (