Архивы рубрики: VC++

WinAPI — Действия с посторонними окнами (ресайз, перемещение, скрытие)

Как «словить» окно? Под словом «словить» я имею ввиду то, как обнаружить наличие окна (любого окна) среди других окон. Функция «ловли окна» очень часто используется в том случае если вам необходимо совершить над ним (окном) какое-нибудь действие: закрыть, переместить, спрятать, изменить размеры и т.д . Еще раз скажу, что все эти действия будут совершаться над «чужими» окноми, т.е. не над теми которые ва разрабатываете, а над окнами других приложений . Итак вы уже поняли ( я надеюсь ) для чего нужно «словить» окно. Ниже представлен фрагмент кода, позволяющий определить наличие окна в системе:
if findwindow(nil, ‘ 1.txt — Блокнот ‘) 0 then showmessage (‘Окно присутствует’);

Если вы поместите эту строчку в процедуру обработки события нажатия на кнопку (или в другое место ),запустите программу и нажмете на кнопку, ваша программа выдаст сообщение «Окно присутствует» если вы перед этим запустили блокнот и сохранили документ под названием 1.txt Иными словами программа смотрит есть ли окно с таким заголовком какой вы указали (он выделен вот таким цветом ) и если оно есть — выдает сообщение, в противном же случае ничего не произойдет.

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

//Поместите этот код в процедуру обработки события нажатия на кнопку

if findwindow(nil, ‘ 1.txt — Блокнот ‘) 0 then
SetWindowPos( findwindow(nil,’ 1.txt — Блокнот ‘) ,HWND_BOTTOM,1,1,20,20,SWP_nosize);
//———————————————————————————

Запустите Блокноти сохраните пустой(или не пустой) документ под именем 1.txt (Обратите внимание, что заголовок окна будет 1.txt — Блокнот ) Теперь запустите программу и нажмите на кнопку. Если все сделано правильно, то окно блокнота передвинится в левый верхний угол экрана. Теперь давайте разберем, как вы это сделали.

Первая строчка — функция «ловли» окна. Она проверяет есть ли окно с таким заголовком. Если есть, то выполняется следующая (вторая) строчка:

SetWindowPos( findwindow(nil,’ 1.txt — Блокнот ‘) ,HWND_BOTTOM,1,1,20,20,SWP_nosize);
Где :

первый параметр — заголовок окна, с которым надо что-либо сделать ( в данном случае — передвинуть). Заголовок передается функцией «ловли» окна

findwindow(nil,’ 1.txt — Блокнот ‘) , где 1.txt — Блокнот заголовок окна

Второй параметр — положение окно ( т.е. его Z-расположение ) он нам сейчас не нужен. Просто считайте, что он должен быть HWND_BOTTOM

Третий параметр — координыты окна по «Оси X» в пикселах.

Четвертый параметр — координыты окна по «Оси Y» в пикселах

(т.е. этими координмтами вы указываете куда двигать окно).

Пятый — размер окна по горизонтали

Шестой — размер окна по вертикали

И, наконец, седьмой параметр, он имеет несколько значений. Вот основные из них:

SWP_HIDEWINDOW — после проделаных действий над окном ( перемещением и изменением размера ) окно прячется

SWP_NOSIZE — если указан этот параметр, то 5й и 6й параметры учитываться не будут, т.е. окно менять размер не будет (этот параметр указан в нашем примере )

SWP_NOMOVE — если указан этот параметр, то 3й и 4й параметры учитываться не будут, т.е. окно не будет перемещаться.

SWP_SHOWWINDOW — после проделаных действий над окном ( перемещением и изменением размера ) окно будет показано ( работает только если окно было спрятано)

Теперь можно и текст изменить… Вот, например, есть у вас какое-нибудь окно, допустим заголовок у него все тот же — 1.txt — Блокнот . Как же из своей программы поменять заголовок у этого окна? А очень просто:

//Поместите этот код в процедуру обработки события нажатия на кнопку

setwindowtext (findwindow(nil,’ 1.txt — Блокнот ‘),’Окно By Vasya Pupkin’)

//———————————————————————————

Запустите программу, нажмите на кнопку и…о чудо — окно названо вашим именем 🙂

Функция setwindowtext имеет 2 параметра :

Первый — заголовок окна, у которого будем менять заголовок. (Простите за каламбур). Он определяется через функцию «ловли» окна:

findwindow(nil,’ 1.txt — Блокнот ‘) , где 1.txt — Блокнот и есть тот самый (начальный) заголовок окна

Второй — новый заголовок (типа String)

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

Showwindow(findwindow(nil,’ 1.txt — Блокнот ‘),sw_hide);

После выполнения программы окно «1.txt — Блокнот» будет спрятано (но не закрыто!). У этой функции тоже 2 параметра:

Первый — заголовок окна. Он определяется через функцию «ловли» окна:

findwindow(nil,’ 1.txt — Блокнот ‘) , где 1.txt — Блокнот заголовок окна

Второй — указания, что делать:

SW_HIDE -Прятать окно

SW_MAXIMIZE -Максимизировать окно

SW_MINIMIZE -Минимизировать окно

SW_SHOW -показать окно (Его надо вызывать если вы хотите показать уже спрятанное окно)

Еще один способ передачи комманд окнам — это использование функции Postmessage . Например, чтобы закрыть окно с заголовком «1.txt — Блокнот» вставьте этот код в процедуру обработки события нажатия на кнопку:

postmessage(findwindow(nil,’1.txt — Блокнот’), wm_quit,0,0);

Если у вас было запущено окно с заголовком 1.txt — Блокнот, то оно закроется.

Postmessage имеет несколько параметров, и в различных случаях они разные. Вот некий шаблон использования Postmessage:

postmessage( findwindow(nil,’ заголовок окна ‘), wm_команда, парам3, парам4 );

В первом параметре — findwindow(nil,’ заголовок окна ‘) вы передаете заголовок окна.

Второй пареметр — комманда, указывающая какое действие должно совершиться над окном ( в данном примере эта комманда — WM_QUIT, она закрывает окно). Все эти комманды начинаются на WM_ (wm_size, wm_quit, и т.д.)

Третий и четвертый параметры(парам3 и парам4) — это параметры wm_комманды. Чтобы получить сведения о этих параметрах( да и о самих wm_коммандах) откройте справку Delphi ( ПУСК=>Программы=> Borland Delphi => HELP=> MS SDK Help Files=> Win32 Programmer’s Reference ) откройте вкладку «Указатель» и наберите WM_ и вы увидете целый список этих комманд. Если вы хотите узнать параметры для данной wm_комманды, откройте справку по этой wm_комманде и там вы увидете, какие должны быть параметры.

 

Вывод текста с использованием Windows API функций

Вывод текста является одной из основных задач, которую приходится решать в программе при организации вывода данных. Речь пойдет о выводе текста с использованием Windows API функций. Данная статья, безусловно не претендует на полноту обзора этой тематики, но о некоторых «подводных камнях» я все же расскажу.

Функции, которые позволяют это выполнять, весьма разнообразны, и использовать их, как Вы уже поняли, можно в разных ситуациях и случаях, и собственно говоря, именно Вам и решать, какие использовать. Кроме функций, которые непосредственно выводят текст, также существует внушительный список «подсобных» функций.
Давайте глянем на tCanvas. В этом классе реализовано только две функции TextOut и TextRect. Когда задача стоит просто в выводе текста на цветном фоне (например, combo box цветов), то функций этого класса предостаточно, и в большинстве случаев я ими и пользуюсь. Но давайте заглянем за ширму. Функция TextOut использует одноименную функцию Windows, а TextRect использует функцию ExtTextOut, т.е. если на прямую вызывать функции Windows, можно получить тот же результат. Кроме того, в этих функциях есть передаваемые параметры, которые класс прячет, использование которых в определенных ситуациях не может быть не нужным и полезным. Формат функций я буду приводить из модуля windows.pas поставляемый с Delphi 3.
function TextOut(DC: HDC; X, Y: Integer; Str: PChar; Count: Integer): BOOL;

 

Справочную информацию о передаваемых параметрах Вы сможете посмотреть здесь в файле справки, но давайте я первый раз все же расскажу. Все GDI функции вывода имеют параметр DC — контекст устройства, на который нужно нарисовать. Естественно, Canvas подставляет туда Canvas.Handle. Если Вы хотите нарисовать на чем-то другом, то должны самостоятельно получить контекст устройства функциями, типа GetDC. Не забывайте (!) освобождать ресурсы функцией ReleaseDC.

X, Y — привязочные координаты, относительно которых происходит прорисовка. По умолчанию, привязка выполнена к левому верхнему углу. Я конечно немного не правильно выразился — в Windows принято понятие «выравнивание», а не «привязка». Оно настраивается через функцию SetTextAlign, но о ней чуть позже.

Str — переменная, которая содержит выводимый текст, а Count — длина этого текста. В качестве переменной Str можно использовать переменную, типа array [0..n] of Chat; можно передавать просто текст: ‘Пример текста’. Если текст находится у Вас в переменной типа string то нужно произвести приведение типов: PChar(VarString).
Читать далее

Экспорт данных из Delphi в Excel

Рано или поздно практически каждый программист сталкивается с необходимостью организовать экспорт данных в MS Office. При этом каждое «поколение» программистов натыкается на одни и те же вилы.
Вот три часто встречающихся вопроса.
1. Как определить установлен ли Excel
2. Как определить запущен ли Excel
3. Как вывести данные в Excel
Большую помощь в понимании этих и других вопросов приносит чтение исходных текстов функций модуля ComObj. 🙂

Во всех случаях следует подключить модули ComObj и ActiveX

1. Как определить установлен ли Excel

Функция возвращает True если найден OLE-объект
Пример использования
if not IsOLEObjectInstalled(‘Excel.Application’) then
ShowMessage(

‘Класс не зарегистрирован’

)
else
ShowMessage(

‘Класс найден’

);

function IsOLEObjectInstalled(Name: String): boolean;
var
ClassID: TCLSID;
Rez : HRESULT;
begin

// Ищем CLSID OLE-объекта

Rez := CLSIDFromProgID(PWideChar(WideString(Name)), ClassID);

if Rez = S_OK then

// Объект найден

Result := true
else
Result := false;
end;
Если нужна более подробная информация об объекте, можно почитать хелп по функции API CLSIDFromProgID.
2. Как определить запущен ли Excel

Данный пример ищет активный экземпляр Excel и делает его видимым
var
ExcelApp : Variant;
begin
try

// Ищем запущеный экземплят Excel, если он не найден, вызывается исключение

ExcelApp := GetActiveOleObject(‘Excel.Application’);

// Делаем его видимым

ExcelApp.Visible := true;
except
end;
3. Как вывести данные в Excel

Можно выводить данные последовательно в каждую ячейку, но это очинь сильно замедляет работу. Лучше сформировать вариантный массив, и выполнить присвоение области (Range) этого массива.
var
ExcelApp, Workbook, Range, Cell1, Cell2, ArrayData : Variant;
TemplateFile : String;
BeginCol, BeginRow, i, j : integer;
RowCount, ColCount : integer;
begin

// Координаты левого верхнего угла области, в которую будем выводить данные

BeginCol := 1;
BeginRow := 5;

// Размеры выводимого массива данных

RowCount := 100;
ColCount := 50;

// Создание Excel

ExcelApp := CreateOleObject(

‘Excel.Application’

);

// Отключаем реакцию Excel на события, чтобы ускорить вывод информации

ExcelApp.Application.EnableEvents := false;

// Создаем Книгу (Workbook)
// Если заполняем шаблон, то
//Workbook := ExcelApp.WorkBooks.Add(‘C:\MyTemplate.xls’);

Workbook := ExcelApp.WorkBooks.Add;

// Создаем Вариантный Массив, который заполним выходными данными

ArrayData := VarArrayCreate([1, RowCount, 1, ColCount], varVariant);

// Заполняем массив

for I := 1 to RowCount do
for J := 1 to ColCount do
ArrayData[I, J] := J * 10 + I;

// Левая верхняя ячейка области, в которую будем выводить данные

Cell1 := WorkBook.WorkSheets[1].Cells[BeginRow, BeginCol];

// Правая нижняя ячейка области, в которую будем выводить данные

Cell2 := WorkBook.WorkSheets[1].Cells[BeginRow + RowCount — 1, BeginCol +
ColCount — 1];

// Область, в которую будем выводить данные

Range := WorkBook.WorkSheets[1].Range[Cell1, Cell2];

// А вот и сам вывод данных
// Намного быстрее поячеечного присвоения

Range.Value := ArrayData;

// Делаем Excel видимым

ExcelApp.Visible := true;

 

Сообщения Windows

Человеку, знакомому с Delphi, должна быть ясна схема событийного управления. Программист пишет только код реакции на какое-либо событие, а дальше программа ждёт, когда система сочтёт, что настало время передать управление этому участку кода. Простые программы в Delphi состоят исключительно из методов реакции на события вроде OnCreate, OnClick, OnCloseQerry и т. д. Причём событием называется не только событие в обычном смысле этого слова, то есть когда происходит что-то внешнее, но и ситуация, когда событие используется просто для передачи управления основной программе в тех случаях, когда VCL не может сама справиться с какой-то задачей. Примером такого события является, например, TListBox.OnDrawItem. Устанавливая стиль списка в lbOwnerDrawFixed или lbOwnerDrawVariable, программист как бы сообщает VCL, что он не доволен теми средствами рисования элементов списка, которыми она располагает, и что он берёт эту часть задачи на себя. И каждый раз, когда возникает необходимость в рисовании элемента, VCL передаёт управление специально написанному коду. На самом деле разница между двумя типами событий весьма условна. Можно так же сказать, что когда пользователь нажимает клавишу, VCL не знает, что делать, и поэтому передаёт управление обработчику OnKeyPress.

Событийное управление не есть изобретение авторов Delphi. Такой подход исповедует сама система Windows. Только здесь события называются сообщениями (message), что, на мой взгляд, даже лучше отражает ситуацию. Windows посылает программе сообщения, связанные либо с тем, что произошло что-то внешнее (мышь, клавиатура…), либо с тем, что самой системе потребовались от программы какие-то действия. Самым распространённым таким действием является предоставление информации. Например, когда Windows хочет узнать заголовок окна, она посылает этому окну специальное сообщение, в ответ на которое окно должно сообщить системе свой заголовок. Ещё бывают сообщения, которые просто уведомляют программу о начале какого-то действия (например, о начале перетаскивания окна) и предоставляют возможность вмешаться. Но это вмешательство необязательно.

В Delphi для реакции на каждое событие обычно создаётся свой метод. В Windows одна процедура, называемая оконной, обрабатывает все сообщения. В языке Си нет понятия «процедура», поэтому при использовании Паскаля может возникнуть путаница. Дело в том, что то, что называется оконной процедурой, на самом деле является функцией. Тем не менее, я буду использовать общепринятый термин «оконная процедура». Каждое сообщение имеет свой уникальный номер, а оконная процедура обычно целиком состоит из оператора case, и каждому сообщению соответствует своя альтернатива этого оператора. Номера сообщений учить не надо, потому что можно использовать константы, описанные в модуле Messages.dcu. Эти константы начинаются с префикса, указывающего на принадлежность сообщения к какой-то группе. Например, сообщения общего назначения начинаются с WM_: например, WM_Paint, WM_GetTextLength. Сообщения, специфичные, например, для кнопок, начинаются с префикса BM_. Остальные группы сообщений также связаны либо с теми или иными элементами управления, либо со специальными действиями, например, с динамическим обменом данными (dynamic data exchange, DDE). Обычной программе приходится обрабатывать довольно много сообщений, поэтому оконная процедура бывает, как правило, очень длинной и громоздкой. Оконная процедура описывается программистом как callback функция и указывается при создании оконного класса. Таким образом все окна данного класса имеют одну и ту же оконную процедуру. Впрочем, существует возможность породить так называемый подкласс, то есть новый класс, наследующий все свойства существующего, за исключением оконной процедуры. Несколько подробнее об этом будет сказано далее.

Кроме номера, каждое сообщение содержит два параметра — WParam и LParam. Буквы «W» и «L» означают «Word» и «Long», то есть первый параметр 16-разрядный, а второй — 32-разрядный. Однако так было только в старых, 16-разрядных версиях Windows. В 32-разрядных версиях оба параметра 32-разрядные, несмотря на их названия. Конкретный смысл каждого параметра зависит от сообщения. В некоторых сообщениях один или оба параметра могут вообще не использоваться, в других — наоборот, двух параметров даже не хватает. В этом случае один из параметров (обычно LParam) содержит указатель на дополнительные данные. После обработки сообщения оконная процедура должна вернуть какое-то значение. Обычно это значение просто сигнализирует, что сообщение не нуждается в дополнительной обработке, но в некоторых случаях оно более осмысленно, например, WM_SetIcon должно вернуть дескриптор иконки, которая была установлена ранее. Если программист не хочет обрабатывать сообщение самостоятельно, он должен вызвать для его обра-ботки функцию DefWindowProc.

Обработка сообщения требует времени, иногда довольно значительного. За это время окну может быть отправлено ещё несколько сообщений. Чтобы они не пропали, Windows организует так называемую очередь сообщений. Очередь сообщений своя для каждой нити. Нить должна сама выбирать сообщения из этой очереди, транслировать их и затем вызывать функцию Dispatch-Message, чтобы направить это сообщение в нужную оконную процедуру. Всё это лучше не писать самому, а оставить на совести VCL, которая прекрасно с этим справляется. При программировании в Delphi обычно требуется либо нестандартная реакция на сообщение, либо отправка сообщения другому окну.

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

Кроме параметров WParam и LParam, каждому сообщению приписывается время возникновения и координаты курсора в момент возникновения. Эти параметры можно узнать с помощью функций GetMessagePos и GetMessageTime.

Разумеется, что Delphi предоставляет программисту все средства, необходимые для обработки сообщений. Самый простой способ — описать метод для обработки сообщения с директивой message. Это выглядит примерно так

type TSomeForm = class(TForm)
…………….
procedure WMSomeMessage(var Message: TMessage); message WM_SomeMessage;
…………….

procedure TSomeForm.WMSomeMessage;
begin
…………..
inherited
end;

Стандартная оконная процедура в Delphi устроена так, что она ищет среди методов класса специальные методы для обработки каждого сообщения. Эти методы во многом подобны обыкновенным виртуальным методам. Другими словами, если переопределить такой метод, будет вы-зван именно новый, а не старый вариант. Вообще говоря, в классе-родителе метода для обра-ботки какого-то конкретного сообщения может и не существовать. Это, однако, никак не сказывается на синтаксисе (в отличие от обычных виртуальных методов, где приходится писать директиву virtual для вновь созданных и override для перекрытых). Кроме того, при перекрытии методов обработки сообщений не важно имя метода, значение имеет только константа, стоящая после message. Именно поэтому при вызове перекрытого метода для обработки данного сообщения достаточно просто написать inherited, без указания имени метода. Такой способ вызова не приведёт к ошибке даже в том случае, если класс-родитель вообще не имел метода для обработки такого сообщения.

Тип TMessage сделан специально для обработки сообщений. Это запись, содержащая 32-разрядные целые поля Msg, WParam, LParam и Result. Первое поле содержит номер сообщения, два следующих — параметры сообщения, а полю Result метод должен присвоить то значение, которое потом вернёт системе оконная процедура. Именно из-за необходимости передавать значение параметр метода обработки сообщения должен быть переменной. При обработке сообщений часто приходится сталкиваться с ситуациями, когда один 32-разрядный параметр используется для передачи двух 16-разрядных значений. Чтобы облегчить программисту работу в таких случаях, тип TMessage описан как вариантная запись, поэтому в нём есть поля WParamLo, WParamHi, LParamLo, LParamHi, ResultLo и ResultHi, имеющие тип Word и дающие доступ к старшему и младшему словам соответствующего параметра.

Так как параметры WParam и LParam могут иметь совершенно различный смысл для разных сообщений, не всегда удобно представлять их в виде чисел. Иногда предпочтительнее, чтобы они

 

Callback функции

Прежде чем двигаться дальше, необходимо разобраться с тем, что такое callback функции. На русский язык это обычно переводится как функции косвенного вызова. Эти функции в программе описываются, но обычно не вызываются напрямую, хотя ничто не запрещает сделать это. В этом они похожи на те методы класса, которые связаны с событиями. Ничто не мешает вызывать напрямую, например, метод FormCreate, но делать это приходится крайне редко. С другой стороны, даже если этот метод не вызывается явно, он всё равно выполняется, потому что VCL автоматически вызывает его без прямого указания программиста. Еще одно общее свойство — конкретное имя метода при косвенном вызове не важно. Можно изменить его, но если этот метод по-прежнему будет связан с событием OnCreate, он так же будет успешно вызываться. Разница заключается только в том, что такие методы вызываются внутренними механизмами Delphi, а callback функции — самой системой Windows. Соответственно, на эти функции налагаются следующие требования: во-первых, эти функции должны быть именно функциями, а не методами класса (впрочем, иногда это условие удаётся обойти); во-вторых, эти функции должны быть написаны в соответствии с моделью вызова stdcall. Справочная система предлагает использовать модель callback, которая в имеющихся версиях Windows совпадает с stdcall. Однако в Delphi такая модель не поддерживается. Что же касается того, как программист сообщает системе о том, что он написал callback функцию, то это в каждом случае по-своему.

Очень часто функции косвенного вызова используются при перечислении некоторых объектов. В качестве примера рассмотрим перечисление окон с помощью функции EnumWindows. В справке она описана так:

BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam);

Соответственно, в Windows.pas она имеет вид

function EnumWindows(lpEnumFunc: TFNWndEnumProc; lParam: LPARAM): BOOL; stdcall;

тип TFNWndEnumProc совпадает с типом Pointer. Здесь должен стоять указатель на callback функцию. Синтаксис этой функции описан так:

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);

Функции с таким именем не существует в Win API. Это так называемый прототип функции, согласно которому следует описывать callback функцию. На самом деле этот прототип предоставляет большую свободу, чем это может показаться на первый взгляд. Как я уже сказал выше, имя может быть любым. Любыми могут быть и типы функции и параметров, при условии что новые типы совпадают по размерам с теми, которые указываются. Что касается типа функции и типа первого параметра, то они имеют определённый смысл и менять их тип практически бессмысленно. Другое дело со вторым параметром. Он предназначен специально для передачи значения, которое программист волен использовать по своему усмотрению, система не имеет на него никаких видов. А программисту может показаться удобнее работать не с типом LPARAM (то есть LongInt), а, например, с указателем или же с массивом из четырёх байт. Лишь бы были именно четыре байта, а не восемь, шестнадцать или ещё какое-то число. Можно даже превратить этот параметр в параметр-переменную, так как при этом функции будут передаваться всё те же четыре байта — адрес переменной. Но эти удовольствия для тех, кто хорошо разбирается с тем, как используется стек для передачи параметров при различных моделях вы-зова.

Как же работает EnumWindows? После вызова эта функция начинает по очереди перебирать все имеющиеся в данный момент окна верхнего уровня, то есть те, у которых нет родителя. Для каждого такого окна вызывается эта самая callback функция, в качестве первого параметра ей передаётся дескриптор данного окна (каждый раз, естественно, новый), в качестве второго — то, что было передано самой функции EnumWindows в качестве второго параметра (каждый раз одно и то же). Что же может делать callback функция с этим дескриптором? А всё, на что у программиста хватит фантазии. Например, можно минимизировать или вообще закрыть все эти окна, хотя не понятно, с чего бы вдруг устраивать такую диверсию. Или можно проверять все эти окна на соответствие какому-то условию, пытаясь найти нужное. А значение, возвращаемое callback функцией, влияет на работу EnumWindows. Если она возвращает False, значит, всё, что нужно, уже сделано, можно не перебирать остальные окна.

Окончательный код для того случая, когда второй параметр имеет тип Pointer, выглядит так:

function MyCallbackFunction(Wnd:HWnd; P: Pointer):Bool; stdcall;
begin
{ что-то делаем }
end;
…………..
var MyPointer:Pointer;
…………..
EnumWindows(@MyCallbackFunction, LongInt(MyPointer));

Что бы мы ни делали с типом второго параметра callback функции, тип соответствующего параметра EnumWindows не меняется. Поэтому необходимо явное приведение передаваемого параметра к типу LongInt. Обратное преобразование типов при вызове MyCallbackFunction осуществляется автоматически.

В 16-разрядных версиях Windows вызов callback функций осложнялся тем, что для них необходимо было делать специальный код, называемый прологом. Пролог создавался с помощью функции MakeProcInstance, удалялся после завершения с помощью FreeProcInstance. То есть вызов EnumWindows должен был бы выглядеть так:

var MyProcInstnace: TFarProc;
……………….
MyProcInstance := MakeProcInstance(@MyCallbackFunction, HInstance);
EnumWindows(MyProcInstance, LongInt(MyPointer));
FreeProcInstance(MyProcInstance);

В Delphi этот код будет работоспособным, так как для совместимости MyProcInstance и FreeProcInstance оставлены. Но они ничего не делают (в чём легко убедиться, просмотрев ис-ходный файл Windows.pas), поэтому можно обойтись и без них. Другой способ, с помощью которого в 16-разрядных версиях можно сделать пролог — описать функцию с директивой export. Эта директива сохранена для совместимости и в Delphi, но в 32-разрядных версиях она также ничего не делает (несмотря на то, что справка, например, по Delphi 3.0 утверждает обратное; в справке по Delphi 4.3 этой ошибки уже нет).