федеральное государственное образовательное
учреждение Высшего профессионального образования
«Южный федеральный университет»
Авакян Леон Александрович
«ИСПОЛЬЗОВАНИЕ
WIN
API
ФУНКЦИЙ В СРЕДЕ ДЕЛФИ»
(учебно-методическое пособие)
Ростов-на-Дону
2008
Авакян Л.А.
Использование Win API функций в среде Делфи: Учебно-методическое пособие. - Ростов-на-Дону, 2008. - 45 с.
Учебно-методическое пособие «Использование Win API функций в среде Делфи» к курсам лекций по программированию и вычислительным методам для студентов физического факультета ЮФУ ставит своей задачей оказать помощь студентам в понимании и освоении приемов программирования в Windows. В пособии даны общие понятия о строении операционной системы Windows, описаны способы создания окон и элементов управления, а также способы управления сторонними приложениями.
Содержание
Содержание 3
Введение 4
Модуль 1. Архитектура Windows 5
1.1. Что такое Win API?_ 5
1.2. Оконный дескриптор, сообщение и событие 8
1.3. Функция MessageBox_ 10
Проектное задание 12
Тест рубежного контроля №1_ 12
Модуль 2. Создание элементов управления_ 14
2.1. Создание окна средствами WinAPI 14
2.2. Создание кнопок (button) средствами WinAPI 19
2.3. Создание однострокового редактора (Edit) средствами WinAPI 21
2.3. Создание многострокового редактора (Memo) средствами WinAPI 23
2.4. Меню на WinAPI 24
2.5. Стандартные диалоговые окна_ 27
Проектное задание 28
Тест рубежного контроля №2_ 29
Модуль 3. Управление сторонними приложениями_ 31
3.1. Утилита слежения за окнами и сообщениями WinSight32_ 31
3.2. Приемы управления сторонними приложениями_ 35
Проектное задание 43
Тест рубежного контроля №3_ 44
Список литературы_ 45
Настоящее учебно-методическое пособие разработано на основе нормативных документов Министерства образования и науки Российской Федерации, относящихся к вхождению России в Болонский процесс, теории образовательной квалиметрии и современных технологий дидактического обеспечения учебного процесса.
Пособие адресовано студентам для самостоятельного и более углубленного изучения приемов программирования под операционной системой Windows во время и после прослушивания курсов лекций по программированию. Его основная цель состоит в оказании помощи в понимании и освоении Win API функций операционной системы.
После изучения материала модулей, представленных в предлагаемом пособии, обучающемуся необходимо выполнить предложенное проектное задание и ответить на вопросы теста рубежного контроля. Переход к изучению материала следующего модуля возможен только после успешного выполнения предложенных контрольных материалов.
Введение
Настоящее учебно-методическое пособие посвящено API (application programming interfaces) функциям операционной системы Microsoft Windows, вызов которых мало отличается в различных языках программирования. Все примеры, приведенные в пособии, написаны на языке Object Pascal (среда программирования Delphi), как на наиболее распространенном языке обучения на департаменте физики ЮФУ.
Методическое пособие состоит из трех частей. В первой части даны основные понятия (дескриптор, сообщение), которые используются в дальнейшем. Также, в этой части описаны некоторые полезные функции, которые могут быть применены в любом приложении. Во второй части поэтапно описывается, как создать окно и управляющие элементы на нем без использования библиотек (VCL, MFC и пр.), лишь на основе API функций. Одним из преимуществ таких программ является их малый размер (десятки килобайт), что может быть очень важно в ряде случаев (например, при передаче приложений по сети). В третьей части описаны приемы управления сторонними приложениями на основе Windows API.
Предполагается, что читатель знаком со средой Delphi, знает синтаксис и умеет работать с указателями. Хотя примеры и написаны исключительно на Object Pascal, материал может представлять интерес и для программистов других языков.
Модуль 1. Архитектура
Windows
Комплексная цель:
получение общих знаний о работе операционной системы Windows, понятие о дескрипторах и сообщениях.
Краткое изложение программного материала:
в модуле даны понятия об
· API функциях операционной системы
· Дескрипторах и сообщениях Windows
Рассмотрена функция API MessageBox, как наиболее полезная и простая в применении функция.
1.1. Что такое
Win API?
В настоящее время очень широкую распространенность полу
чили операционные системы семейства Windows NT/20000/XP. Они широко используются не только как домашние системы, но и в качестве серверов.
Как известно, OC Windows NT целиком построена на системе DLL (динамически загружаемых библиотек). Система предоставляет приложениям сервисные API функции, с помощью которых оно может взаимодействовать с системой.
API функции представляют из себя ничто иное как функции в системных DLL.
Основной язык для приводимых фрагментов кода - Delphi, но материал актуален и для любого другого языка (С, С++, Ассемблер и.т.д.).
Функции и процедуры Windows API, или, сокращенно, WinAPI-функции, являются набором программных интерфейсов приложений (application programming interfaces - APIs), доступных в операционных системах Microsoft Windows. Все Windows-приложения взаимодействуют с WinAPI вне зависимости от того, на каком языке они написаны.
Доступ более низкого уровня к системе Windows, часто применяющийся в драйверах устройств, предоставляется Windows Driver Foundation или Native API, которые отличаются в разных версиях операционной системы.
Имеется детальное описание WinAPI функций, выпускаемое Microsoft под названием software development kit (SDK), которое также включает в себя некоторые инструменты разработки приложений и примеры программного кода.
Все Windows API функции располагаются в файлах динамических библиотек (*.dll), которые находятся в системных папках ОС (как правило, папка System32).
Функциональность, предоставляемая WinAPI, может быть разбита на 8 категорий:
1. Основные сервисы
(Base Services) предоставляют доступ к основным ресурсам системы Windows, таким, как файловые системы, устройства, процессы и потоки, обработка ошибок. Эти функции располагаются в файлах kernel
32.
dll
, advapi
32.
dll
.
2. Дополнительные сервисы
(Advanced Services) предоставляют доступ к надстройкам над kernel-библиотеками, включая такие элементы, как реестр Windows, перезагрузка и завершение работы системы, запуск/создание/остановка сервисов Windows, управление учетными записями пользователей. Эти функции располагаются в advapi
32.
dll
.
3. Интерфейс взаимодействия с графическими устройствами
(Graphics Device Interface, или, сокращенно, GDI) предоставляет набор функциональных возможностей вывода графического содержимого на мониторы, принтеры и другие устройства вывода. Функции располагаются в файле gdi
32.
exe
.
4. Пользовательский интерфейс
(User Interface, UI) предоставляет набор функций создания и управления окнами и наиболее простых элементов управления, таких, как кнопки и полосы прокрутки; получение данных с мыши и клавиатуры и прочие функции, связанные с графическим интерфейсом пользователя (GUI). Этот модуль расположен в файле user
32.
dll
, однако, начиная с Windows XP, эти функции объединены с библиотекой стандартных элементов управления (Common Control Library) и располагаются в файле comctl
32.
dll
.
5. Стандартная библиотека диалогов
(Common Dialog Box Library) предоставляет стандартные диалоговые окна для открытия и сохранения файлов, выбора цвета, шрифта и т.д. Библиотека располагается в файле comdlg
32.
dll
. Эту категорию часто объединяют с пользовательским интерфейсом.
6. Стандартная библиотека элементов управления
(Common Control Library) позволяет приложениям использовать дополнительные элементы управления операционной системы Windows, включая такие элементы, как панели статуса, панели состояния, панели инструментов и вкладки. Библиотека располагается в файле comctl
32.
dll
. Эту категорию также часто объединяют с пользовательским интерфейсом.
7. Оболочка
Windows
(Windows Shell) является компонентом WinAPI, который предоставляет доступ к функциям системной оболочки, также позволяя изменять и улучшать их. Компонент располагается в файлах shell
32.
api
, shlwapi
.
dll
.
8. Сетевые сервисы
(Network Services) предоставляют доступ к различным сетевым возможностям операционной системы. Их подкомпоненты включают NetBIOS, Winsock, NetDDE, RPC и многие другие.
В методическом пособии будут рассмотрены функции пользовательского интерфейса и графической библиотеки GDI как наиболее простые и часто употребляемые. Применение этих функций не требует глубокого понимания архитектуры операционной системы, однако результат применения этих функций очень часто позволяет упростить командный код и добиться результатов, недостижимых с помощью стандартных методов среды разработки.
1.2. Оконный дескриптор, сообщение и событие
Объект в Windows является некоторой сложной структурой, которая представляет системный ресурс, такой как файл, поток, графическое изображение. Приложение не может обращаться напрямую к внутренней структуре объекта или системным ресурсам, которые он представляет. Вместо этого, приложение должно получить специальный дескриптор (handle) объекта и использовать этот дескриптор для работы с системным ресурсом. В Microsoft® Win32® программном интерфейсе приложений (WinAPI) дескрипторы применяются как непрямые указатели.
Оконный дескриптор (hWnd) является уникальным идентификатором (который является целым четырехбайтовым беззнаковым числом), который присваивает Windows каждому созданному окну. В этом случае, под окном подразумевается любые элементы: от кнопок управления и надписей до диалоговых окон и окон в целом.
Windows посылает специальные, определенные системой, сообщения при взаимодействии с приложением. Она использует эти сообщения для управления действиями приложения и для предоставления ввода и прочей информации, нужной для работы приложения. Приложения также могут отправлять такие сообщения для различных целей, например, для управления окнами, созданными на основе заранее зарегистрированных оконных классов.
Практически каждому сообщению в Windows сопоставлено событие в Делфи, при поступлении которого можно выполнить специальные действия. Мы не будем использовать библиотеку VCL, а вместе с ней и события в Делфи (см. модуль 2).
Каждое системное сообщение имеет уникальный идентификатор сообщения (номер) и соответствующее ему название (определенное в заголовочном файле), которое показывает назначение сообщения. Например, сообщение с названием WM_PAINT просит окно перерисовать свое содержимое.
Название сообщения описывает также и категорию, к которой это сообщение принадлежит. Приставка в названии определяет тип окна, которое может прочитать и обработать это сообщение. Например, приставка WM_ обозначает, что сообщение предназначается обычному окну.
Сообщения для обычных окон включают в себя множество информационных сообщений и запросов, таких, как сообщения от мышки и клавиатуры, выбор элементов меню и диалоговых окон, создание и управление окнами, динамический обмен данными (dynamic data exchange - DDE).
Приложение может создавать сообщения, которые могут использовать для своих внутренних нужд или для взаимодействия с окнами других процессов. В случае, если приложение создает свои собственные сообщения, оконная функция, которая принимает их, должна интерпретировать эти сообщения и соответствующим образом их обработать.
В операционной системе Windows зарезервированы значения идентификаторов сообщений в интервалах от 0x0000 до 0x03FF (WM_USER – 1) и от 0x8000 до 0xBFFF для системных сообщений. Приложения не могут использовать эти значения для своих собственных сообщений.
1.3. Функция
MessageBox
Начнем знакомиться с API функциями с наиболее простой и часто употребляемой функции MessageBox. Эта функция позволяет выводить стандартное окно сообщения Windows с заданны текстом и кнопками.
Функция объявлена в файле windows.pas следующим образом:
function
MessageBox; external
user32 name 'MessageBoxA';
Откуда видно, что функция реализована в библиотеке user32.dll. Синтаксис функции описан в справке (на языке C++):
int MessageBox
(
HWND
hWnd, // ссылка на окно владельца
LPCTSTR
lpText, // текст сообщения
LPCTSTR
lpCaption, // заголовок окна
UINT
uType // стиль окна
);
hWnd – ссылка на окно-владельца, может быть равно 0, тогда владельцем будет рабочий стол windows.
lpText, lpCaption – строки символов текста сообщения и заголовка соответственно. В языке Object Pascal имеют тип PChar. Для преобразования строки типа String к PChar можно использовать функцию PChar(). Если текст сообщения задан в явном виде, то преобразование будет проведено автоматически, например, код
var
s: string
;
…
s := 'ZL';
MessageBox(0, 'Текст сообщения', PChar(s), 0);
приведет к появлению окна, похожего на приведенное на рисунке 1. Отличия в оформлении могут быть связаны с особенностями и настройкой операционной системы.
Рис. 1.
uType – указывает особенности окна сообщения, такие, как кнопки и иконка в левой его части. Например, указав MB_OK (который равен нулю, чем мы и воспользовались ранее), мы получим окно с одной кнопкой Ok. Если бы мы указали стиль MB_ICONSTOP OR
MB_OKCANCEL, то получили бы окно с двумя кнопками Ok и Cancel и иконкой со знаком остановки. Полный список параметров приведен в справке Delphi и MSDN.
Возвращаемое значение функции равно одной из констант IDABORT, IDCANCEL, IDIGNORE, IDNO, IDOK, IDRETRY, IDYES в зависимости от выбранной пользователем кнопки (если их несколько).
Примером использования этой функции является окно, появляющееся при выходе из программы. Запишем обработчик события формы OnCloseQuery:
procedure
TForm1.FormCloseQuery(Sender: TObject; var
CanClose: Boolean);begin
CanClose := MessageBox( handle, {handle –
свойство
формы
,
содержащее
дескриптор
окна
}
PChar('Действительно выйти?'+#13+'Все несохраненные данные будут потеряны.'), '', MB_ICONQUESTION or
MB_YESNO ) = IDYES;end
;
Окно, выводимое при попытке закрыть программу, приведено на рисунке 2.
Рис. 2.
Проектное задание
Написать программу, выводящую дескриптор окна и всех оконных компонентов (свойство Handle всех объектов-наследников TControl библиотеки VCL) в окне, выводимом функцией MessageBox, содержащем три кнопки – Ok, Retry, Cancel. Проверить, какую из кнопок выбрал пользователь.
Тест рубежного контроля №1
Тест содержит 5 заданий, на выполнение которых отводится 3 минуты. Выберите наиболее правильный, по Вашему мнению, вариант ответа и отметьте его любым значком в бланке ответов.
1. Windows API функции это: |
|||
1) |
Функции, предоставляемые оболочкой Delphi при работе в ОС Windows |
2) |
Функции всех динамических библиотек (DLL) |
3) |
Системные функции операционной системы Windows, используемые при разработке приложений |
4) |
Функции, которые выполняет операционная система (поддержка устройств, интерфейс пользователя…) |
2. Сообщения в Windows позволяет |
|||
1) |
Взаимодействовать операционной системе и приложению |
2) |
Управлять системой из приложения |
3) |
Управлять приложением |
4) |
Все вышеперечисленное |
3. Дескриптор это |
|||
1) |
Объект или ресурс операционной системы |
2) |
Некоторое однобайтовое целое число |
3) |
Непрямой указатель |
4) |
Указатель на область памяти |
4. Выполнение кода MessageBox(0, ‘’, ‘’, MB_ICONASTERYX or MB_YESNO) приведет к |
|||
1) |
Возникновению ошибки при исполнении |
2) |
Возникновению ошибки при компиляции |
3) |
Появлению окна без заголовка и текста, но с иконкой с вопросом и кнопками да/нет |
4) |
Появлению окна без заголовка и текста, но с иконкой остановки и кнопками да/нет |
5. Выполнение кода MessageBox(0, '', '', MB_OK or MB_YESNO or MB_ABORTRETRYIGNORE or MB_RETRYCANCEL) приведет к |
|||
1) |
Возникновению ошибки при исполнении |
2) |
Возникновению ошибки при компиляции |
3) |
Не приведет к возникновению ошибки, однако окно показано не будет |
4) |
Приведет к появлению окна с большим количеством кнопок |
Бланк ответов
№ |
1 |
2 |
3 |
4 |
1) |
||||
2) |
||||
3) |
||||
4) |
||||
5) |
Критерии оценки
4 и более правильных ответов – отлично
3 правильных ответа – хорошо
2 правильных ответа – удовлетворительно
1 и менее правильных ответов - неудовлетворительно
Модуль 2. Создание элементов управления
Разработка программ зачастую напоминает священный ритуал, построенный на произнесении ряда обязательных магических заклинаний. Особенно это касается Windows приложений. Windows-заклинания позволяют вывести графическое окно, обработать поступающие сообщения. Порядок их следования предопределен священными руководствами.
Легалов А. И.
, д.т.н., проф. Сибирского федерального университета
Комплексная цель:
научиться создавать полноценные программы без использования дополнительных визуальных библиотек, таких как VCL в Delphi или MFC в C++.
Краткое изложение программного материала:
в модуле показано как создавать на основе Win API функций:
· Окна
· Кнопки
· Редакторы ввода
· Меню
· Стандартные диалоговые окна
Материал ориентирован на студентов, умеющих читать и понимать программный код и снабжен большим количеством примеров. Приведены некоторые способы управления созданными элементами.
2.1. Создание окна средствами
WinAPI
Для того, чтобы создать окно, необходимо:
1. Создать и зарегистрировать в Windows специальный класс окна с помощью функции RegisterClass();
2. Создать (CreateWindow) и показать (ShowWindow) экземпляр окна;
3. На этапе создания необходимо указать процедуру, которая будет обрабатывать сообщения, приходящие из операционной системы. В этой процедуре в дальнейшем и будет сосредоточен весь код.
Регистрация специального класса окна производиться с помощью функции ATOM RegisterClass(CONST WNDCLASS *lpWndClass
), возвращаемое значение которой равно нулю в случае ошибки. Информация о классе содержится в структуре lpWndClass
типа WNDCLASS* (язык C), которую необходимо предварительно заполнить, например, следующим образом (Delphi):
Var
WndClass: TWndClass;
…
FillChar(WndClass, SizeOf(WndClass), 0); //заполняем структуру
нулями
with
WndClass do begin
hInstance := SysInit.hInstance; //
Обязательный
параметр
,
идентификатор приложения
lpszClassName := sClassName; //имя класса
lpfnWndProc := @WindowProc; //
ссылка на оконную функцию
hbrBackground := GetStockObject(LTGRAY_BRUSH); // способ
заливки клиентской области
end
;
Заполнение нулями необходимо для того, чтобы мусор, который, возможно, остался в памяти, не повлиял на параметры, значения которых явно не прописывается (стиль окна, иконка, курсор…).
После того, как создан класс, создаем на его основе окно. Это делается с помощью функции CreateWindow().
После того, как окно создано, его можно показать, например, с помощью функции ShowWindow(). Следует отметить, что эту функцию можно применить только к родительскому окну, т.е. если приложение содержит главное окно и дочерние компоненты, то аргументом этой функции является дескриптор главного окна.
Прием сообщений осуществляется с помощью нескольких функций: GetMessage(); TranslateMessage(); DispatchMessage(); DefWindowProc(); Для того, чтобы организовать цикл получения сообщений, нужно в первую очередь написать функцию окна, т.е. функцию, которая будет совершать какие-либо действия в ответ на сообщение (адрес этой функции указывается в структуре WNDCLASS: IpfnWndProc:=@WindowProc). Необходимо правильно описать функцию:
На языке С++:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
На языке Object Pascal: function
WindowProc(hWnd: THandle; uMsg, wParam, lParam: Integer): Integer; stdcall
; export
;
где HWND - дескриптор окна, который был получен после выполнения функции CreateWindow(), uMsg - код сообщения (16 бит) (может использоваться, например, следующим образом: if
uMsg=WM_CLOSE then
…),WPARAM, LPARAM - параметры, которые передаются вместе с сообщением (16 бит).
Цикл обработки сообщений имеет следующий вид:
while
GetMessage(Msg, 0, 0, 0) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end
;
Функция GetMessage() выбирает первое сообщение из очереди сообщений, предназначенных для данного приложения, функция TranslateMessage() переводит виртуальный код сообщения в строку символов, функция DispatchMessage() отправляет сообщение на обработку оконной функцией, заданной при регистрации класса окна.
Оконная функция может иметь следующий вид:
function
WindowProc(hWnd: THandle; uMsg, wParam, lParam: Integer): Integer; stdcall
; export
;begin
Result := 0; case
uMsg of
WM_DESTROY: … WM_PAINT: … … end
else
Result := DefWindowProc(hWnd, uMsg, wParam, lParam); end
;
Внутри блока case
располагается код, ответственный за особую реакцию приложения на некоторые сообщения (закрытие окна, его перерисовка). Функция DefWindowProc вызывает стандартную оконную функцию, которая обрабатывает все остальные сообщения, не указанные в блоке.
Пример создания простейшего окна приведен ниже. Обратите внимание, что весь код располагается в файле project1.dpr, как и в случае консольного приложения (но без директивы $APPTYPE CONSOLE).
program
project1;uses
Windows, Messages; const
sClassName = 'MyWindow'; //
Имя
класса
окна
var
hWnd: THandle; //
идентификатор
окна
(
дескриптор
)
WndClass: TWndClass; //структура
WndClass
Msg: TMsg; //структура для принятия сообщений
function
WindowProc(hWnd: THandle; uMsg, wParam, lParam: Integer): Integer; {
функция
окна
}
stdcall
; export
;begin
Result := 0; case
uMsg of
WM_DESTROY: //
если
uMsg
равна
WM_DESTROY(
код
закрытия
окна
)
то
закрываемся
begin
halt(0); end
; end
; Result := DefWindowProc(hWnd, uMsg, wParam, lParam); //
обработать
остальные
сообщения
end
; begin
FillChar(WndClass, SizeOf(WndClass), 0); //
заполняем
структуру
нулями
with
WndClass do begin
hInstance := SysInit.hInstance; lpszClassName := sClassName; //
имя
класса
lpfnWndProc := @WindowProc; //
ссылка
на
оконную
функцию
hbrBackground := GetStockObject(LTGRAY_BRUSH); end
; RegisterClass(WndClass); //регистрируем класс
hWnd := CreateWindow(sClassName, '', WS_OVERLAPPEDWINDOW, 200, 200, 300, 300, 0, 0, hInstance, NIL
); //создаем окно
if
hWnd = 0 then begin
//если произошла ошибка, то выходим
MessageBox(0, 'Initialisation failed', NIL
, ID_OK); Exit; end
; ShowWindow(hWnd, SW_normal); //
показываем
окно
while
GetMessage(Msg, HWnd, 0, 0) do begin
//
получаем
сообщение
TranslateMessage(Msg); DispatchMessage(Msg); end
; Halt(Msg.wParam);end
.
В результате работы такой программы должно появиться обычное окно, которое можно закрыть стандартным образом.
Все последующие примеры этой главы будут основаны на приведенном коде, изменяться будет лишь оконная функция.
2.2. Создание кнопок (
button) средствами WinAPI
Создание кнопки производится с помощью уже использованной нами для создания окна функцией CreateWindow. В качестве класса окна нужно использовать определенный в системе класс кнопки BUTTON. Основные доступные классы, предоставляемые операционной системой, следующие: LISTBOX, COMBOBOX, MEMO, MAINMENU, EDIT, SCROLLBAR, BUTTON, LISTVIEW, STATIC, TREEVIEW, HEADER, TOOLBAR, STATUSBAR, TRACKBAR, UPDOWN, PROGRESS, TABCONTROL, RICHEDIT, POPUPMENU, CHECKBOX, LABEL, GAUGE.
Изменим оконную функцию, описанную в предыдущем разделе, следующим образом:
function
WindowProc(hWnd: THandle; uMsg, wParam, lParam: Integer): Integer; stdcall
; export
;{
функция
окна
}
begin
Result := 0; case
uMsg of
WM_DESTROY: //
если
uMsg
равна
WM_DESTROY(
код
закрытия
окна
),
то
закрываемся
begin
halt(0); end
; WM_CREATE: begin
//
создание
кнопки
hButton := CreateWindowEx (0, 'BUTTON', //
определенный
класс
кнопки
'&Нажмите здесь', // надпись на кнопке
ws_Child or
ws_Visible or
bs_PushButton, //
стили
для
кнопки
10, 10, // координаты левого верхнего угла
200, 80, // размер
hWnd, //
handle
родителя
id_Button, // идентификатор кнопки (задается программистом)
hInstance, nil
); end
; WM_COMMAND: // проверяем, от какой кнопки пришло сообщение
if
LoWord (wParam) = id_Button then
//
если
это
событие
- click
if
HiWord (wParam) = bn_Clicked then
// код обработки нажатия кнопки
MessageBox (hWnd, 'Вы нажали на кнопку', '', MB_OK); end
; Result := DefWindowProc(hWnd, uMsg, wParam, lParam); end
;
Теперь при создании окна (при получении сообщения WM_CREATE) создается экземпляр кнопки с указанными параметрами.
Обработка сообщений, передаваемых элементам управления, реализована с помощью сообщения WM_COMMAND, приходящего родительскому окну. В этом случае, первые 2 байта wParam хранят идентификатор элемента, остальные 2 – код уведомления/события. Таким образом, для того, чтобы обработать нажатие на кнопку, нужно принять сообщение WM_COMMAND и проанализировать параметр оконной функции wParam.
2.3. Создание однострокового редактора (
Edit) средствами WinAPI
Создание строкового редактора осуществляется аналогично кнопке с помощью процедуры CreateWindow, но с использованием класса EDIT:
hEdit := CreateWindow ('EDIT', // стандартный класс
'Hello', // текст
WS_CHILD or
WS_VISIBLE or
WS_TABSTOP or
WS_BORDER, // стиль
10, 10, // положение
150, 24, // размер
hWnd, // родительское окно
id_Edit, // идентификатор данного контрола
hInstance, // как обычно - экземпляр программы
nil
) ;Чтобы созданный элемент был дочерним по отношению к главному окну – необходимо указать флаг WS_CHILD в стиле элемента. Также, для каждого элемента применимы как стандартные WS_ флаги стилей, так и свои собственные (ES_, BS_, SS_ и т.д.) которые зачастую являются специфичными только для этого типа элементов.
Для получения доступа к текстовой строке Edit’а, воспользуемся функциями GetWindowText и SetWindowText. Первая функция копирует строку в указанный буфер. В нашем случае этой строкой является содержимое edit’а. Вторая функция позволяет установить текст (если это возможно).
Дополним предыдущий пример с использованием этих функций так, чтобы при нажатии на кнопку появлялось окно с текстом edit’а. Оконная функция будет иметь следующий вид:
function
WindowProc(hWnd: THandle; uMsg, wParam, lParam: Integer): Integer; stdcall
; export
;{функция окна}
var
p^: Pointer; // буфер для считывания строки
begin
Result := 0; case
uMsg of
WM_DESTROY: halt(0); WM_CREATE: begin
// create button
hButton := CreateWindowEx (0,'BUTTON','&Click here', ws_Child or
ws_Visible or
bs_PushButton, 10, 100, 200, 80, hWnd, id_Button, hInstance, nil
); // create edit
hEdit := CreateWindow ('EDIT', 'Hello', WS_CHILD or
WS_VISIBLE or
WS_TABSTOP or WS
_BORDER, 10, 10, 250, 24, hWnd, id_Edit, hInstance, nil
) ; SetWindowText( hEdit, 'некоторый текст'); // меняем текст
end
; WM_COMMAND: if
LoWord (wParam) = id_Button then
if
HiWord (wParam) = bn_Clicked then
//
обработка
нажатия
кнопки
begin
GetMem(p, 255*SizeOf(Char)); //
подготовка
буфера
GetWindowText(hEdit, p, 255*SizeOf(Char)); //
считывание
текста
в
буфер
MessageBox (hWnd, PChar(p), 'текст в Edit', MB_OK); FreeMem(p); //
освобождение
буфера
end
; end
; Result := DefWindowProc(hWnd, uMsg, wParam, lParam); end
;
Результат работы представлен на рисунке:
2.3. Создание многострокового редактора (
Memo) средствами WinAPI
Создание многострокового редактора производится на основе уже рассмотренного элемента EDIT путем использования стиля ES_MULTILINE следующим образом:
hMemo := CreateWindow('EDIT', 'Memo1', WS_VISIBLE or
WS_CHILD or
WS_DLGFRAME or
WS_VSCROLL or
ES_MULTILINE or
ES_AUTOVSCROLL, // c
тили
для
edit-
элемента
5, 180, 250, 70, hWnd, 0, hInstance, nil
);
Модифицируем код предыдущего пункта так, чтобы текст edit’а сохранялся в Memo1 после нажатия на кнопку. Для этого изменим код обработчика нажатия на кнопку следующим образом:
var
p: PChar;… WM_COMMAND: if
LoWord (wParam) = id_Button then
if
HiWord (wParam) = bn_Clicked then
// обработка нажатия кнопки
begin
GetMem(p, 32*1024); // подготовка буфера
// получим текст из
Memo
GetWindowText(hMemo, p, 32*1024); // добавление символа конца строки Move(#13#10, PChar(Integer(p) + Length(p))^, 2); // добавление текста из Edit GetWindowText(hEdit, PChar(Integer(p + Length(p))), 32*1024); // устанавливаем новый тек
c
т в
Memo
SetWindowText(hMemo, p); // прокрутка
memo
до самого конца
while
(LOWORD(SendMessage(hMemo, EM_SCROLL, SB_PAGEDOWN,0))<> 0) do
; FreeMem(p); // освобождение буфера
end
; Результат работы представлен на рисунке:
2.4. Меню на
WinAPI
Работа с меню тоже достаточно проста и логична. Как и каждый элемент управления, меню имеет свой дескриптор (HMENU). Он позволяет обращаться и изменять свойства пункта или группы пунктов.
Основные функции, необходимые нам для работы с меню:
- CreateMenu – создаёт главное меню
- CreatePopupMenu – создание всплывающего меню
- AppendMenu – добавление какого-либо пункта в меню
- ModifyMenu – изменение свойств пункта или группы
- SetMenu – установка меню для окна
Удаление пунктов (DeleteMenu) используется редко и остается для самостоятельного изучения.
Обработка сообщений присланных меню аналогична работе с кнопками – приходит сообщение WM_COMMAND с идентификатором пункта.
const
sClassName = 'myWindow'; //
Имя
класса
окна
id_Button = 100; // id for button click
id_edit = 101; // id for edit
// ids for menu
id_action = 1; id_save = 2; id_about = 3; id_exit = 4; … WM_CREATE: begin
// create button
hButton := CreateWindowEx (0,'BUTTON','&Click here', ws_Child or
ws_Visible or
bs_PushButton, 10, 100, 200, 80, hWnd, id_Button, hInstance, nil
); // create edit
hEdit := CreateWindow ('EDIT', 'Hello', WS_CHILD or
WS_VISIBLE or
WS_TABSTOP or
ES_MULTILINE or
ws_border, 10, 10, 250, 24, hWnd, id_Edit, hInstance, nil
) ; SetWindowText( hEdit, 'некоторый текст'); // create memo
hMemo := CreateWindow('EDIT', 'Memo1', WS_VISIBLE or
WS_CHILD or
WS_DLGFRAME or
WS_VSCROLL or
ES_MULTILINE or
ES_AUTOVSCROLL, 5, 180, 250, 70, hWnd, 0, hInstance, nil
); //
создание
меню
m_main := CreateMenu; m_file := CreatePopupMenu; // MF_STRING - обычная строка
// MF_SEPARATOR - разделительная полоса в меню
// MF_POPUP - добавляет всплывающее меню
// заполняем всплывающее меню
AppendMenu(m_file, MF_STRING, id_save, 'сохранить как'); // сохранить содержимое memo
AppendMenu(m_file, MF_SEPARATOR, 0, nil
); //
разделитель
AppendMenu(m_file, MF_STRING, id_exit, 'выход'); // выход из программы
// заполняем главное меню
AppendMenu(m_main, MF_POPUP, m_file, 'файл'); // "цепляем" всплывающее меню
AppendMenu(m_main, MF_STRING, id_about, 'о программе'); // добавляем пункт
// выравниваем пункт меню "о программе" по правому краю
ModifyMenu(m_main, 1, MF_BYPOSITION or
MF_HELP, id_about, 'о программе'); // назначаем главное меню программы
SetMenu(hWnd, m_main); end
;
Для задания реакции на выбор конкретного пункта меню обрабатываем сообщение WM_COMMAND, как и в случае кнопки, например, следующим образом:
WM_COMMAND: case
LoWord (wParam) of
id_Button: … id_about: //
окошко
о
программе
MessageBox(hWnd, 'программа на чистом WinAPI', 'о программе', MB_ICONINFORMATION); id_exit: // отправляем сами себе сообщение о закрытии окна
PostMessage( hWnd, WM_CLOSE, 0, 0 ); end
;
Полученное меню представлено на рисунке:
2.5. Стандартные диалоговые окна
Стандартные компоненты OpenDialog, SaveDialog и аналогичные также используют чистый WinAPI код. Но необходимые для их создания функции находятся в библиотеке “commdlg32.dll”. Следовательно, для того, чтобы их использовать, необходимо подключить модуль CommDlg.pas к нашему проекту (добавить его в секцию uses).
Для создания и работы с диалогами существует множество функций, но нам понадобится только одна: GetSaveFileName. Эта функция открывает стандартное окно выбора файла для сохранения, схожее, как две капли воды, с окном, открываемым TSaveDialog’ом. Результат функции при неудачном выборе файла (нажатие кнопки отмены или другие напасти) равен нулю.
Для её использования нам необходимо описать структуру TOpenFilename, характеризующую окно диалога (фильтры, максимальная длина имени файла, выбор нескольких файлов и другие флаги).
Изменим код предыдущего примера так, чтобы при выборе пункта меню “Сохранить”, открывалось стандартное диалоговое окно сохранения для выбора файла. Для этого добавим следующий код в область обработки сообщения WM_COMMAND:
…
id_exit: // отправляем сами себе сообщение о закрытии окна
PostMessage( hWnd, WM_CLOSE, 0, 0 ); id_save: begin
//
заполняем
структуру
T
SaveDialog
ZeroMemory(@SaveDialog, SizeOf(SaveDialog)); with
SaveDialog do begin
lStructSize := SizeOf(SaveDialog); hWndOwner := hWnd; lpstrFilter := 'Текстовые документы (*.txt)'#0'*.txt'#0#0; lpstrDefExt := 'txt'; // папка должна существовать и файл доступен для записи
Flags := OFN_PATHMUSTEXIST or
OFN_HIDEREADONLY; // максимальная длина имени файла
nMaxFile := 250; nMaxFileTitle := nMaxFile; GetMem(lpstrFile, nMaxFile); //
подготовка
буфера
для
имени
файла
ZeroMemory(lpstrFile, nMaxFile); end
; if
GetSaveFileName(SaveDialog) then begin
// пользователь указал имя файла
GetMem(p, 32*1024); GetWindowText(hMemo, p, 32*1024); AssignFile(F, SaveDialog.lpstrFile); Rewrite(F, 1); BlockWrite(F, p^, Length(p)); CloseFile(F); FreeMem(p); end
; FreeMem(SaveDialog.lpstrFile); end
;
Проектное задание
Модифицировать программу, полученную в конце модуля, так, чтобы при изменении размеров окна элементы управления меняли свои позиции и размеры. Добавить возможность загрузки текстового файла.
Указание:
обработать сообщение
WM
_
RESIZE
, изменять размеры компонентов функцией
SetWindowRect
.
Тест рубежного контроля №2
Тест содержит 5 заданий, на выполнение которых отводится 3 минуты. Выберите наиболее правильный, по Вашему мнению, вариант ответа и отметьте его любым значком в бланке ответов.
1. Любая программа под Windows, имеющая окна и интерфейс пользователя должна содержать |
|||
1) |
Цикл обработки сообщений |
2) |
Точку входа |
3) |
Описание класса окна |
4) |
Все вышеперечисленное |
2. Оконная функция позволяет |
|||
1) |
Закрыть приложение |
2) |
Управлять операционной системой |
3) |
Задавать особую реакцию приложения на некоторые сообщения |
4) |
Необходима для поддержания цикла сообщений |
3. Для создания кнопки необходимо создать окно функцией CreateWindowEx c классом: |
|||
1) |
TButton |
2) |
Button |
3) |
Заранее зарегистрированным классом с помощью RegisterClass |
4) |
BitBtn |
4. Создание однострочного и многострочного редактора отличаются |
|||
1) |
Набором констант, определяющих стили |
2) |
Классом, указываемом при создании |
3) |
Всем вышеперечисленным |
4) |
Не отличаются – меняются лишь размеры |
5. Функция CreatePopupMenu позволила в одном из приведенных выше примеров |
|||
1) |
Создать иерархию вложенных меню |
2) |
Функция не использовалась |
3) |
Создать всплывающее меню |
4) |
Создать главное меню программы |
Бланк ответов
№ |
1 |
2 |
3 |
4 |
1) |
||||
2) |
||||
3) |
||||
4) |
||||
5) |
Критерии оценки
4 и более правильных ответов – отлично
3 правильных ответа – хорошо
2 правильных ответа – удовлетворительно
1 и менее правильных ответов - неудовлетворительно
Модуль 3. Управление сторонними приложениями
Комплексная цель:
научиться получать сведения об окна (в частности, о его классе), собирать данные о сообщениях.
Краткое изложение программного материала:
в модуле описана программа WinSight32, которая позволяет:
· Собирать данные об окнах в операционной системе
· Следить за сообщениями
Во второй части модуля приведены приемы работы со сторонними приложениями: манипуляции с его окнами (изменение заголовков, положения и т.д.) и управляющими элементами (нажатие кнопки, ввод информацию в редакторы, активация пунктов меню).
3.1. Утилита слежения за окнами и сообщениями
WinSight32
Программа WinSight32 предоставляет отладочную информацию об оконных классах, окнах и сообщениях. Используя WinSight32, можно изучить, как любое приложение создает классы и окна, а также следить за получаемыми и передаваемыми сообщениями. Эта программа оказывается крайне полезной для того, чтобы определит класс и заголовок окон сторонних приложений, а также проследить за сообщениями, приходящими к ним. Знание класса окна необходимо для успешного и стабильного получения дескриптора окна (см. раздел 3.2), знание которого позволяет выполнять множество операций с окном. Программа WinSight является пассивным наблюдателем: она перехватывает и отображает информацию о сообщениях, но не препятствует сообщениям доходить до приложений.
Эта утилита входит в стандартную поставку Delphi, как правило, ее можно запустить из меню Пуск, физически же, она располагается в папке bin установки Delphi.
WinSight имеет два режима просмотра, которые управляются с помощью пункта главного меню View:
· Window Tree view (вид дерева окон)
· Message Trace view (вид отслеживания сообщений)
При первом запуске программа функционирует в первом режиме. Вид дерева окон отображает иерархию окон на рабочем столе (рабочий стол также является окном, причем его дескриптор всегда равен нулю).
Этот вид позволяет:
· Определять, какие окна существуют в данный момент времени
· Наблюдать за статусом окон, включая скрытые окна
· Узнать, какие окна получают сообщения
· Выбрать окна, за сообщениями которых нужно проследить
Данные о каждом окне отображаются в следующем формате:
Дерево Дескриптор {Класс} Модуль Положение “Заголовок”
Возможно получить более подробную информацию об окне, выполнив одно из следующих действий,
· Дважды щелкнуть по строке списка с интересующим окном
· Выделить окно в списке и нажать ввод (enter)
· Выделить окно и выбрать пункт меню Spy->Open Detail
в результате которых появится окно с подробной информацией о выбранном окне, например, такое:
Для поиска окон предусмотрено два режима поиска: Find Window и Follow focus, которые запускаются из пункта меню Spy.
В режиме поиска окна (Find Window) нужно использовать кнопки вверх и вниз клавиатуры для выделения окна. Выберите окно в дереве окон, затем используйте кнопки со стрелочкам на клавиатуре для продвижения по дереву. Выделяемые окна в дереве окон будут помечены рамкой на экране для идентификации.
Для выхода из режима поиска окон нажмите любую кнопку мышки или клавишу Escape.
Режим следования за фокусом (Follow Focus) позволяет обнаруживать окна более простым способом – достаточно щелкнуть по интересующему окну и WinSight выделит это окно среди дерева окон.
Режим следования за фокусом остается включенным до тех пор, пока не будет явно выключен в меню Spy.
Для слежения за сообщениями необходимо указать, за какими окнами нужно следить в меню Messages – за всеми (All Windows) либо только за выделенными (Selected Windows). В этом меню также можно указать фильтр для сообщений (options) и процессов (process).
После нажатия на кнопку Start! (Пуск!) главного меню, программа начнет собирать сообщения, отображая их в соответствующем виде, включаемом в меню View. Сбор останавливается кнопкой Stop.
Сообщения, отправленные командой SendMessage, отображаются дважды: при отправке и при возвращении обратно.
Обработанные оконной функцией сообщения отображаются лишь один раз, так как их возвращаемые значения бессмысленны. Окно WinSight может быть разделено для того, чтобы отображать одновременно и дерево окон, и список сообщений.
Сообщения в окне Message View отображаются в следующем виде:
Дескриптор “Заголовок”-или-{Класс} Сообщение Статус
WinSight32 также отображает значки + и * перед сообщением для того, чтобы указать на возможность пропуска сообщений.
Близким аналогом рассмотренной программы является утилита Spy++, идущая в комплекте с Microsoft Visual Studio, в то время как WinSight идет в комплекте поставки сред программирования фирмы Borland.
Для управления сторонними приложениями (следующий раздел этой части) необходимо уметь определять заголовки и классы окон, контроль над которыми необходимо получить.
3.2. Приемы управления сторонними приложениями
Очень часто возникают ситуации, когда необходимо заставить некоторое стороннее приложение, исходный код которого недоступен и не экспортирует собственного API, выполнять некоторые действия. Одним из примеров является нажатие 500 раз на кнопку “Отмена” при установки ПО с некачественного диска. В другом примере, необходимо провести рутинную операцию по сглаживанию кривой, которая включает в себя команд – загрузить файл, построить график, выполнить сглаживание, экспортировать результат… Гораздо удобнее один раз нажать на кнопку и проделать эти операции автоматически. В этом разделе будет рассказано, каким образом можно подавать команды на различные управляющие элементы приложений.
В качестве примера, рассмотрим управление приложением, написанным на чистом API в предыдущей части “Создание …”.
Начать необходимо с того, чтобы найти дескриптор интересующего окна, что можно сделать с помощью функции FindWindow, которое находит первое попавшееся родительское окно верхнего уровня с совпадающими ClassName и WindowName. Не осуществляет поиск дочерних окон. Синтаксис этой функции в Делфи:
function
FindWindow(ClassName, WindowName: PChar): HWnd; где ClassName - имя класса окна (заканчивающееся пустым символом, nil
- если все классы), WindowName - текстовый заголовок окна или nil
, если все окна.
Функция возвращает 0, если окно не найдено, либо, в случае успеха, дескриптор окна.
Таким образом, для получения дескриптора необходимо указать имя класса окна и его заголовок. Если они неизвестны, то можно было бы запустить процедуру поиска с использованием функции EnumWindows, которая будет рассмотрена позже.
В нашем случае, зная заголовок и класс окна, можно обойтись следующим кодом:
program
program_1;{$APPTYPE CONSOLE}
uses
Windows, SysUtils;const
sClassName = 'myWindow';var
handle: HWND;begin
handle := FindWindow(sClassName, nil
); if
handle = 0 then
Writeln('The window not found!') else
Writeln('*> handle = ', handle); writeln('press enter'); readln;end
.
В данном случае нет необходимости указывать заголовок окна – оно однозначно определяется классом.
Приведем для справки функции, полезные при работе с окнами верхнего уровня:
Функция |
Описание |
Параметры |
Возвращаемое значение |
GetClassName |
Считывает имя класса окна. |
Wnd: Идентификатор окна. ClassName: Буфеp для приема имени класса. MaxCount: Размер буфера |
Фактическое число скопированных символов; 0 - если ошибка |
GetWindowText |
Копиpует в Str заголовок окна или текст органа управления |
Wnd: Идентификатор окна или органа управления. Str: Буфер, принимающий строку. MaxCount: Размер буфера Str. |
Фактическое число скопированных байт или 0, если текст отсутствует |
GetWindowTextLength |
Считывает длину заголовка окна или текста органа управления. |
Wnd: Идентификатор окна |
Длина заголовка окна в символах |
IsIconic |
Определяет, является ли окно пиктограммой (минимизированным). |
Wnd: Идентификатор окна |
Не нуль, если минимизировано; 0 - если нет |
IsWindow |
Определяет, является ли окно допустимым существующим окном. |
Wnd: Идентификатор окна |
Не нуль, если окно достоверно; 0 - если нет. |
IsWindowEnabled |
Определяет, является ли окно разрешенным для ввода с мыши и с клавиатуры |
Wnd: Идентификатор окна |
Не нуль, если окно разрешено; 0 - если нет. |
IsWindowVisible |
Определяет, сделано ли окно видимым функцией ShowWindow. |
Wnd: Идентификатор окна |
Не нуль, если окно существует на экране (даже если полностью закрыто); 0 - если нет |
IsZoomed |
Определяет, является ли окно максимизированным. |
Wnd: Идентификатор окна |
Не нуль, если окно максимизировано; 0 - если нет |
GetWindowRect |
Считывает в ARect размерности ограничивающего прямоугольника окна (в координатах экрана). |
Wnd: Идентификатор окна. Rect: Пpинимающая стpуктуpа TRect. |
Не используется |
GetClientRect |
Функция возвращает размер рабочей области окна (уже без заголовка, меню и т.д.) в глобальных экранных координатах |
Wnd: Идентификатор окна. Rect: Структура TRect для приема координат пользователя. |
Не используется |
GetWindowLong |
Считывает информацию об окне или о значениях дополнительного байта окна |
Wnd: Идентификатоp окна. Index: Смещение в байтах или одна из следующих констант: GWL_EXSTYLE GWL_STYLE GWL_WNDPROC GWL_HINSTANCE GWL_HWNDPARENT GWL_ID GWL_USERDATA |
Информация, характерная для окна |
GetClassLong |
Считывает из структуры окна TWndClass со смещением Index длинное значение. Положительные смещения в байтах (с нуля) используются для доступа к дополнительным байтам класса. |
Wnd: Идентификатор окна. Index: Смещение в байтах или константа … GCL_WNDPROC возвращает адрес стандартной процедуры окна |
Считанное значение |
GetWindowThreadProcessId |
Возвращает идентификатор процесса к которому принадлежит данное окно. Как правило, одинаков для всех окон приложения. |
Wnd: Идентификатор окна lpdwProcessId : 32битное значение идентификатора процесса |
Идентификатор потока |
Переберем теперь все окна системы и выведем их заголовки и классы в консольное окно. Для этого, заведем следующую вспомогательную функцию:
function
EnumProc(h: HWND; param: longint): boolean; stdcall
; var
wnd_class, wnd_name: pchar; // буферы для класса и заголовка окна
begin
GetMem(wnd_name, 256); //
резервируем
память
GetMem(wnd_class, 256); GetWindowText(h, wnd_name, 256); GetClassName(h, wnd_class, 256); writeln(k,') ',h,' "',wnd_name,'" {',wnd_class,'}'); inc(k); //
k
– глобальная переменная-счетчик
result := true; FreeMem(wnd_name); //
освобождаем
память
FreeMem(wnd_class); result := true; //
продолжаем
перебирать
окна
end
;
адрес которой будет передан API функции EnumWindows:
begin
... k := 1; EnumWindows( @EnumProc, 0 ); ...end
.
Результат работы консольного приложения (отрывок):
221) 66248 "SysFader" {SysFader}
222) 66106 "┬ёяы√тр■∙хх ьхэ■ ёхЄш" {ATL:701331A0}
223) 65860 "" {SynTrackCursorWindowClass}
224) 65812 "" {SynTrackCursorWindowClass}
225) 65788 "CL RC Engine3 Dummy Winidow" {CL RC Engine3 Dummy Winidow}
226) 65770 "" {tooltips_class32}
227) 131134 "CiceroUIWndFrame" {CiceroUIWndFrame}
228) 196642 "TF_FloatingLangBar_WndTitle" {CiceroUIWndFrame}
229) 66212 "" {tooltips_class32}
230) 131408 "╤яшёюъ с√ёЄЁющ тёЄртъш" {#32770}
231) 590728 "" {ComboLBox}
232) 393944 "" {WMDMNotificationWindowClass}
233) 131900 "DDE Server Window" {OleDdeWndClass}
234) 525194 "" {WMPMessenger}
Практически любые манипуляции с окнами и элементами управления выполняются путем отправки сообщений с помощью функции SendMessage и PostMessage. Отличие между этими функциями заключается в том, что функция SendMessage ожидает, пока адресат не обработает сообщение, в то время как PostMessage просто добавляет сообщение в очередь.
LRESULT SendMessage/PostMessage(
HWND hWnd, //
дескриптор
окна
-адресата
UINT Msg, // посылаемое сообщение
WPARAM wParam, // первый параметр сообщения
LPARAM lParam // второй параметр сообщения
);
Например, рассмотренная выше функция GetWindowText эквивалентна посылке сообщения WM_GETTEXT.
Довольно часто используется сообщение WM_SYSCOMMAND, которое, позволяет выполнять различные операции в зависимости от значения параметра wParam:
· SC_CLOSE Закрывает окно.
· SC_CONTEXTHELP Изменяет курсор на вопросительный знак.
· SC_DEFAULT Выбирает элемент по умолчанию; эмулирует двойное нажатие на Системное меню.
· SC_HOTKEY Инициирует окно, связанное с текущим - указанной комбинацией горячих клавиш.
· SC_HSCROLL Прокручивается горизонтально окно.
· SC_KEYMENU Открывает Системное меню как результат нажатия клавиши.
· SC_MAXIMIZE (или SC_ZOOM) Разворачивает окно.
· SC_MINIMIZE (или SC_ICON) Сворачивает окно.
· SC_MONITORPOWER Устанавливает состояние дисплея.
· SC_MOUSEMENU Открывает Системное меню как результат щелчка мыши.
· SC_MOVE Перемещает окно.
· SC_NEXTWINDOW Переходит к следующему окну.
· SC_PREVWINDOW переходит к предыдущему окну.
· SC_RESTORE Восстанавливает окно к его нормальной позиции и размеру.
· SC_SCREENSAVE Запускает стандартный хранитель экрана.
· SC_SIZE Задает размеры окно.
· SC_TASKLIST Выполняет или инициирует Windows Task Manager.
· SC_VSCROLL Прокручивается окно вертикально.
Например, для того чтобы свернуть окно достаточно написать:
SendMessage(handle, WM_SYSCOMMAND, SC_MINIMIZE, nil
);
Можно также показать или скрыть окно, используя функцию API ShowWindow.
Нажмем теперь на единственную кнопку, которая имеется на тестовом приложении. Для этого необходимо знать дескриптор кнопки, поэтому пройдем по всем элементам управления также, как это делали с окнами верхнего уровня, а именно, с помощью функции EnumChildWindows:
EnumChildWindows(handle, @EnumProc, 0);
где вспомогательная функция EnumProc осталась неизменной. Функция EnumChildWindows перечисляет не только элементы верхнего уровня, но и вложенные друг в друга (например, кнопки на панели). В результате применения этой функции к нашему приложению получим список дочерних элементов:
1) 918740 "&Click here" {Button}
2) 853116 "Hello" {Edit}
3) 1180824 "Memo1" {Edit}
В нашем случае, когда заранее известен класс и заголовок элемента, его дескриптор можно получить более простым путем, а именно, с помощью функции FindWindowEx. Эта функция аналогична уже рассмотренной функции FindWindow, предназначенной для работы с дочерними окнами. Синтаксис этой функции:
function
FindWindowEx(hndParent, hndChild :HWnd; ClassName, WindowName :LpctStr): HWnd;
где HndParent - дескриптор родительского окна верхнего уровня, HndChild - дескриптор дочернего окна с которого начинается поиск, ClassName - имя класса окна (заканчивающееся пустым символом, nil
- если все классы), WindowName - текстовый заголовок окна или nil
, если все окна. Функция возвращает дескриптор найденного окна либо нуль, если окно не найдено.
Получим дескриптор кнопки:
h_butt := FindWindowEx( handle, 0, 'BUTTON', '&Click here'); if
h_butt = 0 then
Writeln('The button not found!') else
Writeln(' handle of the button is ', h_butt);
Для эмуляции нажатия на кнопку нам необходимо знать ее идентификатор (иногда его называют акселератором), значение которого можно получить по дескриптору кнопки с помощью функции GetDlgCtrlID:
var
id_butt: integer;
…
id_butt := GetDlgCtrlID(h_butt);
И убедимся, что этот идентификатор совпадает с заданным нами в предыдущей части (id_button=100). Согласно справки MSDN по сообщению BN_CLICKED, нижнее слово параметра wParam, который имеет тип LongWord или двойного слова (одно слово – 16 бит), должно содержать идентификатор, а верхнее – номер сообщения BN_CLICKED. Параметр lParam содержит дескриптор элемента управления – кнопки. То есть, необходимо отправить следующее сообщение:
SendMessage(handle, WM_COMMAND, MakeWParam(id_butt, BN_CLICKED), h_butt);
где функция MakeWParam собирает двойное слово из двух слов.
Для того, чтобы записать текст в строковой редактор можно использовать сообщение WM_SETTEXT. Участок кода, выполняющий поиск edit’а и меняющий его текст выглядит следующим образом:
var
pCh: pChar; ... h_edit := FindWindowEx( handle, 0, 'EDIT', 'Hello'); if
h_edit = 0 then
Writeln('The edit not found!') else begin
Writeln(' handle of the edit is ', h_edit); pCh := 'текст, вставленный из другой программы'; SendMessage(h_edit, WM_SETTEXT, 0, integer(pCh)); end
;
Научимся теперь выбирать нужные пункты меню. Нажмем, например, на пункт меню выход. Это третий под пункт (разделители тоже считаются!) первого меню, то есть код будет иметь следующий вид:
h_menu := GetMenu(Handle); //получили дескриптор главного меню окна.
if
h_menu <> 0 then begin
h_menu := GetSubMenu(h_menu,0);//получили дескриптор первого пункта главного меню (0 -первый пункт)
//получим идентификатор 3 пункта подменю меню (черты в меню - это также пункты)
id_menu := GetMenuItemID(h_menu, 2); if
id_menu <> 0 then
//
запускаем
пункт
меню
.
Именно
PostMessage, SendMessage -
не
работает
.
PostMessage(handle, WM_COMMAND, id_menu, 0); end
;
В результате, программа должна закрыться, так как выбранный нами пункт меню – выход.
В случае, если функции не работают по не ясной причине, можно получить код ошибки с помощью функции GetLastError. Приведем ниже программный код, выдающий сообщение об ошибке:
// следующая функция является макросом, не переведенным с С++ в поставке
Delphi
function
MAKELANGID(sPrimaryLanguage : Word; sSubLanguage : Word) : Word; begin
result := (sSubLanguage shl
10) or
sPrimaryLanguage; end
; var
pCh: pChar; // буфер для сообщения об ошибке
…// при возникновении ошибки ее код нужно преобразовать в понятный пользователю текст с помощью функции
FormatMessage
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER OR
FORMAT_MESSAGE_FROM_SYSTEM, nil
, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) {Default language}
, pCh, 0, nil
);//
показываем
ошибку
пользователю
:
MessageBox( 0, pCh, 'Last error', MB_OK or
MB_ICONINFORMATION );
Проектное задание
Написать программу, которая будет пытаться двигать главное окно компилятора Delphi, вызывать пункты меню сохранить (Save all) и запустить (Run). При вызове пункте меню Save as… программно заставить сохранить по указанному заранее пути. Попробуйте создать свою кнопку в чужом приложении.
Тест рубежного контроля №3
Тест содержит 5 заданий, на выполнение которых отводится 3 минуты. Выберите наиболее правильный, по Вашему мнению, вариант ответа и отметьте его любым значком в бланке ответов.
1. Утилита WinSight позволяет |
|||
1) |
Следить за окнами и сообщения, приходящим к ним |
2) |
Следить за окнами и изменять их свойства |
3) |
Изменять сообщения, приходящие окнам и менять класс окон |
4) |
Только определить класс и заголовок окна |
2. Режим поиска Follow focus позволяет |
|||
1) |
Искать окна |
2) |
Отслеживать сообщения |
3) |
Искать окна и отслеживать нужные сообщения |
4) |
Не используется в 32 разрядных операционных системах |
3. Для управления окном необходимо получить его |
|||
1) |
Экранные координаты (Rect) |
2) |
Графический дескриптор (HDC) |
3) |
Оконный дескриптор (HWND) |
4) |
Класс окна, зарегистрированный в системе |
4. Для программного нажатия на кнопку необходимо знать |
|||
1) |
Дескриптор кнопки |
2) |
Акселератор (id) кнопки |
3) |
Дескриптор и акселератор |
4) |
Дескриптор кнопки, ее акселератор и дескриптор окна |
5. Перебор всех главных окон в системе рекомендуется осуществлять функциями: |
|||
1) |
EnumWindows |
2) |
FindWindow, GetNextWindow |
3) |
EnumChildWindow |
4) |
FindWindowEx |
Бланк ответов
№ |
1 |
2 |
3 |
4 |
1) |
||||
2) |
||||
3) |
||||
4) |
||||
5) |
Критерии оценки
4 и более правильных ответов – отлично
3 правильных ответа – хорошо
2 правильных ответа – удовлетворительно
1 и менее правильных ответов - неудовлетворительно
Список литературы
1. MSDN, July 2006
2. Материалы форума сайта “Мастера Делфи” http://www.delphimaster.ru/
3. Материалы форума сайта “Королевсво Делфи” http://www.delphikingdom.com/
4. В.В. Фаронов. Delphi 4. Учебный курс. 1999.