Работаем с окнами и объектами. Delphi. Api. Часть 1
В этом наброске хочу углубиться в изъезженную тему про работу с формами и объектами на них.
Чтобы что-то сделать с объектом, нам нужно получить его handle — уникальный идентификатор.
Ты наверняка уже сталкивался с api функциями, предназначенными для этих целей. Перечислим их: FindWindow, FindWindowEx, GetWindow. Это далеко не все, но нам их хватит, оговорюсь, что я с GetWindow полностью обхожусь, его мы рассмотрим чуток позднее.
[FindWindow]
синтаксис:
function FindWindow(lpClassName, lpWindowName: PChar): HWND;
HWND это тот же integer, то есть функция возвращает число, handle объекта. Функция работает только для форм, то есть объектов верхнего уровня.
параметры:
lpClassName — класс объекта;
lpWindowName — заголовок, в данном случае формы.
Хочу обратить внимание, что функция находит объект, только если:
переданные класс и заголовок полностью соответствуют классу и заголовку объекта,
заголовок соответствует, а переданный класс nil,
заголовок передан nil, а класс соответствует,
передано nil и nil.
В последнем случае функция возвращает первое по счету окно. Это я к тому, что если FindWindow(nil, ‘wnd.txt — Блокнот’) нам вернет handle окна блокнота, то FindWindow(nil, ‘Блокнот’) или FindWindow(nil, ‘wnd.txt’) нам вернет 0, мы должны знать точные данные об окне. Реализовывать поиск по части заголовка мы будем позже.
[FindWindowEx]
синтаксис:
function FindWindowEx(Parent, Child: HWND; ClassName, WindowName: PChar): HWND;
Здесь у нас всё аналогично, но появляется пара новых параметров:
Parent — handle родительского объекта;
Child — handle дочернего объекта.
Как parent, так и Child имеют право быть нулями.
[пример]
Долго думал, что привести в качестве примера, рассмотрим изъезженное до дыр получение данных из формы логина qip. Как мы знаем (а если не знаем — используем Spy++), форма логина имеет класс TManForm — достаточно редко найдешь форму с таким классом, заголовок нам уже не интересен. На форме стоит TGroupBox, в одном экземпляре, в котором мы находим TComboBox с «ICQ#/Email/SN». Вычислим handle этого объекта:
Если наша функция чего-то не находит, возвращается 0.
[GetWindow]
Эта вещь будет помощней FindWindow и FindWindowEx, синтаксис:
function GetWindow(hWnd: HWND; uCmd: UINT): HWND;
Не похоже на FindWindow, не так ли? Рассмотрим что есть что:
hWnd — handle некоего объекта;
uCmd — команда, действие, указывает как получить новый handle.
Константы для uCmd начинаются с префикса GW_ (от GetWindow), рассмотрим ближе:
GW_CHILD — дочерний объект, первый по счету (их может быть много);
GW_OWNER — по идее должно возвращать handle окна-владельца. Учитываем, что окно-родитель и окно-владелец могут быть разными, это тема для отдельного разговора, оговорюсь лишь, что для определения родителя можно использовать GetParent;
GW_HWNDFIRST, GW_HWNDLAST, GW_HWNDNEXT, GW_HWNDPREV — константы для получения первого, последнего, следующего или предыдущего объекта на этом уровне, то есть когда одинаковый родитель (или его нет).
GW_MAX — честно говоря, не очень понимаю, будет время — разберусь и напишу об этом)
Тут нужно обратить внимание на то, что ни с заголовком, ни с классом объекта функция не работает, нам надо проверять их вручную.
[узнаём заголовок или класс объекта]
Приведу сразу готовые функции, которые использую сам:
это для заголовка.
Что касается класса:
На этом пока остановлюсь, в следующих частях жди примеры работы с GetWindow и функции поиска окон, в том числе рекурсивные, основанные на нём. Также, может быть, рассмотрю работу и с меню.
Подпишись на rss и следующие части не пройдут мимо)
А в качестве постового сегодня ссылка на случай, если Вам нужно создать резюме в Ростове-на-Дону.
Handle delphi что такое
Здравствуйте, _silent, Вы писали:
_>Люди, может, подскажете тупому программеру — что такое HANDLE?
_>Уже и встречался и даже работал. Но что это такое, так и не знаю. Странно, да? Вот и я так думаю. Объясните мне, пожалуйста.
идентификатор объекта
Здравствуйте, _silent, Вы писали:
_>Люди, может, подскажете тупому программеру — что такое HANDLE?
_>Уже и встречался и даже работал. Но что это такое, так и не знаю. Странно, да? Вот и я так думаю. Объясните мне, пожалуйста.
1) рукоять, рукоятка, черенок, ручка
the handle of wife — рукоятка жены (скалка)
starting handle — кнопка «Старт» в Windows
3) оснащать (что-л.) ручкой, рукоятью; приделывать рукоять (к чему-л.)
to handle — приделать ноги.
4) управлять чем-л., справляться с чем-л. руками
she handled the boyfriend very easily — она с легкостью справлялась с товарищем
handle a wife — управлять женой (фантаст.)
5) хорошо управляться; слушаться (рук, руля и т. п.)
the wife handles well — жена идет/работает очень послушно (фантаст.)
6) обходиться, обращаться; управляться, справляться с кем-л., чем-л.
a wife handles all my affairs — жена занимается всеми моими делами (фантаст.)
handle with care! — осторожно! (надпись на свадебных платьях)
| | От: | mrhru |
| Дата: | 03.12.03 12:26 | |
| Оценка: | 6 (1) | |
Здравствуйте, _silent, Вы писали:
_>Люди, может, подскажете тупому программеру — что такое HANDLE?
_>Уже и встречался и даже работал. Но что это такое, так и не знаю. Странно, да? Вот и я так думаю. Объясните мне, пожалуйста.
Handle — идентификатор-указатель объекта. Термин используется в основном в процедурном программировании. Т.е. об объекте неизвестно ничего, а работа с ним осуществляется через методы, в которые этот handle и передаётся. Например, WinAPI. Или в CLX от Borland — используется библиотека Qt, которая возвращает указатели на объекты просто как целые.
| | От: | Dmitry V.Abramov | http://imap.ru |
| Дата: | 03.12.03 15:03 | ||
| Оценка: | 10 (2) | ||
_>Люди, может, подскажете тупому программеру — что такое HANDLE?
_>Уже и встречался и даже работал. Но что это такое, так и не знаю. Странно, да? Вот и я так думаю. Объясните мне, пожалуйста.
Воспринимай его как универсальную ссылку (pointer) на некую структуру. Что это за структура, создана ли отдельно или является частью другой, — ты не знаешь. Но об этом прекрасно осведомлена та функция WinAPI, которая тебе его предоставила или которой ты его передашь.
При такой аналогии становится понятно, почему хэндлы нужно получать и освобождать. А уж API само разберется — заводить под него память или дать просто адрес, освобождать память при CloseHandle или ну его.
По аналогии — можно воспринимать классы как хэндлы, структура которых тебе известна.
Встречается еще аналогия, что HANDLE — это пароль, который тебе дают в одном месте, чтобы ты его произнес в другом и тебя пустили. Но это не объясняет, почему пароль нужно освобождать (CloseHandle).
| | От: | Diouzshev |
| Дата: | 03.12.03 16:46 | |
| Оценка: | 6 (1) | |
Hello, _silent!
You wrote on Wed, 03 Dec 2003 11:44:16 GMT:
s> Люди, может, подскажете тупому программеру — что такое HANDLE?
s> Уже и встречался и даже работал. Но что это такое, так и не знаю.
s> Странно, да? Вот и я так думаю. Объясните мне, пожалуйста.
И я свою лепту внесу
With best regards, Alexander Diouzshev-Maltsev.
| | От: | Аноним |
| Дата: | 03.12.03 17:01 | |
| Оценка: | 9 (1) | |
Попробую объяснить на пальцах.
На эпоху структурного программирования,
когда проектировалось большинство ОС,
вместо объектов и методов
использовались структуры и функции для работы с ними.
Такой подход характеризуется полным отсутствием инкапсуляции, т.е.
каждый может получить полный доступ к полям структуры безо
всяких функций и сделать её состояние внутренне противоречивым.
Простейший способ решения этой проблемы —
просто скрыть от пользователя декларацию структуры:
Здесь хэндл — просто указатель.
Так, например, устроен HINSTANCE(HMODULE).
Другая возможная реализация Хэндла — через
индекс в некоторой скрытой от пользователя таблице
(которая вообще говоря может лежать в другом адресном пространстве).
Пример такой реализации:
Структура хэндла может быть составной — включать в себя
различные флаги и т.п.
Собственно, для реализация хэндла важно лишь уметь каким-то
образом получать по нему доступ к самому объект.
| | От: | Slicer [Mirkwood] | https://ru.linkedin.com/in/maksim-gumerov-039a701b |
| Дата: | 03.12.03 17:58 | ||
| Оценка: | 6 (1) | ||
Здравствуйте, Dmitry V.Abramov, Вы писали:
_>>Люди, может, подскажете тупому программеру — что такое HANDLE?
_>>Уже и встречался и даже работал. Но что это такое, так и не знаю. Странно, да? Вот и я так думаю. Объясните мне, пожалуйста.
DVA>Воспринимай его как универсальную ссылку (pointer) на некую структуру. Что это за структура, создана ли отдельно или является частью другой, — ты не знаешь. Но об этом прекрасно осведомлена та функция WinAPI, которая тебе его предоставила или которой ты его передашь.
Ты бы людей не путал словами pointer и ссылка — с непривычки еще поймут неправильно. Я бы сказал так: хендл, хотя официально это не регламентировано 
DVA>При такой аналогии становится понятно, почему хэндлы нужно получать и освобождать.
Надо ли освобождать — зависит от сущности, чей идентификатор нас интересует. Если мы всего лишь пользуемся сущностью, созданной кем-то другим, то часто ничего освобождать не надо. Если же сущность находится в нашем владении (т.е. грубо говоря, мы ее создали, например, путем вызова ф-ии ОС, которая вернула хендл). Бывает, что владельцем сущности является ОС — тогда освобождать таки надо, но для того, чтобы ОС уменьшила счетчик ссылок на сущность и при достижении 0 удалила эту больше ненужную сущность (разумеется, некоторые сущности могут в из соображений оптимизации никогда не удаляться, если они используются часто — даже если счетчик достиг 0, можно предположить, что сущность «популярна» и ее скоро снова кто-то захочет поюзать).
| | От: | Kluge |
| Дата: | 03.12.03 23:45 | |
| Оценка: | 3 (1) | |
В w2k Handle это смешение относительно начала таблицы Handle-ов процесса, в указанном месте начинается описание объекта ядра. Т.к. объект ядра может лежать и в памяти процесса и в памяти ядра давать на него указатель не разумно
Здравствуйте, Slicer [Mirkwood], Вы писали:
SM>Ты бы людей не путал словами pointer и ссылка — с непривычки еще поймут неправильно. Я бы сказал так: хендл, хотя официально это не регламентировано 
Вот. Сразу все стало значительно понятней.
ЗЫ А кто такая сущность?
| | От: | Leonid Troyanovsky |
| Дата: | 04.12.03 07:20 | |
| Оценка: |
Здравствуйте, All, мы писали:
А>Здравствуйте, Slicer [Mirkwood], Вы писали:
Sorry, это опять я забыл зарегистрироваться
Delphi: What is Application.Handle?
The Delphi help says:
Provides access to the window handle of the main form (window) of the application.
Description
Use Handle when calling Windows API functions that require a parent window handle. For example, a DLL that displays its own top-level pop-up windows needs a parent window to display its windows in the application. Using the Handle property makes such windows part of the application, so that they are minimized, restored, enabled and disabled with the application.
If i focus on the words «the window handle of the main form of the application«, and i take that to mean the window handle of the main form of the application, then i can compare:
but they are not the same:
Really what i’m asking is: What is the design rationale that makes Application.Handle exist? If i can understand the why, the how should become obvious.
Update Understanding through a game of twenty questions:
This can cause some problems with modal forms shown from secondary forms.
If the user switches away from the app while a modal form is up, and then back to the form that showed it, the modal form may hide beneath the form. It is possible to deal with this by making sure the modal form is parented [sic; he meant owned] to the form that showed it (using params.WndParent as above)
He also talked about using the new Windows extended style that forces a window to appear on the taskbar (when the normal rules of making it un-owned is insufficient, impractical, or undesirable), by adding the WS_EX_APPWINDOW extended style:
But then he cautions:
If you click on a secondary forms taskbar button while another app is active this will still bring all the applications forms to front. If you do not want that there is option
This will add the taskbar button for your form, but there are a few other minor details to handle. Most obviously, your form still receives minimize/maximize that get sent to the parent form (the main form of the application). In order to avoid this, you can install a message handler for WM_SYSCOMMAND by adding a line such as:
Note that this handler goes in the PARENT form of the one you want to behave independantly of > the rest of the application, so as to avoid passing on the minimize message. You can add similar > code for SC_MAXIMIZE, SC_RESTORE, etc.
will remove Application and it’s window Handle from interfering with my form, and Windows should once again send me my mimimize/maximize/restore messages?
Obviously Borland realized the flaw in their initial design. What was their initial design, what problem was it solving, what is the flaw, what was the re-design, and how does it solve the problem?
3 Answers 3
The reason for the application window has a bit of a sordid history. When developing Delphi 1, we knew we wanted to use «SDI» (windows scattered all over the desktop) ui model for the IDE. We also knew that Windows sucked (and still does) at that model. However we also noticed that Visual Basic at that time employed that model and it seemed to work well. Upon further examination, we found that VB used a special «hidden» parking window which was used as the «owner» (Windows blurs the notion of parent and owner at times, but the distinction is similar to VCL) for all the other visible windows.
This is how we solved the «problem» where the windows containing the main menu was rarely ever focused so processing Alt-F for the File menu simply wouldn’t work. By using this central parking window as an intermediary, we could more easily keep track of and route messages to the appropriate windows.
This arrangement also solved another issue where normally multiple top level windows were entirely independent. By making the application handle the «owner» of all these windows, they would all behave in concert. For instance, you may have noticed that when you select any of the application windows, all the application windows move to the front and retain their z-order relative to each other. This would also make the application minimize and restore as a functional grouping.
That is a consequence of using this model. We could have manually done all this work to keep things straight, but the design philosophy was to not re-invent Windows, but to leverage it where we could. That is also why a TButton or a TEdit is really a Windows «User» BUTTON and EDIT window class and style, respectively.
As Windows evolved, that «SDI» model began to fall out of favor. In fact Windows itself began to become «hostile» to that style of application. Starting with Windows Vista and continuing to 7, the user shell doesn’t seem to work well with an application using a parking window. So, we set out to shuffle things around in VCL to eliminate the parking window and move its function into the main form. This presented several «chicken and egg» problems whereby we need to have the parking window available early enough in the application initialization so that other windows can «attach» to it, but the main form itself may not be constructed soon enough. TApplication has to jump through a few hoops to get this to work, and there have been a few subtle edge cases that have caused issue, but most of the problems have been worked out. However, for any application you move forward, it will remain using the older parking window model.
Delphi: Что такое Application.Handle?
Помощь Delphi гласит:
Предоставляет доступ к дескриптору окна основной формы (окна) приложения.
Описание
Используйте Handle при вызове функций Windows API, для которых требуется дескриптор родительского окна. Например, библиотека DLL, которая отображает свои собственные всплывающие окна верхнего уровня, нуждается в родительском окне для отображения своих окон в приложении. Использование свойства Handle делает такие окна частью приложения, так что они минимизируются, восстанавливаются, включаются и отключаются вместе с приложением.
Если я сосредоточусь на словах «дескриптор окна основной формы приложения«, и я понимаю, что это означает дескриптор окна основной формы приложения, тогда я могу сравнить:
но они не одинаковые
На самом деле я спрашиваю: что такое логическое обоснование, которое делает Application.Handle существующим? Если я могу понять почему, как должно стать очевидным.
Обновление понимания через игру из двадцати вопросов:
Говоря о решении сделать окно появляется на панели задач, сделав его владельцем null Питер Белов в 2000 году сказал:
Это может вызвать некоторые проблемы с модальными формами, показанными из вторичных форм.
Если пользователь отключается от приложения, когда модальная форма активна, а затем возвращается к форме, которая его показала, модальная форма может скрываться под формой. С этим можно справиться, убедившись, что модальная форма является парентной [sic; он имел в виду принадлежал] к форме, которая показала это (используя params.WndParent как указано выше)
Он также говорил об использовании нового расширенного стиля Windows, который заставляет окно появляться на панели задач (когда обычные правила сделать его не принадлежащим недостаточно, непрактично или нежелательно), добавляя WS_EX_APPWINDOW расширенный стиль:
Но тогда он предупреждает:
Если вы нажмете на кнопку панели задач вторичных форм, когда другое приложение активно, это все равно выведет все формы приложений на передний план. Если вы не хотите, чтобы был вариант
Это добавит кнопку панели задач для вашей формы, но есть несколько других мелких деталей, которые нужно обработать. Очевидно, что ваша форма по-прежнему получает минимизацию / максимизацию, которые отправляются в родительскую форму (основную форму заявки). Чтобы избежать этого, вы можете установить обработчик сообщений для WM_SYSCOMMAND, добавив строку, например:
Обратите внимание, что этот обработчик имеет вид PARENT, который вы хотите вести независимо от остальной части приложения, чтобы избежать передачи сообщения свернуть. Вы можете добавить похожий> код для SC_MAXIMIZE, SC_RESTORE и т. Д.
удалит Application и это окно Handle от вмешательства в мою форму, и Windows должна снова отправить мне мои сообщения об уменьшении / увеличении / восстановлении?
Очевидно, Borland понял недостаток в их первоначальном дизайне. Каков был их первоначальный дизайн, какую проблему он решал, в чем недостаток, какова была редизайн и как она решает проблему?
3 ответа
Причина для окна приложения имеет немного грязную историю. При разработке Delphi 1 мы знали, что хотим использовать модель интерфейса пользователя «SDI» (окна разбросаны по всему рабочему столу) для IDE. Мы также знали, что Windows сосала (и все еще делает) в этой модели. Однако мы также заметили, что Visual Basic в то время использовал эту модель, и она, казалось, работала хорошо. При дальнейшем рассмотрении мы обнаружили, что VB использовал специальное «скрытое» парковочное окно, которое использовалось как «владелец» (Windows иногда размывает понятие «родитель» и «владелец», но различие похоже на VCL) для всех остальных видимых окон,
Вот как мы решили «проблему», когда окна, содержащие главное меню, были редко когда-либо сфокусированы, поэтому обработка Alt-F для меню «Файл» просто не работала. Используя это центральное парковочное окно в качестве посредника, мы могли бы легче отслеживать и направлять сообщения в соответствующие окна.
Эта договоренность также решила другую проблему, где обычно несколько окон верхнего уровня были полностью независимы. Заставив приложение обращаться с «владельцем» всех этих окон, они будут вести себя согласованно. Например, вы могли заметить, что когда вы выбираете любое из окон приложения, все окна приложения перемещаются вперед и сохраняют свой z-порядок относительно друг друга. Это также приведет к минимизации и восстановлению приложения в виде функциональной группировки.
Это является следствием использования этой модели. Мы могли бы вручную проделать всю эту работу, чтобы все было прямо, но философия дизайна заключалась не в том, чтобы заново изобретать Windows, а в том, чтобы использовать ее там, где мы могли. Именно поэтому TButton или TEdit действительно являются классом и стилем окон Windows BUTTON и EDIT пользователя соответственно.
По мере развития Windows эта модель «SDI» начала терять популярность. Фактически, сама Windows стала «враждебной» к этому стилю приложения. Начиная с Windows Vista и далее до 7, пользовательская оболочка, похоже, не очень хорошо работает с приложением, использующим окно парковки. Итак, мы решили перемешать вещи в VCL, чтобы убрать окно парковки и перенести его функцию в основную форму. Это создало несколько проблем типа «курица и яйцо», из-за которых нам нужно было иметь окно парковки достаточно рано при инициализации приложения, чтобы другие окна могли «прикрепиться» к нему, но сама основная форма может быть построена недостаточно быстро. TApplication должен перепрыгнуть через несколько обручей, чтобы заставить это работать, и было несколько тонких крайних случаев, которые вызвали проблему, но большинство проблем было решено. Тем не менее, для любого приложения, которое вы продвигаете, оно будет использовать старую модель окна парковки.





