Главная » 2009 » Август » 19 » Программирование на основе Win32 API в Delphi
Программирование на основе Win32 API в Delphi
08:54
Программирование на основе Win32 API в Delphi Источник: www.codenet.ru 1. Введение
Любую современную программу или программную технологию можно представить как совокупность программных "слоев". Каждый из этих слоев производит свою собственную работу, которая заключается в повышении уровня абстракции производимых операций. Так, самый низший слой (слои) вводит понятия, которые позволяют абстрагироваться от используемого оборудования; следующий слой (слои) позволяет программисту абстрагироваться от сложной последовательности вызовов функций, вводя такое понятие как протокол и т.д. Практически в любом современном программном продукте можно обнаружить и выделить около десятка последовательных слоев абстракции.
Абстракция от оборудования и низкоуровневых протоколов вводится в ядра операционных систем в виде библиотек api (application program interface). Однако современные тенденции приводят к необходимости абстрагирования и от самих операционных систем, что позволяет переносить программы с одной операционной системы на другую путем простой перекомпиляции (транслируемые программы, в основном, вообще не требуют никаких действий по переносу).
Абстракцию, которая доступна программисту в виде библиотек api можно назвать базовой. Это самый низкий уровень абстракции, который доступен для прикладного программирования. На уровне ядра системы доступны и более низкие уровни абстракции, однако для их использования необходимо разрабатывать специализированные программы (драйвера, модули). Базовый уровень абстракции (api) предоставляет максимально широкие возможности для прикладного программирования и является наиболее гибким. Однако, программирование с использованием api является гораздо более трудоемким и приводит к значительно большим объемам исходного кода программы, чем программирование с использованием дополнительных библиотек.
Дополнительные библиотеки поставляются со многими средствами разработки с целью уменьшения трудоемкости и сроков разработки программ, что в итоге приводит к повышению их конкурентноспособности. Но применение дополнительных библиотек абстракций приводит к резкому увеличению размеров откомпилированных программ, из-за того что в программу включается код используемых библиотек, к тому же это включение зачастую бывает неэффективным - в программу включаются неиспользуемые участки кода. Кроме того, чем больше уровень абстракции библиотеки, тем сложнее ее код, и тем больше трудностей возникает при решении сложных задач. Приходится учитывать множество взаимосвязей и взаимных влияний отдельных элементов и процессов библиотеки друг на друга. Кроме того, структура и функциональность любой библиотеки обычно рассчитывается на удовлетворение всех потенциально возникающих задач, что приводит к ее громоздкости и неэффективности.
В delphi используется очень мощная и сложная библиотека vcl (visual components library), которая помимо непосредственных абстракций вводит также и множество своих функциональных классов. В этой библиотеке находятся компоненты для визуального отображения информации, работы с базами данных, с системными объектами, компоненты для работы с internet-протоколами, классы для написания своих com-объектов и многое другое. Модули библиотеки подключаются к компиляции по мере необходимости, однако базовый размер простейшего диалогового проекта с одной формой превышает 300кБ (со статически скомпонованной библиотекой). И такой размер во многих случаях может оказаться слишком большим, особенно если программа не требует большой функциональности в интерфейсе.
Для решения этой проблемы можно отказаться от использования библиотеки vcl, и программировать, используя базовый набор функций win32 api. Однако, если при разработке линейных, недиалоговых, нерезидентных программ не возникает никаких трудностей, то разработка программ, требующих активного взаимодействия с пользователем или системой, становится трудоемкой. Структурное программирование, рекомендуемое в таких случаях, оказывается неэффективным и трудоемким.
Данная статья посвящена проблеме создания и использования компактной объектно-ориентированной библиотеки, которая бы облегчила построение небольших и эффективных программ на основе win32 api.
2. Существующие решения
Автору известны три объектно-ориентированные библиотеки, которые можно рассматривать как альтернативу библиотеке vcl при написании компактных программ. Это библиотеки классов xcl, acl и kol. Все библиотеки бесплатны и поставляются в исходных кодах.
Библиотека acl (api control library) Автор: Александр Боковиков, Екатеринбург, Россия Страничка: http://a-press.ur.ru/pc/bokovikov e-mail: abb@adx.ru Классы и модули: tfont, tfonts, tcontrol, twincontrol, tstdcontrol, tlabel, tedit, tlistbox, tbutton, tcheckbox, tcombobox, tgroupbox, tprogressbar, tkeyboard
Библиотека kol (key object library) Автор: vladimir kladov (mr.bonanzas) Страничка: http://xcl.cjb.net e-mail: bonanzas@xcl.cjb.net Классы и модули: tobj, tlist, tgraphictool, tcanvas, tcontrol, ttimer, ttrayicon, tstream, tstrlist, tdirlist, tinifile
Как видно из списка приведенных для каждой библиотеки классов, эти библиотеки предендуют скорее не на помощь при написании программ с использованием win32 api, а пытаются создать более высокий уровень абстракции чем api, по крайней мере в графической части (особенно это относится к xcl). Более того, иерархия и перечень объектов совпадают с соответствующими структурами в библиотеке vcl, что скорее всего связано с желанием авторов обеспечить логическую совместимость с vcl при построении программ на основе этих библиотек.
Данные библиотеки не обеспечивают минимального размера программы, за счет того что предоставляют более высокий уровень абстракции. Они являются компромисом между программированием с использованием vcl и программированием на чистом api.
3. Принципы построения api-библиотеки
тандартным видом api-программирования является структурное программирование. Примеры такого программирования на win32 api есть практически в любой книжке по borland pascal, borland c++, microsoft visual c++ и другим системам разработки. Множество примеров api-программирования на С содержится в поставке microsoft visual c++.
Структурное программирование с оконными функциями, процедурами обработки команд, не в состоянии обеспечить быструю и эффективную разработку программ. В современной ситуации большинство программистов привыкло к объектно-ориентированному методу, с возможностью инкапсуляции, наследования и переопределения методов объектов. Такое программирование оказывается наиболее эффективным.
Кроме того, для построения эффективной api-библиотеки прежде всего нужно выяснить, какие задачи при работе с win32 api являются наиболее трудоемкими. Практика показывает, что наиболее неудобным и трудоемким элементом является реализация основного диспетчера логики программы - оконной функции. Реализация этой функции в качестве метода класса, а не простой глобальной функции, позволила бы улучшить структуру кода и облегчить программирование путем инкапсулирования всех переменных внутри оконного класса.
Программирование может быть еще более облегчено, есть возпользоваться механизмом message-процедур языка object pascal. Вызов этих процедур полностью лежит на компиляторе и корневом объекте tobject и включает в себя методы dispatch, defaulthandler, а также все методы, объявленные с директивой message. Такое решениее позволит полностью отказаться от громоздкого оператора case в оконной функции.
Учитывая все вышеперечисленное автором была создана компактная библиотека оконных классов winlite. Эта библиотека является минимальной, она не вводит более высоких уровней абстракции чем существуют в win32 api - она только облегчает работу, переводом программирования в объектно-ориентированное русло. Размер библиотеки очень небольшой и вся она помещается в один модуль. Библиотека реализует базовый класс tliteframe и построенные на основе него оконные классы:
tlitewindow - класс окна, с возможностью subclass'инга; tlitedialog - класс немодального диалога; tlitedialogbox - класс модального диалога. Библиотека может быть использована совместно с vcl. На первый взгляд, это возможность является абсурдной и ненужной, так как об экономии размера в этом случае не может быть и речи. Однако, иногда бывают моменты, когда реализация специфических оконных элементов на основе объектов twincontrol или tcustomcontrol может быть затруднена или неэффективна из-за их сложности и неочевидного поведения. В этом случае, можно реализовать такой элемент на базе класса tlitewindow - он будет вести себя стандартным образом, как и полагается вести себя стандартному оконному элементу win32. Благодаря своей простой архитектуре библиотека может быть использована в многопоточной программе. Конечно, вы не сможете вызывать методы классов одного потока из другого потока без соответствующей синхронизации. Однако, вы можете беспрепятственно создавать оконные классы в различных потоках без блокировки и синхронизации, а также посылать сообщения оконным классам в другом потоке.
Практический совет: при api-программировании программист должен сам следить за корректным освобождением многочисленных ресурсов, которые занимает программа во время выполнения. Поэтому, для облегчения этой задачи используйте какую-либо контролирующую утилиту, например memproof или numega boundschecker. Корректное освобождение занятых ресурсов крайне необходимо !
Для редактирования шаблонов диалогов можно использовать любой редактор ресурсов, например borland resource workshop, правда он несколько неудобен, а окончательный результат все равно приходится корректировать вручную.
Вся документация необходимая для api-программирования содержится в поставляемых компанией microsoft компакт-дисках с документацией под общим названием msdn (microsoft developer's network). Существует online-версия документации по адресу http://msdn.microsoft.com. Урезанная версия msdn, содержащая основные файлы помощи, поставляется с delphi.
Прежде чем вы решите работать над своим проектом в русле win32 api, подумайте, а зачем вам это нужно? В подавляющем числе случаев размер программы не имеет никакого значения. Я не хочу сказать, что api-программирование сложнее чем vcl-программирование. Во многих случаях легче изучить и написать 10 вызовов api с кучей аргументов и понимать, что происходит, чем написать 1 вызов простой, на первый взгляд, vcl-инструкции и потом долго исследовать дебри vcl в поисках ответа. Просто api-программирование - это другая культура, к которой вы, возможно, не привыкли. И первоначальная работа может вызвать у вас сильное разочарование. api-программирование требует дотошности, кропотливости и внимательного изучения документации.
Те же, кто отважился программировать на api, наряду с библиотекой winlite могут совместно использовать невизуальные классы как из состава vcl (модули sysutils, classes), так и многие сторонние - естественно, что размер вашей программы при этом увеличится.
Невизуальные классы библиотеки acl - http://a-press.ur.ru/pc/bokovikov Невизуальные классы библиотеки xcl - http://xcl.cjb.net jedi code library - http://www.delphi-jedi.com Системные компоненты на torry - http://www.torry.ru Заслуживает внимание работа Владимира Кладова по изменению функциональности обязательного модуля system.pas. Со времен первых версий turbo pascal этот модуль по умолчанию компонуется в исполняемый код программы. Код модуля реализует многие принципы и решения заложенные в синтаксис и логику языка object pascal, и изменение этого модуля позволяет модифицировать реализацию этой логики. Такое решение является специфичным для языка object pascal в отличие, например, от c/c++, где компилятор и абсолюдно все модули никак не связаны. Изменение модуля system.pas, а именно его разбиение на блоки и сокращение редко используемых участков кода позволило сократить постоянные (не переменные) издержки примерно на 8 кБ. Конечно, для больших проектов, такое сокращение может быть и незаметным, однако интересен сам принцип. Модифицированный модуль system.pas - http://xcl.cjb.net 4. Библиотека winlite
//////////////////////////////////////////////////////////////////////////////// // winlite, библиотека классов и функций для работы с win32 api // (c) Николай Мазуркин, 1999-2000 // _____________________________________________________________________________ // Оконные классы ////////////////////////////////////////////////////////////////////////////////
unit winlite;
interface
uses windows, messages;
Инициализационные структуры Объявление структур, которые используются для формирования параметров вновь создаваемых окон и диалогов соответственно.
//////////////////////////////////////////////////////////////////////////////// // Параметры для создания окна //////////////////////////////////////////////////////////////////////////////// type twindowparams = record caption : pchar; style : dword; exstyle : dword; x : integer; y : integer; width : integer; height : integer; wndparent : thandle; wndmenu : thandle; param : pointer; windowclass : twndclass; end;
//////////////////////////////////////////////////////////////////////////////// // Параметры для создания диалога //////////////////////////////////////////////////////////////////////////////// type tdialogparams = record template : pchar; wndparent : thandle; end;
Декларация базового класса tliteframe Базовый класс для окон и диалогов. Инкапсулирует в себе дескриптор окна и объявляет общую оконную процедуру. Реализует механизм message-процедур.
//////////////////////////////////////////////////////////////////////////////// // tliteframe // _____________________________________________________________________________ // Базовый класс для объектов tlitewindow, tlitedialog, tlitedialogbox //////////////////////////////////////////////////////////////////////////////// type tliteframe = class(tobject) private fwndcallback: pointer; fwndhandle : thandle; fwndparent : thandle; function windowcallback(hwnd: hwnd; msg, wparam, lparam:longint): longint; stdcall; protected procedure windowprocedure(var msg: tmessage); virtual; public property wndhandle: thandle read fwndhandle; property wndcallback: pointer read fwndcallback; public constructor create(awndparent: thandle); virtual; destructor destroy; override; end;
Декларация оконного класса tlitewindow Создание уникального класса окна и создание окна. Возможность субклассинга стороннего окна.
//////////////////////////////////////////////////////////////////////////////// // Конструктор //////////////////////////////////////////////////////////////////////////////// constructor tliteframe.create(awndparent: thandle); begin inherited create; // Запоминаем дескриптор родительского окна fwndparent := awndparent; // Создаем место под блок обратного вызова fwndcallback := virtualalloc(nil,12,mem_reserve or mem_commit, page_execute_readwrite); // Формируем блок обратного вызова asm mov eax, self mov ecx, [eax].tliteframe.fwndcallback mov word ptr [ecx+0], $6858 // pop eax mov dword ptr [ecx+2], eax // push _self_ mov word ptr [ecx+6], $e950 // push eax mov eax, offset(tliteframe.windowcallback) sub eax, ecx sub eax, 12 mov dword ptr [ecx+8], eax // jmp tliteframe.windowcallback end; end;
//////////////////////////////////////////////////////////////////////////////// // Деструктор //////////////////////////////////////////////////////////////////////////////// destructor tliteframe.destroy; begin // Уничтожаем структуру блока обратного вызова virtualfree(fwndcallback, 0, mem_release); // Уничтожение по умолчанию inherited; end;
//////////////////////////////////////////////////////////////////////////////// // tliteframe // _____________________________________________________________________________ // Функции обработки сообщений ////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////// // Функция обратного вызова для получения оконных сообщений //////////////////////////////////////////////////////////////////////////////// function tliteframe.windowcallback(hwnd: hwnd; msg, wparam, lparam: integer): longint; var windowmsg : tmessage; begin // Запоминаем дескриптор окна, если это первый вызов оконной процедуры if fwndhandle = 0 then fwndhandle := hwnd; // Формируем сообщение windowmsg.msg := msg; windowmsg.wparam := wparam; windowmsg.lparam := lparam; // Обрабатываем его windowprocedure(windowmsg); // Возвращаем результат обратно системе result := windowmsg.result; end;
//////////////////////////////////////////////////////////////////////////////// // Виртуальная функция для обработки оконных сообщений //////////////////////////////////////////////////////////////////////////////// procedure tliteframe.windowprocedure(var msg: tmessage); begin // Распределяем сообщения по обработчикам dispatch(msg); end;
//////////////////////////////////////////////////////////////////////////////// // Деструктор //////////////////////////////////////////////////////////////////////////////// destructor tlitewindow.destroy; begin // Наш объект - объект субклассиннга ? if fwndsubclass = nil then begin // Уничтожаем класс окна unregisterclass(fwndparams.windowclass.lpszclassname, hinstance); // Уничтожаем окно if iswindow(fwndhandle) then destroywindow(fwndhandle); end else // Восстанавливаем старую оконную функцию setwindowlong(fwndhandle, gwl_wndproc, dword(fwndsubclass)); // Уничтожение по умолчанию inherited; end;
//////////////////////////////////////////////////////////////////////////////// // Формирование параметров окна по умолчанию //////////////////////////////////////////////////////////////////////////////// procedure tlitewindow.createwindowparams(var windowparams: twindowparams); var wndclassname : string; begin // Формируем имя класса str(dword(self), wndclassname); wndclassname := classname+':'+wndclassname; // Заполняем информацию о классе окна with fwndparams.windowclass do begin style := cs_dblclks; lpfnwndproc := wndcallback; cbclsextra := 0; cbwndextra := 0; lpszclassname := pchar(wndclassname); hinstance := hinstance; hicon := loadicon(0, idi_application); hcursor := loadcursor(0, idc_arrow); hbrbackground := color_btnface + 1; lpszmenuname := ''; end; // Заполняем информацию об окне with fwndparams do begin wndparent := fwndparent; caption := 'lite window'; style := ws_overlappedwindow or ws_visible; exstyle := 0; x := integer(cw_usedefault); y := integer(cw_usedefault); width := integer(cw_usedefault); height := integer(cw_usedefault); wndmenu := 0; param := nil; end; end;
//////////////////////////////////////////////////////////////////////////////// // tlitewindow // _____________________________________________________________________________ // Функции обработки сообщений ////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////// // Обработчик сообщений по умолчанию //////////////////////////////////////////////////////////////////////////////// procedure tlitewindow.defaulthandler(var msg); begin // Наш объект - объект субклассиннга ? if fwndsubclass = nil then // Вызываем системную функцию обработки сообщений with tmessage(msg) do result := defwindowproc(fwndhandle, msg, wparam, lparam) else // Вызываем старую оконную функцию обработки сообщений with tmessage(msg) do result := callwindowproc(fwndsubclass, fwndhandle, msg, wparam, lparam); end;
//////////////////////////////////////////////////////////////////////////////// // Конструктор //////////////////////////////////////////////////////////////////////////////// constructor tlitedialog.create(awndparent: thandle); begin inherited; // Формируем параметры диалога createdialogparams(fdlgparams); // Создаем диалог with fdlgparams do createdialogparam(hinstance, template, wndparent, wndcallback, 0); end;
//////////////////////////////////////////////////////////////////////////////// // Деструктор //////////////////////////////////////////////////////////////////////////////// destructor tlitedialog.destroy; begin // Уничтожаем диалог if iswindow(fwndhandle) then destroywindow(fwndhandle); // Уничтожение по умолчанию inherited; end;
//////////////////////////////////////////////////////////////////////////////// // Формирование параметров диалога по умолчанию //////////////////////////////////////////////////////////////////////////////// procedure tlitedialog.createdialogparams(var dialogparams: tdialogparams); begin dialogparams.wndparent := fwndparent; dialogparams.template := ''; end;
//////////////////////////////////////////////////////////////////////////////// // Обработка сообщений по умолчанию //////////////////////////////////////////////////////////////////////////////// procedure tlitedialog.defaulthandler(var msg); begin // Возвращаемые значения по умолчанию with tmessage(msg) do if msg = wm_initdialog then result := 1 else result := 0; end;
Реализация модального диалогового класса tlitedialogbox
//////////////////////////////////////////////////////////////////////////////// // Формирование параметров диалога по умолчанию //////////////////////////////////////////////////////////////////////////////// procedure tlitedialogbox.createdialogparams(var dialogparams: tdialogparams); begin dialogparams.wndparent := fwndparent; dialogparams.template := ''; end;
//////////////////////////////////////////////////////////////////////////////// // Активизация модального диалога //////////////////////////////////////////////////////////////////////////////// function tlitedialogbox.showmodal: integer; begin // Формируем параметры диалога createdialogparams(fdlgparams); // Показываем диалог with fdlgparams do result := dialogboxparam(hinstance, template, wndparent, wndcallback, 0); end;
//////////////////////////////////////////////////////////////////////////////// // Обработка сообщений по умолчанию //////////////////////////////////////////////////////////////////////////////// procedure tlitedialogbox.defaulthandler(var msg); begin // Возвращаемые значения по умолчанию with tmessage(msg) do if msg = wm_initdialog then result := 1 else result := 0; end;
Новейшая уникальная программа «Стройное тело» была разработана ведущими российскими экспертами-диетологами и эндокринологами (РАМН). Программа составит индивидуальную диету и разработает методику похудения исходя из Ваших личных параметров. При соблюдении данной диеты Вы похудеете больше чем на 10 кг в месяц. В первую неделю вы гарантированно потеряете 3-5кг лишнего веса. http://tinyurl.com/d5pxa2m
Нужна помощь Трифонов Александр http://svkris.ru/help-me/63-trifonov-alexander.html - Трифонов Александр, 2010, г. Белгород, Россия Диагноз: резидуально-органическое поражение центральной нервной системы, миотононический синдром с левосторонним гемисиндромом, мозжечковая недостаточность, задержка психоречевого развития, сколиоз 2 степени. Необходимо: курс лечения в клинике в Трускавце стоимостью 141 372 рублей. Подробнее на сайте http://www.svkris.ru/help-me.html - Красноярский благотворительный фонд "Святая Кристина"
Need help Alexander Trifonov http://svkris.ru/help-me/63-trifonov-alexander.html - Alexander Trifonov, 2010, Belgorod, Russia Diagnosis: residual-organic lesion of the central nervous system, miotononichesky syndrome with left gemisindromom, cerebellar failure, delay psychoverbal development, scoliosis 2 degrees. You need: a course of treatment in a clinic in Truskavets worth 3366 euros. Read more: http://www.svkris.ru/help-me.html - Krasnoyarsk charity "Holy Kristina"
http://mamont-creative.ru/stat/417-venecianskiy-karnaval-prazdnik-izyskannosti-i-bogatstva-ili-pochemu-ya-vlyubi las-v-gorod-kazanovy-.html - Венецианский карнавал - праздник изысканности и богатства, или Почему я влюбилась в город Казановы ...