ShowMessageFmt — Процедура Delphi

Содержание

ShowMessageFmt — Процедура Delphi

The Formatting parameter defines how the Data array is manipulated into the displayed string.

The dialog has an OK button to allow the user to register the message and close the dialog.

The Formatting string can comprise a mix of ordinary characters (that are passed unchanged to the result string), and data formatting characters. This formatting is best explained by the example code.

In simple terms, each data formatting substring starts with a % and ends with a data type indicator :

d = Decimal (integer)
e = Scientific
f = Fixed
g = General
m = Money
n = Number (floating)
p = Pointer
s = String
u = Unsigned decimal
x = Hexadecimal

The general format of each formatting substring is as follows:

ShowMessageFmt — Процедура Delphi

Displays a message box with a formatted message.

Call ShowMessageFmt to display a simple message box with an OK button. The name of the application’s executable file appears as the caption of the message box.

The Msg parameter is a format string for the message that appears in the message box. The Params parameter provides the parameters that are assembled into the message. The Params_Size parameter gives the index of the element of the Params array (one less than the number of parameters).

For more information about how messages are formatted, see Format Strings.

To display a message in a message box with other buttons, or with an icon, use the MessageDlg function.

Диалоговые окна для вывода сообщений в Delphi

(ShowMessage, MessageDlg и MessageDlgPos)

Для оповещения пользователя о некорректно введенных данных можно использовать процедуру ShowMessage или функции MessageDlgPos и MessageDlg.

ShowMessage(const Msg: String) — эта процедура выводит окно с сообщением и кнопкой Ok. В заголовке содержится название исполняемого файла, если в опциях приложения не задан параметр Title, если задан то выводиться будет он. Строка Msg — будет выводиться как текст сообщения.

MessageDlg(const Msg: String; AType: TMsgDlgType; Abuttons: TMsgButtons; HelpCtx: Longint): Word — функция показывающая диалоговое окно сообщения в центре экрана и дает возможность пользователю ответить на сообщение. Msg — параметр отвечающий за выводимый текст сообщения.

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

  • mtError — на фоне красного круга расположен белый косой крест и заголовок окна — Error;
  • mtWarning — на фоне желтого треугольника расположен черный восклицательный знак -«!» и заголовок окна — Warning;
  • mtConfirmation на фоне белого круга расположен синий знак «?» и заголовок окна — Confimation;
  • mtInformation — на фоне белого круга расположена синия буква «i» и заголовок окна — Information;
  • mtCustom — диалоговое окно не содержит рисунка, в заголовке выводиться имя исполняемого файла приложения или Title свойства Application приложения.

AButtons — параметр, который задает набор кнопок на диалоговой форме и может принимать произвольные комбинации из значений:

  • кнопка Yes — mbYes,
  • кнопка Ok — mbOk,
  • кнопка No — mbNo,
  • кнопка Cancel — mbCancel,
  • кнопка Abort — mbAbort,
  • кнопка Retry — mbRetry,
  • кнопка Ignore — mbIgnore,
  • кнопка All — mbAll,
  • кнопка Help — mbHelp.

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

MessageDlg(‘Большое значение’, mtConfirmation, [mbYes,mbNo],0 );

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

  • кнопки OK и Cancel — mbOkCancel,
  • кнопки Yes, No и Cancel — mbYesNoCancel,
  • кнопки Abort, Retry и Ignore — mbAbortRetryIgnore.

Эти константы не надо брать в скобки, так как они являются предопределенными множествами.

Если мы внутри скобок не объявим список, то в диалоговом окне не будет ни одной кнопки и пользователю придется закрывать окно системными кнопками Windows.

MessageDlg(‘Большое значение’, mtConfirmation,mbAbortRetryIgnore,0 );

При щелчке на любой кнопке (кроме Help) результат возвращается функцией MessageDlg (свойство ModalResult), а сама форма закрывается. Результат может быть одним из значений списка:

mrOk, mrRetry, mrNo,mrNone, mrAbort, mrYes, mrCancel, mrIgnore, mrAll

Параметр HelpCtx задает тему справки. Ее можно вызвать во время показа диалогового окна клавишей F1. Обычно этот параметр устанавливается равным нулю и справка не выводиться.

Не нашли то, что искали? Воспользуйтесь поиском:

Лучшие изречения: Да какие ж вы математики, если запаролиться нормально не можете. 8426 — | 7329 — или читать все.

188.64.174.135 © studopedia.ru Не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования. Есть нарушение авторского права? Напишите нам | Обратная связь.

Отключите adBlock!
и обновите страницу (F5)

очень нужно

Некоторые встроенные функции и процедуры Delphi.

1) для строковых типов:

Типы строк Максимальная длина Используется для Нулевой символ в конце
Short String Обратной совместимости Нет
Ansi String 2^31 (2 Гб) Символ ANSI Есть
String Или 255, или 2 Гб Символ ANSI или Unicode Есть или нет
Wide String 2^30 (1 Гб) Символ Unicode, в серверах COM и интерфейсах Есть

Родовым является тип String, который имеет разный смысл в зависимости от директивы компилятора. Если включена директива <$H+>(она включена по умолчанию), то String интерпретируется компилятором как тип Ansi String – длинная строка с нулевым символом в конце. Если же включена директива <$H->, то String интерпретируется компилятором как тип Short String – короткая строка без нулевого символа в конце. Если в объявлении типа после ключевого слова String следует число символов в квадратных скобках (например, String[4]), то независимо от директив компилятора, тип трактуется как строка без нулевого символа в конце с указанным числом символов. Стандартная функция LENGTH возвращает число символов в строке, переданной ей в качестве параметра. Процедура SetLength устанавливает длину строки.

Процедуры и функции обработки строк:

FloadToStr (Value: Extended): string Преобразует Value в строку с точностью 15 цифр.
IntToStr (Value: integer): string Возвращает строку, содержащую преобразованное целое значение Value.
StrToFload (const S: string): Extended Преобразует строку S в действительное число.
StrToInt (const S: string): Integer Преобразует строку S в целое число.
Val (S; var V; var Code: Integer) Преобразует строку S в целое число Code.

2) Функции массива:

Функция Описание
Length Число элементов массива
High Наибольшее значение индекса
Low Наименьшее значение индекса

3) Функции для числовых массивов:

Функция Тип аргумента Тип результата Описание
MinIntValue Array of integer Integer Возвращает минимальное значение элемента массива целых чисел
MaxInValue Array of integer Integer Возвращает максимальное значение элемента массива целых чисел
MinValue Array of double Double Возвращает минимальное значение элемента числового массива
MaxValue Array of double Double Возвращает максимальное значение элемента числового массива
Sum Array of double Extended Возвращает сумму элементов массива

Эти функции определены в модуле math и этот модуль должен подключаться оператором USES, чтобы компилятор их понимал.

4) Процедуры и функции вызова диалоговых окон:

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

А) простейшей из таких процедур является ShowMessage, отображающая окно сообщения с кнопкой ОК. Она имеет вид:

Procedure ShowMessage (const Msg: string);

Текст сообщения задается параметром Msg. Заголовок окна совпадает с именем выполняемого файла приложения.

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

Procedure ShowMessageFmt (const Msg: string; Params array of const);

Параметр Msg в этой процедуре задает строку описания формата, а параметр Params задает массив параметров, форматируемых строкой Msg.

Приведем примеры использования этих процедур:

ShowMessage (» Работа приложения успешно завершена’);

ShowMessageFmt (» Задано% d параметров из % d ‘, [N1, N2]);

Последний оператор при N1=5, N2=7 выдаст сообщение с текстом: «Задано 5 параметров из 7».

В) Следующая функция отображает окно, в котором задается вопрос и анализируется полученный ответ – это функция MessageDlg. Она объявляется следующим образом:

Function MessageDlg (const Msg: string; AType: TMsgDlgType; AButtons: TMsgDlgButtons; HelpCtx: Longint): Word;

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

Вид окна задается параметром AType. Возможные значения параметра:

Значение Описание
mtConfirmation Окно подтверждения, содержащее зеленый вопросительный знак.
mtInformation Информационное окно.
mtError Окно ошибок, содержащее красный стоп-сигнал.
mtWarning Окно замечаний.
mtCustom Заказное окно без рисунка. Заголовок соответствует имени выполняемого файла.

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

Значения Описания
mbYes Кнопка с надписью ДА
mbNo Кнопка с надписью НЕТ
mbOK Кнопка с надписью ОК

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

If MessageDlg (» Действительно хотите закончить приложение? ‘, mtConfirmation, [mbYes, mbNo], 0) = mrYes then begin

MessageDlg (» Работа приложения закончена ‘, mtInformation, [mbOk], 0);

Первый вызов функции MessageDlg приводит к отображению окна типа mtConfirmationс вопросом о завершении приложения. Если пользователь нажимает кнопку Yes, то выводится второе окно типа mtInformationс сообщением о завершении работы приложения.

Заключение

В первую очередь Delphi предназначен для профессионалов-разработчиков корпоративных информационных систем. Может быть, здесь следует пояснить, что конкретно имеется в виду. Не секрет, что некоторые удачные продукты, предназначенные для скоростной разработки приложений (RAD – rapid application development) прекрасно работают при изготовлении достаточно простых приложений, однако, разработчик сталкивается с непредвиденными сложностями, когда пытается сделать что-то действительно сложное. Бывает, что в продукте вскрываются присущие ему ограничения только по прошествии некоторого времени.

Delphi такие ограничения не присущи. Хорошее доказательство тому – это тот факт, что сам Delphi разработан на Delphi. Можете делать выводы. Однако Delphi предназначен не только для программистов-профессионалов. Я читал в электронной конференции совершенно неожиданные для меня письма, где учителя, врачи, преподаватели ВУЗов, бизнесмены, все те, кто используют компьютер с чисто прикладной целью, рассказывали о том, что приобрели Delphi for Windows для того, чтобы быстро решить какие-то свои задачи, не привлекая для этого программистов со стороны. В большинстве случаев им это удается. Поразительный факт – журнал Visual Basic Magazine присудил свою премию Delphi for Windows.

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

Список литературы

1. Архангельский А.Я. Программирование в Delphi. Учебник по классическим версиям Delphi. – М.: ООО «Бином-Пресс», 2006. – 1152 с.: ил.

2. Архангельский А.Я. Программирование в Delphi. – М.: ООО «Бином-Пресс», 2004. – 1152 с.: ил.

3. Гофман В.Э., Хомоненко А.Д. Delphi. Быстрый старт. – СПб: БХВ-Петербург, 2003. – 288 с.: ил.

Диалоговые окна в Дельфи (процедуры и функции, реализующие диалоговые окна)

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

Процедура ShowMessage, функции MessageDlg и MessageDlgPos отображают окно (панель) вывода сообщений, а функции InputBox и InputQuery — окно (панель) для ввода информации.

Процедура ShowMessage

Процедура ShowMessage (const Msg: String) отображает окно сообщения с кнопко ОК. Заголовок содержит название исполняемого файла приложения, а строка Msg выводится как текст сообщения.

Функция MessageDlg

Функция MessageDlg(const Msg: String; AType: TMsgDlgType; AButtons: TMsgDlgButtons; Helpctx: Longint) : word отображает окно сообщения в центре экрана и позволяет получить ответ пользователя. Параметр Msg содержит отображаемое сообщение.

Окно сообщения может относиться к различным типам и наряду с сообщением содержать картинки. Тип окна сообщения определяется параметром АТуре, который может принимать следующие значения:

  • mtWarning (окно содержит черный восклицательный знак в желтом треугольнике и заголовок Warning);
  • mtError (окно содержит белый косой крест в красном круге и заголовок Error);
  • mtInformation (окно содержит синюю букву «i» в белом круге и заголовок Information);
  • mtConfirmation (окно содержит синий знак «?» в белом круге и заголовок Confirmation);
  • mtCustom (окно не содержит картинки, в заголовке выводится название исполняемого файла приложения).

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

  • mbYes (кнопка Yes);
  • mbAbort (кнопка Abort);
  • mbNo (кнопка No);
  • mbRetry (кнопка Retry);
  • mbOk (кнопка OK);
  • mbIgnore (кнопка Ignore);
  • mbCancel (кнопка Cancel);
  • mbAll (кнопка All);
  • mbHelp (кнопка Help);
Илон Маск рекомендует:  Селекторы CSS

Для значения параметра AButtons имеются две константы — mbYesNoCancel и mbOKCancel, задающие предопределенные наборы кнопок:

  • mbYesNoCancel = [mbYes, mbNo, mbCancel];
  • mbOKCancel = [mbOK, mbCancel]

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

Параметр HelpCtx определяет контекст (тему) справки, которая появляется во время отображения диалогового окна при нажатии пользователем клавиши . Обычно! значение этого параметра равно нулю.

Пример использования функции MessageDlg:

При нажатии кнопки Button1 производится простейшая проверка даты. Код даты вводится в поле редактирования DateEdit1, размещенное в форме. Если длина даты меньше допустимой, выдается предупреждение с запросом на автоматическую коррекцию (см. скриншот). При утвердительном ответе пользователя в поле даты записывается текущая дата, при отрицательном — фокус передается полю ввода даты.

Функция MessageDlgPos

Функция MessageDlgPos(const Msg: String; AType: TMsgDlgType; AButtons: TMsgDlgButtons; HelpCtx: Longint; X, Y: Integer) : Word отличается от функции MessageDlg наличием параметров Х и Y, управляющих положением окна на экране.

Функция InputBox

Функция InputBox(const ACaption, APrompt, ADefault: String): String отображает диалоговое окно для ввода строки текста. Окно выводится в центре экрана и содержит поле ввода с надписью, а также кнопки ОК и Cancel.

Параметр ACaption задает заголовок окна, а параметр APrompt содержит поясняющий текст к полю ввода. Параметр ADefault определяет строку, возвращаемую функцией при отказе пользователя от ввода информации (нажатие кнопки Cancel или клавиши ).

Пример использования функции InputBox:

Приведенная процедура отображает окно запроса на ввод фамилии пользователя (см. скриншот).

По умолчанию предлагается Иванов.

Функция InputQuery

Функция InputQuery (const ACaption, APrompt: String; var Value: String): Boolean отличается от функции InputBox тем, что вместо третьего параметра— строки по умолчанию— используется параметр Value, который в случае подтверждения ввода содержит введенную пользователем строку.

В качестве результата функция возвращает логическое значение, позволяющее определить, каким образом завершен диалог. Если нажата кнопка ОК, то функция возвращает значение True, если нажата кнопка Cancel или клавиша — значение False.

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

Кроме рассмотренных диалоговых окон, в Delphi имеется ряд других специализированных диалоговых окон, например диалоговое окно выбора каталога, вызываемое функцией Select Directory модуля FileCtrl.

Как использовать функцию ShowMessageFmt

Delphi , Программа и Интерфейс , Диалоги и Фреймы

Как использовать функцию ShowMessageFmt

%s — вместо нее подставляется первый параметр из [], приведенный к типу String
%.8x — подставляется целое, переведенное в 16-ричную систему, дополненное слева нулями до 8-ми цифр
%p — подставляется указатель

Статья Как использовать функцию ShowMessageFmt раздела Программа и Интерфейс Диалоги и Фреймы может быть полезна для разработчиков на Delphi и FreePascal.

Комментарии и вопросы

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

Delphi. Messages. Первые наивные пробы

Решил разобраться с устройством Windows, в частности с сообщениями. Народ в сети массово отправляет к Рихтеру и Русиновичу. Книги приобрел – начал читать. Что хочу сказать – первое впечатление – информация ценнейшая для разработки под Windows. Ещё и на русском языке. Но поскольку практика это лучший инструмент познания, решил сделать несколько простых примеров до основательного чтения этих книг. После чтения сделаю ещё несколько примеров, чтобы сравнить насколько лучше понимаю предмет.

Как отправить сообщение из приложения?

Рассмотрим на примере закрытия окна. Сообщение будет WM_CLOSE. Список всех сообщений для Windows можно посмотреть здесь.

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

Оба сообщения закроют окно c Handle равным Form2.Handle Первый параметр – Handle окна, второй параметр – собственно сообщения, а третий и четвертый – дополнительные параметры, которые задействуются или нет от случая к случаю, например координаты курсора. В приведенном примере оба параметра занулены. Если поместить эти инструкции, скажем в обработчик кнопки окна, то после нажатия в ядро Windows будет направлено сообщение о закрытии окна, а Windows, соответственно просто закроет окно.

Где найти полный список сообщений?

В принципе таких ресурсов много. Вот один из них.

Слушает ли Delphi программа сообщения?

Определенно да. Вот простой пример. Поймаем сообщение нажатия правой кнопкой мыши на любом из компонентов приложения.

Если нажмем правой клавишей мыши на любом компоненте приложения, то увидим

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

Как отправить “кастомное” сообщение?

Под кастомным сообщением я подразумеваю сообщение не из списка, а какое-то свое сообщение, например WM_USER+2001. Вот как можно отправить такое кастомное сообщение. Пример основан на официальной документации

Объявляем глобальную константу

Далее создаем и присваиваем обработчик

И последний штрих – отправляем сообщения

Ещё вариант отправки кастомного сообщения

Определяем свою константу (она должна быть прописана выше, чем метод, который её использует, то есть, например, до описания типов)

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

Далее прописываем его

Далее отправляем PostMessage, скажем, по нажатию кнопки

Блог GunSmoker-а

. when altering one’s mind becomes as easy as programming a computer, what does it mean to be human.

1 декабря 2015 г.

Дело о неработающем ShowMessage

Новый «раздел» статей в блоге: показываем как можно применять возможности отладчика на практике.

На форуме человек задал вопрос: «почему не работает ShowMessage». Код в вопросе был такой:
При загрузке этой библиотеки через LoadLibrary мы должны увидеть сообщение:

Но этого не происходит, ShowMessage просто тихо ничего не делает.

(На самом деле, автор вопроса спрашивал «почему не выполняется код между begin/end». Он также сообщил некоторые интересные детали. В частности, загрузка DLL из другого приложения показывает сообщение. Также, если показ сообщения вставить в любую экспортируемую функцию, то оно будет показано в обоих случаях)

Итак, как же нам решить это загадочное дело?

Для начала нужно создать т.н. тестовый проект. В нашем случае мы создадим новую DLL, впишем в неё код выше. Также мы создадим новое приложение VCL Forms, добавим одну кнопку и напишем такой обработчик:
Запустим приложение вне отладчика через Ctrl + Shift + F9 (Run / Run Without Debugging) и нажмём на кнопку. Нам нужно убедится, что, действительно, ничего не происходит. Т.е. нам нужно воспроизвести поведение.

Если мы не видим проблемного поведения (т.е. сообщение будет всё же показано), значит, мы не смогли воспроизвести проблему. Возможно, поведение программы меняется под отладчиком? Пробуем запустить под отладчиком. Возможно, дело в версии Delphi? Пробуем другую версию Delphi. Возможно, дело в версии Windows? Пробуем другую версию ОС. И так далее. Мы подбираем условия окружения при которых мы надёжно воспроизводим ошибку.

(Забегая вперёд, скажу, что для воспроизведения проблемы нужна Delphi 2007 и выше, Windows Vista и выше)

Как только мы воспроизвели «плохое» поведение под отладчиком — нужно настроить проекты для отладки. Для этого откроем Project / Options каждого проекта (exe и DLL) и включим (если они не включены) следующие опции:

  • Compiling \ Stack Frames
  • Compiling \ Range Checking
  • Compiling \ все опции в категории Debugging
  • Compiling \ Use Debug DCUs
  • Linking \ Debug Information (старое название: TD32 Debug Info)
  • Linking \ Include remote debug symbols
  • Linking \ Map file = Detailed

и выключим опцию Compiling \ Optimization.

Конечно, не все эти опции нужно включать для нашего примера, но тут, что называется, «лишним не будет». Главные опции для нашего случая это Compiling \ Use Debug DCUs — т.к. мы собираемся отлаживать код RTL/VCL ( ShowMessage ) и Linking \ Debug Information — т.к. мы будем отлаживать DLL и EXE.

Кроме того, на вкладке Delphi Compiler мы сбросим Output Directory и Unit Output Directory в » .\ » (без кавычек) — что приведёт к выводу всех файлов в ту же папку где лежат исходники (вместо обычной подпапки \Win32\Debug ).

Сделаем Project / Build каждому проекту (напомню, что простого Project / Compile недостаточно если вы меняете опции проекта, но не его исходный код).

Теперь, откроем проект DLL и установим точку останова:

Теперь используем Run / Parameters и укажем для какого исполняемого файла нужно запускать DLL (это делать не нужно, если вместо проекта DLL вы открываете проект EXE):

Запускаем проект. Вы увидите, что точка останова становится недействительной:

Это — нормально. Ведь DLL не загружена в процесс EXE. Если бы это была точка останова на код в EXE — тогда, да, это было бы не нормально, что-то пошло не так.

Когда вы нажмёте на кнопку в приложении, DLL будет загружена вызовом LoadLibrary , после чего точка останова снова станет действительной. Затем LoadLibrary вызовет DllMain нашей библиотеки с «причиной» = DLL_PROCESS_ATTACH . Для события DLL_PROCESS_ATTACH RTL Delphi автоматически вызывает секции initialization всех модулей из DLL, а также секцию begin/end .dpr файла (и, наоборот, для DLL_PROCESS_DETACH вызываются секции finalization всех модулей). В результате мы встаём на нашей точке останова:

В окне стека вызовов (View / Debug Windows / Call Stack) вы можете видеть, что нас вызвала LoadLibrary , которую, в свою очередь, вызвали мы из Button1Click . К сожалению, отладчик IDE не может полностью реконструировать стек, пропуская некоторые вызовы. Он также не знает где взять отладочную информацию для системных DLL.

В любом случае, мы нажимаем F7 (Run / Step Into), чтобы войти в ShowMessage :

Если вы не видите эту картину, а вместо этого отладчик просто перешёл на следующую после ShowMessage строку (с end ) — т.е. F7 (Step Into) сработала как F8 (Step Over) — то это означает, что у отладчика нет отладочной информации о модулях RTL/VCL. Это происходит потому, что опция Use Debug DCUs не была включена. А если она была включена, значит, она не возымела действия. Последнее может происходить в двух случаях:

  1. Вы не сделали проекту DLL Project / Build (а, например, просто запустили его);
  2. Ваш проект использует пакеты времени выполнения (run-time packages, BPL).

Окей, как только вы разобрались с проблемами, продолжаем нажимать F7.

Вот первая интересная функция. Мы видим, что при выполнении некоторых условий: Vista+ ( TOSVersion.Check(6) ), UseLatestCommonDialogs , ComCtl32.dll V6+ (IsNewCommonCtrl) и использовании стиля по умолчанию (StyleServices.IsSystemStyle), вызывается DoTaskMessageDlgPosHelp , в противном случае — DoMessageDlgPosHelp . Быстро глянем в DoTaskMessageDlgPosHelp :
и DoMessageDlgPosHelp : Отсюда видно, что в зависимости от набора условий ShowMessage реализуется либо через вызовы TaskDialog API, либо через обычную VCL-форму (создаётся в CreateMessageDialog ). Что-то похожее мы уже делали.

Итак, нажмём один раз F8 (Run / Step Over), чтобы выполнить строчку с if и посмотреть, куда мы встанем:

Окей, т.е. в нашем случае ShowMessage будет реализован через Task Dialog API. Заходим внутрь по F7 (Step Into), затем ещё раз (входим в DoTaskMessageDlgPosHelp ). Функция DoTaskMessageDlgPosHelp настраивает диалог, а затем его вызывает. Нам интересно, что происходит в момент вызова диалога, поэтому весь код настройки мы проходим по F8 (Step Over) — вплоть до вызова if LTaskDialog.Execute then . Поскольку в теле DoTaskMessageDlgPosHelp есть цикл (да и в целом это не самая короткая функция) — можно пролистать код вниз и установить новую точку останова на строчку с LTaskDialog.Execute , после чего запустить программу через F9 (Run / Run). Отладчик выполнит код настройки и встанет на точке останова:

Заходим в LTaskDialog.Execute по F7 (Step Into):

Метод Execute — динамический ( dynamic ), поэтому он вызывается не напрямую. Этот код ищет адрес метода в таблице DMT и сохраняет его в регистр ESI , после чего делает на него переход ( JMP ). Мы могли бы (как и выше с LTaskDialog.Execute ) установить точку останова на строчку JMP ESI , но вызов динамического метода — частая операция. Мы бы не хотели вставать на этой точке останова каждый раз, когда мы проходим по F8 (Step Over) вызовы других динамических методов. Поэтому мы установим курсор на строчку JMP ESI и нажмём F4 (Run / Run to Cursor), после чего нажмём F7 (Step Into) и, наконец, попадём внутрь метода Execute :

Проходим метод по F8 (Step Over) или используем F4 (Run to Cursor) и заходим по F7 (Step Into) в Result := Execute(LParentWnd) . Как и ранее, Execute — метод динамический, поэтому используем F4 (Run to Cursor) на JMP ESI и F7 (Step Into) для входа в унаследованную реализацию:

Несколько раз повторим эти операции, путешествуя по реализациям Execute , пока не окажемся в самой нижней TCustomTaskDialog.DoExecute :

Как и выше с DoTaskMessageDlgPosHelp , TCustomTaskDialog.DoExecute сначала производит настройку, а затем вызывает интересующий нас кусок:

Здесь нас интересует вызов TaskDialogIndirect , поэтому ставим курсор на строчку Result := TaskDialogIndirect(. ) = S_OK; и жмём F4 (Run to Cursor). Далее нажимаем F7 (Step Into).

Мы ожидали, что TaskDialogIndirect — функция Windows, для неё у нас нет исходного кода (даже с включенной опцией Use Debug DCUs), поэтому F7 (Step Into) сработает как F8 (Step Over). Но, как мы видим, в Delphi TaskDialogIndirect — это переходник-обманка, которая динамически («по запросу») загружает «настоящую» TaskDialogIndirect (и сохраняет её в глобальной переменной _TaskDialogIndirect ). Это (скрытие реализации под «известным» именем) — подводный камень при отладке, т.к. мы можем не предположить, что за вызовом TaskDialogIndirect скрывается какой-то «наш» код и пропустить его пройдя вызов TaskDialogIndirect по F8 (Step Over).

Если вы попались на эту удочку и выполнили TaskDialogIndirect по F8 (Step Over), то вы увидели, что Result стал равен False , а сообщения на экране не появилось. Т.е. TaskDialogIndirect вернула какой-то код ошибки, который код RTL/VCL успешно проигнорировал. Вы хотите узнать этот код. Для этого вы устанавливаете точку останова на строчку Result := TaskDialogIndirect(. ) = S_OK — это интересующий нас участок кода. Ничего больше нас уже не интересует, поэтому все прочие точки останова (View / Debug Windows / Breakpoints) можно удалить.

(Подсказка: сначала удалите все старые точки останова, а лишь затем устанавливайте новую точку останова на строчку Result := TaskDialogIndirect(. ) = S_OK , а не наоборот.)

Илон Маск рекомендует:  Что такое код asp serverip

Снимите приложение по F2 (Run / Program Reset) и запустите снова. Щёлкните по кнопке — и вы должны встать сразу на строчке Result := TaskDialogIndirect(. ) = S_OK , минуя все предыдущие шаги:

Вызовите CPU отладчик через Ctrl + Alt + C (View / Debug Windows / CPU Windows / Entire CPU):

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

  1. Подготовка аргументов
  2. Непосредственный вызов (передача управления)
  3. Чтение/сохранение/анализ результата

Обратите внимание, что п1 может включать в себя и другие вызовы функций (например, код DoExecute(GetParentWindow) сначала вызовет GetParentWindow , а лишь затем — DoExecute ). Нас же интересует только п2. Несложно сообразить, что п2 будет последним вызовом функции среди всего кода, сгенерированного для этой строки.

Вызов другой функции на ассемблере — это инструкция call , поэтому нас интересует последняя инструкция call в строчках машинного кода между двумя жирными строками ( Vcl.Dialogs.pas.5703: Result := . и Vcl.Dialogs.pas.5705: FModalResult := . ). В данном случае это 04BF2AE0 E88751E7FF call $04a67c6c .

Вы можете нажимать F8 (Step Over), чтобы пройтись по строкам машинного кода вплоть до этой строки, или же вы можете установить курсор на эту строчку и нажать F4 (Run to Cursor). В любом случае вы встанете на этой строке:

Нажмите F8 (Step Over) ещё раз, чтобы выполнить эту функцию.

Результат функции будет помещён в регистр EAX — любая функция всегда возвращает результат через EAX . Но даже, если вы это не знаете, про это можно догадаться, т.к. п3 из списка выше («чтение/сохранение/анализ результата») первым делом проверяет регистр EAX ( test eax,eax ).

Поскольку TaskDialogIndirect возвращает HRESULT , то в EAX будет лежать искомый код ошибки (в виде HRESULT ).

В любом случае, возвращаясь к коду TaskDialogIndirect — здесь нас интересует вызов _TaskDialogIndirect , но мы не знаем по какой ветке пойдёт выполнение, поэтому мы нажимаем F8 (Step Over), пока это не станет ясно (как оказывается, мы идём по ветке else ). Дойдя до Result := _TaskDialogIndirect мы (на всякий случай) нажимаем F7 (Step Into):

В этот раз F7 (Step Into) сработала как F8 (Step Over) — т.к. мы вызвали функцию Windows (для которой у нас нет исходного кода). В данном случае мы можем увидеть, что результат вызова (значение Result типа HRESULT ) равно -2147024809. Для этого вы можете просто навести мышью на слово Result в редакторе кода — и IDE покажет значение Result во всплывающей подсказке. Или вы можете использовать окно локальных переменных (View / Debug Windows / Local Variables). Или вы можете щёлкнуть правой кнопкой по Result и выбрать Debug / Evaluate/Modify из всплывающего меню.

Иногда отладчик может показывать сообщение о том, что значение переменной/выражения не доступно. Обычно это происходит при включенной оптимизации, когда код сохраняет/использует значение ранее и не хранит его вплоть до выхода из функции. Это проще всего лечить отключением оптимизации (что мы и сделали в самом начале при настройке проекта). Альтернативно, вы можете вычислять/проверять интересующие вас значения выше по тексту (пока они ещё не отбрасываются), либо позднее — уже после выхода из функции. В крайнем случае вы всегда можете открыть CPU-отладчик и проверить значения внутри строк — как мы сделали это выше в лирическом отступлении.

Итак, мы вроде как нашли причину, почему ShowMessage ничего не показывает. Потому что она вызывает TaskDialogIndirect , а она, в свою очередь, завершается с ошибкой номер -2147024809. Но что это за ошибка номер -2147024809?

Для этого мы запустим утилиту Error Lookup из состава EurekaLog. Если вы используете EurekaLog, то эта утилита у вас уже есть — её можно запустить через меню Пуск (Пуск / Программы / EurekaLog / Tools / Error Lookup) или через меню IDE Delphi (Tools / EurekaLog / Error Lookup). Если EurekaLog у вас нет, то Error Lookup можно установить бесплатно автономно — скачав её с сайта — вас интересует «Freeware EurekaLog Tools Pack».

Итак, скормим -2147024809 в Error Lookup:

Как вы можете видеть, -2147024809 — это ошибка HRESULT с кодом $80070057 = E_INVALIDARG (причём это спроецированная на HRESULT Win32 ошибка с кодом 87 = ERROR_INVALID_PARAMETER — что можно проверить запустив поиск ошибки 87). Итак, TaskDialogIndirect ругается на неверные аргументы. Уже в этот момент мы должны понять, что что-то идёт сильно не так. Предположительно отлаженный код RTL/VCL должен вызывать предположительно отлаженный код Windows, так что ошибки вида «неверный аргумент» возникать в принципе не должно, если только в функцию не просочатся «неверные» данные от нас лично.

Но какой именно аргумент не нравится TaskDialogIndirect ? Жаль, что системные функции Windows не используют исключения — с кодами ошибок у нас нет указания на аргумент, который не понравился функции. У нас есть два вектора атаки:

  1. Окно-родитель (ParentWnd) устанавливается динамически самим RTL/VCL и не приходит от нашего кода. Возможно, TaskDialogIndirect не понравилось окно?
  2. Известно, что вызов ShowMessage в экспортируемой функции работает успешно. Мы можем сравнить, чем отличаются аргументы между успешным и не успешным вызовами.

Чтобы проверить первую гипотезу, мы установим точку останова на (вторую) строчку Result := _TaskDialogIndirect(. ) в TaskDialogIndirect и удалим все прочие точки останова (как и ранее, это удобнее делать наоборот: сначала удалить все точки останова, потом добавить новую). Перезапустим программу, щёлкнем по кнопке и остановимся на точке. Проанализируем аргументы функции _TaskDialogIndirect (наводите на них мышь или используйте Evaluate/Modify). Вы увидите, что окно-родитель передаётся в pTaskConfig.hwndParent . Нам нужно сбросить это значение в ноль (и для начала стоит выяснить, что ноль ( NULL ) является допустимым аргументом — это так, мы проверили это по документации). Чтобы изменить это значение, удобнее всего вызвать Evaluate/Modify из окна локальных переменных:

Или щёлкните правой по pTaskConfig в редакторе кода и вызовите Evaluate/Modify из контекстного меню, затем допишите «.hwndParent» (без кавычек) в поле Expression и нажмите Evaluate.

Чтобы сбросить это значение, введите 0 в поле New value и нажмите Modify. Теперь значение обнулено:

Выполните функцию (по F8). Результат оказывается тем же самым (функция завершается с ошибкой E_INVALIDARG ). Т.е. дело не в окне-родителе.

Для второй гипотезы нам нужно выписать все аргументы функции. Для этого удобно развернуть окно локальных переменных на всю высоту и развернуть в нём все под узлы. Альтернативно можно также открыть несколько окон Evaluate/Modify:

Просто сделайте скриншот экрана.

Теперь нам нужно вызвать ShowMessage из экспортируемой функции. Для этого снимите выполняющуюся программу и измените текст DLL так:
Затем сохраните изменения, скомпилируйте, закройте проект DLL, откройте проект EXE и измените его код:
Мы пишем тестовый пример, поэтому мы можем наплевать на правильное освобождение ресурсов, но нам важна правильная обработка ошибок, т.к. она облегчает диагностику.

Сохраните и перекомпилируйте проект EXE. Запустите проект и нажмите на кнопку. Убедитесь, что теперь сообщение показывается (это происходит из экспортируемой функции). Закройте проект EXE и откройте проект DLL.

Вернитесь к нашей TaskDialogIndirect . Чтобы это быстро сделать — откройте список точек останова (View / Debug Windows / Breakpoints) и дважды щёлкните по (единственной) установленной точке останова — среда должна перенести вас на (вторую) строчку Result := _TaskDialogIndirect(. ) внутри WinAPI.CommCtrl.TaskDialogIndirect (да, надеюсь, в вашей среде были включены autosave-опции). Напомню, нас интересует второй вызов ShowMessage и, следовательно, второй вызов TaskDialogIndirect . Несложно догадаться, что во второй раз переменная _TaskDialogIndirect будет уже присвоена, поэтому выполнение пойдёт по первой ветке:
Что ж, установим точку останова на (первую) строчку Result := _TaskDialogIndirect(. ) (и уберём точку останова со (второй) строчки Result := _TaskDialogIndirect(. ) ) и запустим программу. Мы встанем на точке, после чего мы можем «заскриншотить» аргументы вызова _TaskDialogIndirect и сравнить их с нашим скриншотом первого вызова (из DllMain ).

Внезапно оказалось, что аргументы обоих вызовов полностью идентичны (но выполнение успешно лишь в одном случае, а в другом оно проваливается с ошибкой E_INVALIDARG ). Следовательно, дело не в аргументах самой TaskDialogIndirect , а в аргументах какой-то другой функции, которую вызывает TaskDialogIndirect — опять же, поскольку системные функции Windows используют коды ошибок, а не исключения, то мы никак не можем идентифицировать точную функцию, вернувшую ошибку.

На этом часто исследование можно считать законченным — у нас нет исходного кода Windows и мы не можем отлаживать её код. В таких случаях нам остаётся только перебор входных значений «угадыванием». Но в данном случае мы получаем ошибку E_INVALIDARG . Проверка аргументов — предположительно, что-то такое, что должно выполняться в начале функции, не слишком глубоко внутрь. Поэтому есть шанс, что мы сможем понять в чём дело, не потратив на это много времени.

Проходим по строчкам до вызова call и заходим в него по F7 (Step Into):

Как вы можете видеть, системная функция TaskDialogIndirect , на самом деле, очень короткая и является лишь переходником к чему-то другому (видимо, аналогично тому, как функция TaskDialog является переходником к TaskDialogIndirect ). В машинном коде не видно каких либо проверок (условных JMP), поэтому можно смело дойти до call и снова войти в него:

В этот раз ситуация интереснее. Код виден большой, длинный, есть какие-то проверки ( cmp , test ), переходы ( jz , jnz ).

Любая функция заканчивается инструкцией ret (в крайне редких случаях функция может безусловно передать управление на другую функцию вместо возврата управления). $CC ( int 3 ) является просто заполнителем свободного места между функциями (в системных DLL, Delphi же использует в качестве заполнителя случайный мусор). Иными словами, сейчас мы вошли в начало некоторой функции.

Однако не всегда ret обозначает конец функции. Это также может быть частью блока try/except/finally.

Что ещё отличается — перешли мы на какую-то внутреннюю функцию в ComCtl32.dll. Эта функция не экспортируется и не имеет имени. Поэтому отладчик >
Итак, абсолютный адрес у нас есть — это $7244550A. Как получить смещение? Для этого откройте View / Debug Windows / Modules и отсортируйте список загруженных DLL по имени (Name):

Найдите в списке DLL, соответствующую нашему адресу. В данном случае это будет ComCtl32.dll, что указывается в стеке вызовов. Альтернативно, вы можете отсортировать список по Base Address и взять максимальный адрес, который будет меньше нашего адреса. В обоих случаях вы найдёте строчку с ComCtl32.dll, откуда узнаете, что её базовый адрес — $72370000. Запускаем калькулятор Windows, переключаем его в режим «Программист», а также меняем режим на HEX, и: $7244550A — $72370000 = $000D550A. Именно это значение ($D550A) и будет смещением кода внутри (от начала) ComCtl32.dll. И именно это значение нужно скормить Address Lookup:

(Примечание: по умолчанию никакой настройки Address Lookup для использования сервера отладочной информации Microsoft выполнять не нужно, но если у вас что-то не работает — вот инструкция)

Окей, оказывается TaskDialogIndirect вызывает CTaskDialog.Show — что является методом Show класса CTaskDialog .

Что теперь? Теперь я предлагаю пройтись по коду с F8 (Step Over), внимательно следя за тем, что происходит в коде. В частности — какие переходы срабатывают. Тут можно придерживаться разных стратегий. Мы можем запустить две среды и две сессии отладки и отлаживать успешный и не успешный вызовы TaskDialogIndirect одновременно, сравнивая выполнение и находя отличия. Это гарантировано даст вам результат, но уж больно трудоёмко. Можно сообразить, что функция TaskDialogIndirect вернула нам код ошибки как результат (в EAX ), поэтому мы можем проследить по машинному коду, откуда пришло это значение («отладкой задом-наперёд»).

Можно также сообразить, что функция состоит непосредственно из кода, а также из вызовов других функций. Мы уже знаем (выяснили выше), что проблема — не в проверке аргументов непосредственно в самой TaskDialogIndirect , проблема в какой-то другой функции, которую вызывает TaskDialogIndirect . Есть ненулевая вероятность, что TaskDialogIndirect вызывает другую функцию, которая также возвращает HRESULT . Следовательно, мы можем следить за появлением известного кода ошибки ($80070057) в регистре EAX после вызова функций ( call ). Поэтому мы просто выполняем код CTaskDialog.Show по F8 (Step Over), пока не увидим $80070057 в EAX :

Окей, а вот и наша под-функция. Нам не пришлось идти слишком далеко (кажется, это третья вызываемая функция). Теперь мы можем установить точку останова на call $7248d137 и перезапустить программу, после чего остановиться на точке и войти в под-функцию по F7. Это снова будет какая-то внутренняя безымянная функция ComCtl32.dll. Снова используем калькулятор: $7248D137 — $72370000 = $11D137 и снова используем Address Lookup:

Илон Маск рекомендует:  Что такое код asp aspdescription

Немного странное имя, но ОК, предположим. В принципе, это имеет смысл, если учесть, что мы вызываем код из DllMain , которая имеет известные ограничения.

Что ж, повторим наш алгоритм: будем проходить код по F8 (Step Over), следя за появлением «волшебного кода» ($80070057) в регистре EAX после вызовов функций ( call ). В этот раз нам придётся пройти довольно много кода, но в итоге мы находим ещё один вызов:

(Кстати, обратите внимание, это как раз одна из функций, которая заканчивается на jmp , а не ret .)

Устанавливаем точку останова и перезапускаем программу (предыдущую точку останова можно удалить, либо просто пропускать/игнорировать при выполнении — главное, не запутаться).

Ой. В отличие от прошлого раза, где под-функция возвращала HRESULT , в этот раз HRESULT приходит от GetLastError , а сама проблемная функция вызывается ранее и, вероятно, возвращает просто 0 ( False ).

Само собой, искать 0 в EAX — гиблое дело, ибо там он будет ну очень часто. Вместо этого можно поступить двумя способами:

  1. Логично предположить, что провалившаяся функция вызывается непосредственно перед вызовом GetLastError , т.е. надо проверить только предыдущий call ;
  2. Можно вместо EAX следить за GetLastError .

И хотя в нашем случае вполне подходит вариант 1, давайте посмотрим на вариант 2. Для этого откройте окно Watch List (View / Debug Windows / Watches), щёлкните по нему правой кнопкой мыши и выберите Add Watch во всплывающем меню:

Впишите «GetLastError» (без кавычек) в Expression, включите Allow side effects and function calls и переключите отображение в Hexadecimal. Allow side effects and function calls необходима, чтобы отладчик вообще вычислял бы выражение с «GetLastError». По умолчанию отладчик не будет вычислять выражения для отладки, если это потенциально может изменить состояние программы. В нашем случае мы знаем, что вызов GetLastError — «безопасен», поэтому мы явно указываем это отладчику. Переключение же вида в Hexadecimal необходимо по той причине, что функция GetLastError возвращает код ошибки в виде числа ( DWORD / Cardinal ), даже хотя в нашем случае этим числом является HRESULT . Переключение режима позволит нам увидеть $80070057, а не -2147024809 (похоже, отладчик Delphi не различает знаковые и беззнаковые типы в Watch-ах).

Перезапустите программу и снова пройдитесь по коду, как мы это делали выше, только теперь вместо EAX смотрите и за EAX , и за GetLastError в Watch List.

Так или иначе вы находите проблемный вызов:

Установите точку останова, перезапуститесь, войдите:

Можно предположить, что этот странный код является заглушкой-переходником. «Входим» в jmp (не важно — по F8 или по F7, jmp это безусловный переход, а не вызов функции):

. и снова переходник! Входим в call :

. и ещё один! Снова входим в call :

Ага, а вот вам и причина для всех этих переходников: InitThread вызывает какую-то внешнюю (импортируемую) функцию, связанную с ComCtl32.dll через отложенный импорт (Delay Loaded). Собственно, здесь мы можем лишь войти в настоящую реализацию отложенного импорта:

Что ж, функция это довольно большая и длинная. Есть куча переходов, вызовов, даже ret -ы, и даже GetLastError будет меняться. Но нам не нужно анализировать весь этот код. Несложно догадаться, что функция должна найти адрес целевой функции (загрузить DLL, сделать туда GetProcAddress ), после чего сохранить результат в переменную (регистр/память) и сделать на него переход (не вызов! т.к. для вызова нужно формировать параметры, про которые функция не в курсе). Т.е. нам нужно дойти до jmp на опосредованное значение (т.е. не на фиксированный адрес типа $7244559С, а на, скажем, регистр).

Более того, если вы были действительно внимательны, то заметили, что этот jmp у нас уже есть — посмотрите выше на функции-переходники: по адресу $723F86EE как раз лежит jmp , который переходит на EAX (результат функции). Т.е. нам нужно только установить там точку останова и запустить программу по F9 (Run / Run). Ну или через F4 (Run to Cursor).

А вот и наша функция — некая InitGadgets из DUser.dll. Описание DUser.dll сообщает, что это — «Windows DirectUser Engine». Это недокументированная, внутренняя DLL Windows.

Окей, продолжаем выполнение дальше. Я, кстати, рекомендую поставить точку останова на начало InitGadgets — когда мы пропустили так много кода (особенно — внутреннего кода), лучше иметь надёжную точку для отката (точку останова в экспортируемой функции), на всякий пожарный. В любом случае, немного пройдясь по коду, следя за EAX и GetLastError , вы быстро найдёте следующее звено:

$6716С0EС — $67160000 (база для DUser.dll) = $C0EC =

ResourceManager.InitContextNL . И буквально чуть-чуть далее:

Опа! И кто же это такой? ;)

Мы заметили явно «наше» проблемное значение $80070057, но не заметили, а как же мы попали в эту строчку. Очевидно, произошла какая-то проверка, которая отправила «хороших» — дальше по коду, а «плохих» завернула на эту ветку. Нам нужно перезапустить программу и пройти InitContextNL заново, внимательно следя на условными переходами — не отправит ли кто нас на адрес $67187А91. И вот мы находим проверку (мы уже так близки к разгадке!):

А вот и переход. Как мы видим, InitContextNL вызывает какую-то функцию, та возвращает ей, видимо, TRUE (1 в EAX ), что не нравится InitContextNL , и она переходит на установку жёстко зашитого кода ошибки. Немного странно: т.е. функция по ESI не завершается с ошибкой (иначе она вернула бы FALSE ), вместо этого функция возвращает какую-то информацию. Возможно, мы не так близки к разгадке, как думали.

Смотрим, $6716BC60 — это:

WinNT.IsInsideLoaderLock ! Тайна раскрыта!

Давайте реконструируем стек вызовов:

  • Button1Click
  • LoadLibrary
  • DllMain
  • ShowMessage
  • TaskDialogIndirect
  • CTaskDialog.Show
  • InitThread
  • InitGadgets
  • ResourceManager.InitContextNL
  • WinNT.IsInsideLoaderLock

WinNT.IsInsideLoaderLock возвращает True — это действительно так, ведь мы находимся внутри DllMain , т.е. критическая секция загрузчика ОС занята нами. Это значение трактуется как ошибка методом InitContextNL класса ResourceManager , который и возвращает искомый код ошибки E_INVALIDARG ($80070057) — даже хотя ошибка не имеет отношения к аргументам. Ну и несложно догадаться, что далее этот код ошибки всплывает до вызова TaskDialogIndirect внутри ShowMessage (где и успешно игнорируется).

Иными словами, Task Dialog API явно проверяет, не вызывают ли его из DllMain , и если да — то отказывается работать.

Это не указано явно в документации к Task Dialog API, но указано опосредовано в описании DllMain :

Calling functions that require DLLs other than Kernel32.dll may result in problems that are difficult to diagnose.

Диалоговые окна в Дельфи (процедуры и функции, реализующие диалоговые окна)

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

Процедура ShowMessage, функции MessageDlg и MessageDlgPos отображают окно (панель) вывода сообщений, а функции InputBox и InputQuery — окно (панель) для ввода информации.

Процедура ShowMessage

Процедура ShowMessage (const Msg: String) отображает окно сообщения с кнопко ОК. Заголовок содержит название исполняемого файла приложения, а строка Msg выводится как текст сообщения.

Функция MessageDlg

Функция MessageDlg(const Msg: String; AType: TMsgDlgType; AButtons: TMsgDlgButtons; Helpctx: Longint) : word отображает окно сообщения в центре экрана и позволяет получить ответ пользователя. Параметр Msg содержит отображаемое сообщение.

Окно сообщения может относиться к различным типам и наряду с сообщением содержать картинки. Тип окна сообщения определяется параметром АТуре, который может принимать следующие значения:

  • mtWarning (окно содержит черный восклицательный знак в желтом треугольнике и заголовок Warning);
  • mtError (окно содержит белый косой крест в красном круге и заголовок Error);
  • mtInformation (окно содержит синюю букву «i» в белом круге и заголовок Information);
  • mtConfirmation (окно содержит синий знак «?» в белом круге и заголовок Confirmation);
  • mtCustom (окно не содержит картинки, в заголовке выводится название исполняемого файла приложения).

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

  • mbYes (кнопка Yes);
  • mbAbort (кнопка Abort);
  • mbNo (кнопка No);
  • mbRetry (кнопка Retry);
  • mbOk (кнопка OK);
  • mbIgnore (кнопка Ignore);
  • mbCancel (кнопка Cancel);
  • mbAll (кнопка All);
  • mbHelp (кнопка Help);

Для значения параметра AButtons имеются две константы — mbYesNoCancel и mbOKCancel, задающие предопределенные наборы кнопок:

  • mbYesNoCancel = [mbYes, mbNo, mbCancel];
  • mbOKCancel = [mbOK, mbCancel]

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

Параметр HelpCtx определяет контекст (тему) справки, которая появляется во время отображения диалогового окна при нажатии пользователем клавиши . Обычно! значение этого параметра равно нулю.

Пример использования функции MessageDlg:

При нажатии кнопки Button1 производится простейшая проверка даты. Код даты вводится в поле редактирования DateEdit1, размещенное в форме. Если длина даты меньше допустимой, выдается предупреждение с запросом на автоматическую коррекцию (см. скриншот). При утвердительном ответе пользователя в поле даты записывается текущая дата, при отрицательном — фокус передается полю ввода даты.

Функция MessageDlgPos

Функция MessageDlgPos(const Msg: String; AType: TMsgDlgType; AButtons: TMsgDlgButtons; HelpCtx: Longint; X, Y: Integer) : Word отличается от функции MessageDlg наличием параметров Х и Y, управляющих положением окна на экране.

Функция InputBox

Функция InputBox(const ACaption, APrompt, ADefault: String): String отображает диалоговое окно для ввода строки текста. Окно выводится в центре экрана и содержит поле ввода с надписью, а также кнопки ОК и Cancel.

Параметр ACaption задает заголовок окна, а параметр APrompt содержит поясняющий текст к полю ввода. Параметр ADefault определяет строку, возвращаемую функцией при отказе пользователя от ввода информации (нажатие кнопки Cancel или клавиши ).

Пример использования функции InputBox:

Приведенная процедура отображает окно запроса на ввод фамилии пользователя (см. скриншот).

По умолчанию предлагается Иванов.

Функция InputQuery

Функция InputQuery (const ACaption, APrompt: String; var Value: String): Boolean отличается от функции InputBox тем, что вместо третьего параметра— строки по умолчанию— используется параметр Value, который в случае подтверждения ввода содержит введенную пользователем строку.

В качестве результата функция возвращает логическое значение, позволяющее определить, каким образом завершен диалог. Если нажата кнопка ОК, то функция возвращает значение True, если нажата кнопка Cancel или клавиша — значение False.

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

Кроме рассмотренных диалоговых окон, в Delphi имеется ряд других специализированных диалоговых окон, например диалоговое окно выбора каталога, вызываемое функцией Select Directory модуля FileCtrl.

Диалоговые окна для вывода сообщений в Delphi

(ShowMessage, MessageDlg и MessageDlgPos)

Для оповещения пользователя о некорректно введенных данных можно использовать процедуру ShowMessage или функции MessageDlgPos и MessageDlg.

ShowMessage(const Msg: String) — эта процедура выводит окно с сообщением и кнопкой Ok. В заголовке содержится название исполняемого файла, если в опциях приложения не задан параметр Title, если задан то выводиться будет он. Строка Msg — будет выводиться как текст сообщения.

MessageDlg(const Msg: String; AType: TMsgDlgType; Abuttons: TMsgButtons; HelpCtx: Longint): Word — функция показывающая диалоговое окно сообщения в центре экрана и дает возможность пользователю ответить на сообщение. Msg — параметр отвечающий за выводимый текст сообщения.

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

  • mtError — на фоне красного круга расположен белый косой крест и заголовок окна — Error;
  • mtWarning — на фоне желтого треугольника расположен черный восклицательный знак -«!» и заголовок окна — Warning;
  • mtConfirmation на фоне белого круга расположен синий знак «?» и заголовок окна — Confimation;
  • mtInformation — на фоне белого круга расположена синия буква «i» и заголовок окна — Information;
  • mtCustom — диалоговое окно не содержит рисунка, в заголовке выводиться имя исполняемого файла приложения или Title свойства Application приложения.

AButtons — параметр, который задает набор кнопок на диалоговой форме и может принимать произвольные комбинации из значений:

  • кнопка Yes — mbYes,
  • кнопка Ok — mbOk,
  • кнопка No — mbNo,
  • кнопка Cancel — mbCancel,
  • кнопка Abort — mbAbort,
  • кнопка Retry — mbRetry,
  • кнопка Ignore — mbIgnore,
  • кнопка All — mbAll,
  • кнопка Help — mbHelp.

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

MessageDlg(‘Большое значение’, mtConfirmation, [mbYes,mbNo],0 );

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

  • кнопки OK и Cancel — mbOkCancel,
  • кнопки Yes, No и Cancel — mbYesNoCancel,
  • кнопки Abort, Retry и Ignore — mbAbortRetryIgnore.

Эти константы не надо брать в скобки, так как они являются предопределенными множествами.

Если мы внутри скобок не объявим список, то в диалоговом окне не будет ни одной кнопки и пользователю придется закрывать окно системными кнопками Windows.

MessageDlg(‘Большое значение’, mtConfirmation,mbAbortRetryIgnore,0 );

При щелчке на любой кнопке (кроме Help) результат возвращается функцией MessageDlg (свойство ModalResult), а сама форма закрывается. Результат может быть одним из значений списка:

mrOk, mrRetry, mrNo,mrNone, mrAbort, mrYes, mrCancel, mrIgnore, mrAll

Параметр HelpCtx задает тему справки. Ее можно вызвать во время показа диалогового окна клавишей F1. Обычно этот параметр устанавливается равным нулю и справка не выводиться.

Не нашли то, что искали? Воспользуйтесь поиском:

Лучшие изречения: Только сон приблежает студента к концу лекции. А чужой храп его отдаляет. 8806 — | 7522 — или читать все.

188.64.174.135 © studopedia.ru Не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования. Есть нарушение авторского права? Напишите нам | Обратная связь.

Отключите adBlock!
и обновите страницу (F5)

очень нужно

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL