Статьи по Delphi (006)…
DirectX для начинающих. Часть вторая.
Автор: Виктор КодаНебольшое отступление.
Прошёл месяц с тех пор как я написал первую часть ( http://www.delphikingdom.com/helloworld/directx.htm ) статьи по использованию DirectX в среде Delphi. У меня накопилось ещё несколько примеров, которые, надеюсь, послужат наглядным руководством для начинающих.Прежде, чем описывать предложенные общему вниманию программы, хочу сообщить о некоторых изменениях в их коде по сравнению с примерами первой статьи, чтобы не останавливаться впоследствии на этих мелких деталях.
Вызовы _AddRef() и _Release() больше не используются – в конце концов я посчитал это бессмысленной тратой времени при наборе кода. К тому же, как выяснилось, что вызов именно этих методов привёл к неработоспособности одного из примеров предыдущей статьи – если кто интересовался, знает, что это был пример опроса клавиатуры с использованием DirectInput. После удаления вызовов программа стал работать корректно. По-видимому, имело место некорректное взаимодействие с драйвером клавиатуры. Выражение вида if COM-объект <> nil then COM-объект := nilПереписано с использованием процедуры следующего вида:
procedure SAFE_DELETE(p: TInterfacedObject);
begin
if p <> nil then
p := nil
end;
|
Модуль basedd8.pas в проектах для DirectDraw переименован в basedd7.pas – всё-таки DirectDraw – это часть DirectX 7, в версий 8 он как таковой отсутствует.
В функции LoadFiles() добавлен вызов DeleteObject() – как известно, после работы объекты GDI надо удалять, иначе они поглощают ресурсы системы. В данном случае именно такой объект создаётся при вызове функции GDI LoadImage() – казалось бы, тип HBITMAP – это всего лишь переопределение типа LongWord, копилятор самостоятельно удалит переменную этого типа после выхода из функции. На самом деле GDI при вызове LoadImage() (и других подобных функций) создаёт ресурс GDI и резервирует для него часть системной памяти, а переменная hBmp – всего лишь идентификатор этого ресурса в общем списке ресурсов Windows. Поэтому в процессе выполнения программы будет удаляться только идентификатор, а ресурс, на который он указывает, будет «висеть» в памяти. Именно поэтому следует вызвать DeleteObject() для удаления объекта GDI. В предыдушем примере я не сделал этого по причине недосмотра.Большая часть примеров в этой статье предназначена для работы с DirectDraw – как мне кажется, наиболее востребованному элементу DirectX (кроме, естественно, Direct3D).
Надеюсь, мой стиль написания кода программ покажется удовлетворительным – он почти во всём подобен стилю, который использовали составители DirectX SDK. Вообще, многие пишут, как курица лапой – и предлагают свои творения на всеобщее обозрение. Ещё полезно заглянуть на страницу в нашем уважаемом Королевстве - http://www.delphikingdom.com/article/tassel.htm - это классика.Почему я не рекомендую использовать DelphiX.
Хочется поделиться с новичками своим мнением по поводу компонетов DelphiX и почему я не рекомендую их использовать. С одной стороны, DelphiX - это удобно – нет необходимости выполнять утомительный набор методов DirectX и длинных, как многоступенчатая ракета, констант наподобие DDENUMSURFACES_CANBECREATED. Однако давайте посмотрим – используется что-нибудь подобное в С++? Я не могу исследовать всю Сеть в поисках овета на такой вопрос, но, думается – нет. Почему? Такие наборы классов – это нестандартный подход. Допустим, вы потратили изрядно своего времени и досконально изучили DelphiX. Так вот, изучив всё ЭТО, вы в итоге не изучили сам DirectX. Второе – изученные классы обладают многим и позволяют писать реальные программы, но всё равно этот подход очень негибок – вы ограничены тем, что уже сделано. На этот счёт у меня есть веский аргумент – это Direct3D. Вот тут DelphiX уж точно не даст развернуться как следует – и это характерно и для других компонент DirectX, пусть и в меньшей мере. Третье – немного, но снижается быстродействие. Четвёртое – в классах DelphiX кроме непосредственных вызовов методов интерфейсов DirectX используются ещё и собственные функции – кто даст гарантию, что в них нет ошибок? В конце концов, именно за такие вот «примочки» Delphi не в почёте у С-программистов – они попросту надсмехаются над такими методами разработки программ. К сожалению, должен к ним присоединиться и я. Как же так, возмутятся многие. Компонентный подход – это ведь основа основ Delphi! Согласен, использование TMemo или TComboBox – это действительно удобный подход, да что там – превосходный, отличный подход! Но вот в случае с DirectX или чем-то подобным использовать такие средства разработки крайне нежелательно. Как бы вы отнеслись к компоненту TOpenGL? Или TWin32API? Вот так-то. DelphiX можно использовать как источник разных идей по реализации того или иного эффекта – перенося всё это в свою программу в виде отдельных функций или собственноручно написанных классов. Так что изучайте прямой API – для уверенности в завтрашнем дне и в собственной квалификации. Собственно, сами примеры Bounds В предыдущей статье я предложил вниманию только один пример для DirectDraw – простая реализация вывода спрайта поверх фона в задний буфер и перенос построенной картинки на экран. Спрайт не мог выйти за пределы экрана – и это не спроста. Если убрать досадное ограничение, то выяснится, что как только часть спрайта выходит за границы экрана (а фактически границы заднего буфера), то сразу исчезает полностью. Возвращение в прежнюю позицию восстанавливает вывод.
Когда я впервые столкнулся с этой проблемой, меня это неприятно поразило. Оказалось, что необходимо использовать интерфейс отсечения – IDirectDrawClipper. Однако он предназначен только для оконных приложений, в полноэкранном режиме от него нет никакого проку. Как уже было упомянуто, рекомендую программировать только полноэкранные приложения как наиболее быстродействующие, и забыть об оконных. Так как же быть – тупик?
К сожалению, даже в SDK нет примеров решения этого вопроса. Впрочем ответить на него не так уж сложно – нужно лишь понять, что хочет DirectDraw и как это преподнести.
Корень проблемы в том, что как только при копировании методом BltFast() часть поверхности выходит за край той поверхности, на которую она копируется (обычно задний буфер), вывод не осуществляется. В чём причина такого нелепого ограничения – думается, опять же в обеспечении наибольшего быстродействия. Например, вы планируете создать игру типа Tetris, а не скроллинговую стрелялку, и все ваши спрайты будут двигаться только в пределах экрана – но вот DirectDraw всё равно пришлось бы проверять их выход за границы, даже при отсутствии в этом необходимости. Хотя эту проблему можно было бы решить с помощью флагов при создании конкретной поверхности, но Microsoft этого не сделала. Ну что же, сделаем за неё эту работу.
Обратите внимание на четвёртый параметр метода IDirectDrawSurface7.BltFast() – это адрес структуры типа TRect. Для чего он нужен? Как известно, назначение струтуры TRect в GDI API – указание положения и размера какой либо области путём задания левого верхнего и правого нижнего угла. Так вот, эта структура позволяет указать DirectDraw о необходимости вывести не всё изображение спрайта, а лишь его часть:
Воспользуемся этой структурой и будем вместо всего спрайта выводить какую-то его облать – ту, которая будет видна на экране, а невидимая будет находиться вне области, описываемой структурой TRect. Т. о. будет создаваться лишь иллюзия пребывания спрайта за пределами экрана. Например, спрайт выходит за границы экрана, как это показано на самом первом рисунке. Тогда выводимая область должна быть такой (показана красным цветом):
Теперь повоображайте и нарисуйте на бумаге, как будут выглядеть выводимые части спрайта при различных его положениях за границами экрана.
Вот код, ответственный за вывод части изображения:
// Предполагаем, что края спрайта не выходят за границы экрана SetRect(rRect, 0, 0, SPRITE_WIDTH, SPRITE_HEIGHT); // Проверяем выход кра¸в, и если такая ситуация имеет место, то корректируем // положение области копирования на поверхности спрайта if nX < 0 then rRect.Left := -nX; if nY < 0 then rRect.Top := -nY; if nX + SPRITE_WIDTH > SCREEN_WIDTH then rRect.Right := SCREEN_WIDTH - nX; if nY + SPRITE_HEIGHT > SCREEN_HEIGHT then rRect.Bottom := SCREEN_HEIGHT - nY; |
nX + rRect.Left, nY + rRect.TopВот и всё. Запустите проект на выполнение – вы увидите, что теперь свободно отбражается даже часть спрайта. Выведите его полностью за пределы экрана – начнёт жужжать встроенный динамик компьютера – эта возможность введена для проверки правильности алгоритма. Кстати, если необходимо вывести всю поверхность изображения, вместо адреса структуры следует передать nil – как это сделано для фона. Scale Иногда при выводе может понадобится растянуть или сжать объект по осям или просто увеличить или уменьшить его – для подобных эффектов DirectDraw предоставляет метод IDirectDrawSurface.Blt(). Он является хотя и более медленным, чем BltFast() – однако при этом более функционален. Так вот, мы снова будем указывать с помощью структуры TRect область вывода изображения – но уже на поверхности-приёмнике данных. Изменяя её размеры, можно добиться пропорционального или непропорционального изменения масштаба изображения по осям X и Y. Думаю, нет надобности описывать действия которые происходят в процедуре OnDraw(). Замечу лишь, что на современных видеокартах с полной аппаратной поддержкой DirectDraw эффект масштабирования выглядит гораздо привлекательнее, чем на «ветеранах». Transparent При выводе изображения на экран нередко встаёт проблема затирания заднего фона, а говоря языком профессионалов – проблема вывода нерегулярных спрайтов. Нерегулярный спрайт – это обычный прамоугольный спрайт, который содержит маску для указания прозрачных участков изображения. Пиксели, принадлежащие этим участкам, при выводе игнорируются – создаётся иллюзия прозрачности. В GDI нет прямого способа вывести нерегулярный спрайт – для этого необходимо подготовить изображение и отдельно маску. В чём недостатки такого подхода? Их много. Во-первых, требуется иметь два изображения – это увеличивает объём данных в памяти и на диске. Во-вторых, скорость вывода данного изображения падает вдвое, а ведь GDI и без этого не славится своей скоростью. В третьих, это дополнительная забота того, кто готовит изображение в графическом редакторе. В своё время автор даже написал небольшую утилиту, которая создавала маску для выбранного изображения и записывала её в отдельный файл. Но теперь об этом можно забыть.
DirectDraw предоставляет удобный инструмент для задания маски прозрачности. Цвета пикселей, которые игнорируются, называются «цветовыми ключами». Каждая поверхность может иметь свои цветовые ключи, причём их может быть несколько. Следующий фрагмент кода создаёт и присоединяет к поверхности «цветовой ключ», цвет которого – чёрный.
var ddck: TDDCOLORKEY; begin ddck.dwColorSpaceLowValue := 0; ddck.dwColorSpaceHighValue := ddck.dwColorSpaceLowValue; pSprite.SetColorKey(DDCKEY_SRCBLT, @ddck); |
Поэтому можно смело присваивать 0 для чёрной маски в любом режиме. Однако, предположим, нам надо задать цветовой ключ в виде чистого синего цвета. Для 24- и 32-битного режима это можно сделать с помощью макроса (функции) из модуля windows.pas:
function RGB(r, g, b: Byte): COLORREF; begin Result := (r or (g shl 8) or (b shl 16)); end; |
Так, для задания цветового ключа в виде чистого синего цвета необходимо написать так:
ddck.dwColorSpaceLowValue := RGB( 0, 0, 255 ); ddck.dwColorSpaceHighValue := ddck.dwColorSpaceLowValue;Ну и в том же духе, в полном соответствии с теорией цвета. А теперь попробуйте задать цветовой ключ для 16-битового режима. Ничего не получится. Почему? Дело в том, что цвет пикселя хранится в ячейке длиной в 16 бит, а цветовых составляющих – 3, появляется лишний бит, который чаще отдаётся зелёному цвету, а иногда просто не используется. Формат, где теряется лишний бит, обозначается 5-5-5 (на каждую цветовую составляющую по пять бит, а не одному байту), другой формат обозначается 5-6-5 (на зелёную составляющую выделяется 6 бит ). Понятно, что задание цвета с помощью функции RGB() для таких форматов ни к чему ни приведёт.
В своё время я довольно долго промучился с этой проблемой, тем более что в имеющейся литературе ничего об этом не сказано. В конце концов решил, что необходимо написать аналогичную к RGB() функцию, но об этом немного позже. Давайте сначала выясним, какой же формат использует установленная на нашем компьютере видеокарта. DirectDraw позволяет узнать это с помощью функции IDirectDrawSurface7.GetPixelFormat(). Единственным параметром необходимо передать адрес структуры TDDPIXELFORMAT. Вот фрагмент соответствующего кода:
var ddpf: TDDPIXELFORMAT; begin ZeroMemory(@ddpf, SizeOf(TDDPIXELFORMAT)); ddpf.dwSize := SizeOf(TDDPIXELFORMAT); pSprite.GetPixelFormat(ddpf); |
Формат цветовых составляющих описывается в полях dwRBitMask, dwGBitMask и dwBBitMask структуры TDDPIXELFORMAT – но только в том случае, если битовое поле dwFlags содержит флаг DDPF_RGB – признак того, что поверхность создана в RGB-режиме. Значения полей dwRBitMask, dwGBitMask и dwBBitMask для режимов с разной глубиной палитры описываются в разделе dwRBitMask, dwGBitMask и dwBBitMask справочной службы DirectX SDK:
DDPF_RGB 16 R: 0x0000F800
G: 0x000007E0
B: 0x0000001F
A: 0x00000000
DDPF_RGB 16 R: 0x0000001F
G: 0x000007E0
B: 0x0000F800
A: 0x00000000
DDPF_RGB 16 R: 0x00007C00
G: 0x000003E0
B: 0x0000001F
A: 0x00000000
Запустите готовое приложение GetPixFormat из каталога DXCommon – и посмотрите, какой формат поверхности использует ваша карта в 16-битовом режиме. Скажу, что на компьютере с видеоакселератором GeForce 2 MX 420 получались значения из самой верхней ячейки – и это соответствует формату 5-6-5. По-моему, именно такой формат принят во всех современных видеокартах (заметьте, что во второй ячейке таблицы составляющие R и B переставлены местами). А вот, например, дедушка S3 Trio 3D/2X использует формат, описанный в нижней ячейке – опытным путём установлено, что это 5-5-5.
Вот как должен быть переписан макрос для формата 5-6-5:
function RGB565(r, g, b: Byte): COLORREF; begin Result := ((r shl 11) or (g shl 5) or b); end; |
А вот как должен выглядеть макрос для формата 5-5-5:
function RGB555(r, g, b: Byte): COLORREF; begin Result := ((r shl 10) or (g shl 5) or b); end; |
Как видно, последний бит не используется. Обратите внимание, что группа битов, отвечающих за красную и синию составляющую, в 16-битовом режиме поменялись старшинством. А формат, описанный во второй ячейке таблицы, наоборот, по старшинству схож с 24- и 32-битовым режимами. Подозреваю, что используется этот формат довольно редко.
Естетственно, что теперь максимальное значение, передаваемое в макросы (функции) RGB565() и RGB555(), соответствует значению 31, а для задания читого зелёного цвета в режиме 5-6-5 необходимо указать RGB565( 0, 63, 0 ), т. к. битов 6.
Для того, чтобы наша DirectDraw-программа без проблем работала в обоих форматах, необходимо проверить текущий формат, запомнить его и при задании цветового ключа для поверхности вызвать соответствующий макрос. Всё это и делается в приложении Transparent – надеюсь при его разборе у вас не возникнет проблем. Не забудьте при копировании методом BltFast() указать флаг DDBLTFAST_SRCCOLORKEY.
Fps
Ещё один полезный пример – вывод текста на поверхность DirectDraw – в виде значения fps. Сама компонента не обладает такими средствами – DirectDraw изначально создавался лишь для максимально быстрого копирования одного изображения на другое. Для вывода текста необходимо использовать GDI.
Как неоднократно упоминалось, GDI очень медленнен, и вывод текста – одна из функций, которая серьёзно может «притормозить» DirectDraw-программу. Поэтому необходимо пользоваться этой функцией как можно реже. Для взаимодействия DirectDraw c GDI введён простой метод IDirectDrawSurface7.GetDC(). Получив контектс, можно спокойно чертить в нём всеми мыслимыми функциями GDI. Метод IDirectDrawSurface7.ReleaseDC() переносит содержимое контекста в область памяти, занятую поверхностью DirectDraw и удаляет контекст.
Откройте файл проекта fps.dpr. Т. к. функция TextOut() уже занята, функцию, отвечающую за вывод текста, пришлось назвать менее звучно – OutText(). Я не буду подробно описывать её, надеюсь, всё понятно. Для ускорения работы программы я поступил так: для вывода текста используется отдельная поверхность – именно на неё и выводится текст средствами GDI. Затем всё время поверхность просто копируется на задний буфер – это осуществляется гораздо быстрее, чем постоянный вывод текста на задний буфер, а когда появляется необходимость изменить текст – он снова выводится на нашу отдельную поверхность. Потребность изменить текст появляется лишь раз в секунду.
Для вызова OutText() я использовал мультимедиа-таймер Windows. Значение fps наращивается при каждом построении кадра и обнуляется после вызова OutText().
И последнее. По-видимому, в операционной системе Windows 2000 функции GDI должны работать быстрее, т. к. эта ОС полностью 32-х разрядная. Но всё же рекомендую пользоваться описанным выше подходом.
Text
Ещё один пример вывода текста – но уже на задний буфер. Добавлен мною для полноты темы. Текст заданных размеров постоянно выводится на задний буфер без поверхности-посредника. При выводе текста я столкнулся с одной проблемой – это сглаживание краёв символов. В модуле windows.pas описана константа ANTIALIASED_QUALITY, но её задание в параметре fdwQuality функции CreateFont() ни к чему ни привело. Может быть, в Windows 9x и МЕ это значение не используется? Во всяком случае, константы ANTIALIASED_QUALITY и NONANTIALIASED_QUALITY в справке Delphi Help не описаны.
Sound
Эта программа – прямое продолжение моего первого примера по использованию DirectSound. Введено ряд усовершенствований:
1. Файл lowfunc.pas теперь полностью закончен и является практически прямым переводом файла wavread.cpp. Выражаю благодарность Max Morozov и iXania, которые помогли мне перевести некоторые сложные конструкции с языка C++ на Object Pascal, т. к. самому мне для этого не хватило квалификации. Теперь нет необходимости использовать отдельную динамическую библиотеку – весь код располагается в exe-файле. Всем спасибо.
2. Я решил написать небольшой класс TWave – он сам заботится об открытии звукового файла, чтении данных из него в звуковой буфер и проигрывании их. Функциональность класса не полная – это лишь пример. Благодаря ООП главный модуль main.pas серьёзно уменьшился, теперь для воспроизведения wav-файла средствами DirectSound достаточно написать:
var sound: TWave; begin sound := TWave.Create(); sound.OpenWaveFile(‘wavefile.wav’); sound.Play(); |
1. В Help-службе DirectX SDK 7 указаны такие возможные значения, которые могут быть помещены в переменную pdwVersion при вызове функции DirectXSetupGetVersion():
DirectX version Value pointed to by pdwVersion DirectX 1 0x00040001 DirectX 2 0x00040002 DirectX 3 0x00040003 DirectX 5.0 0x00040005 DirectX 6.0 0x00040006 DirectX 7.0 0x00040007
А вот в Help-службе DirectX SDK 8 указаны такие:
DirectX version Value pointed to by pdwVersion DirectX 1 0x00000000 DirectX 2 0x00000000 DirectX 3 0x00000000 DirectX 5.0 0x00040005 DirectX 6.0 0x00040006 DirectX 7.0 0x00040007 DirectX 8.0 0x00040008Зачем понадобилось обозначить версии 1, 2 и 3 как отсутствие DirectX – непонятно. Может быть, Microsoft посчитала, что эти ранние версии уже слишком устарели и не обеспечивают пользователя нужными мультимедиа-средствами? А раз так, то может быть лучше вообще известить об отсутствии DirectX? Может статься, что это просто ошибка в файле справки. Второй вариант более правдоподобен, первый – забавен. Используя файл directsetup.pas, мне не удалось экспортировать функцию DirectXSetupGetVersion() из библиотеки dsetup.dll. Дело в том, что она ищется в каталоге, по-видимому, указанном в ключе реестра HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\DirectXDrivers, но я не обнаружил такой ключ в своём реестре. Так что воспользоваться этим файлом не представилось возможным. К тому же структуры TDirectXRegisterAppW2 не существует – приехали! Я самостоятельно перевёл файл dsetup.h из SDK 8 в файл dsetup.pas, пытаясь максимально точно соблюдать синтаксис структур и параметро функций. Может быть, кто-то им воспользуется. Недостаток приведенного метода в том, что вам придётся постоянно «таскать» библиотеку dsetup.dll вместе с исходной программой. Заключение Написание подобных статей – хороший стимул к тщательному изучению DirectX. Скажу, что чем больше работаю с этим комплексом, тем больше разных нюансов всплывает на поверхность. Надеюсь, что мои усилия хоть как-то помогут остальным желающим освоить этого игрового «монстра». Я надеюсь продолжить изучение DirectX и как только получится создать что-то стоящее, попробую поделиться сделанным с остальными. Напоследок хочу выразить особую благодарность Антону Ржешевскому за его дельные советы в освоении DirectX и не только.
Скачать примеры: Samples2.Zip (106 K).
DirectX для начинающих. Считывание и запись.
Автор: Виктор Кода Привет всем, кто интересуется программированием под DirectX на языке Object Pascal! Как и обещал, я продолжаю искать новый материал по DirectX, переводить его на язык Object Pascal и представлять всеобщему вниманию. Недавно у меня появилась идея снятия скриншотов с экрана DirectDraw-программы и записи изображения в простой bmp-файл - некоторые игры позволяют это делать, и я решил последовать их примеру. Потом я наткнулся на другой интересный материал - речь шла о загрузке изображения из bmp-файла без использования функции LoadImage(). Поэтому тема статьи всецело посвящена работе с bmp-файлами на "низком уровне". Замечу, что это немного сложные вещи, но мы ведь сложностей не боимся, правда? Иначе непонятно, зачем тогда заниматься изучением DirectX вообще. Замечания Каждому примеру для нормальной работы необходимо, чтобы файл data.bmp находился в том же каталоге, что и исполняемый файл. Для экономии места в архиве я разместил этот файл в папке первого примера, поэтому вам надо будет скопировать его и в остальные папки. Немного изменилась реализация модуля ddutils.pas - теперь функция CreateSurface() не требует адрес главного интерфейса IDirectDraw, а создаёт и уничтожает локальный интерфейс. Возможно, более практично с точки зрения Pascal-программирования было бы объявить глобальный для модуля ddutils.pas интерфейс IDirectDraw, а для создания и удаления интерфейса воспользоваться секциями initialization и finalization модуля. Также немного изменился стиль написания программ, теперь он совсем грешит С-подобным кодом :-) FASTFILE1В этом примере я попытаюсь показать, как произвести загрузку растра из bmp-файла методом, отличным от того, что применялся в предыдущих уроках. Напомню, как в общих чертах выглядела схема загрузки ранее:
С помощью функции LoadImage() загружался растр и нам сообщался идентификатор загруженного растра в виде переменной типа HBITMAP; Функцией SelectObject() в контекст-источник выбирался созданный растр; Методом IDirectDrawSurface7.GetDC() создавался GDI-совместимый дескриптор контекста-приёмника и осуществлялась блокировка поверхности; Функцией GDI BitBlt() содержимое контекста-источника копировалось на контекст-приёмник. Методом IDirectDrawSurface7.ReleseDC() удалялся созданный контекст и осуществлялась разблокировка поверхности. В чём недостатки такого подхода? В универсальности. Не скажу, что функция LoadImage() очень медленная, но и очень быстрой она не является. Во всяком случае, программисты, писавшие её, не ставили своей задачей обеспечить максимальную скорость загрузки. Посмотрите справку по этой функции (файл win32sdk.hlp) - обилие параметров и констант, задаваемых при вызове, наводят на мысль о том, что она довольно "тяжеловесна". В частности, сказано, что с её помощью можно загружать не только растры из bmp-файлов разных форматов (в их число входят монохромные, 16- и 256-цветные палитровые файлы, а также беспалитровые 24-битные файлы), но и файлы иконок Windows и даже файлы, содержащие изображения курсоров. Естественно, всё это отрицательно сказывается на скорости загрузки - метод получается простым, но не самым эффективным. Поэтому часто программисты пишут собственные быстрые функции для загрузки файлов какого-то определённого формата. В этом примере я реализовал отдельную функцию, которая предназначена для загрузки данных из 24-битного беспалитрового файла формата bmp. Прежде чем приступить к рассмотрению работы функции, необходимо в общих чертах представить себе, каким образом записывается информация в bmp-файле. На рис. 1 показана структура беспалитрового 24-битного файла.
Рис.1. Структура файла BMP, не содержащего палитру
Хранящийся на диске файл DIB, обычно с расширением .bmp, как видно, начинается со структуры BITMAPFILEHEADER, позволяющей начать работу с файлом. Вот как эта структура описана в файле windows.pas:
tagBITMAPFILEHEADER = packed record bfType: Word; // Тип файла. Должен содержать 'BM' ($4d42) bfSize: DWORD; // Размер файла в байтах bfReserved1: Word; // Зарезервировано, должен быть нуль bfReserved2: Word; // Зарезервировано, должен быть нуль bfOffBits: DWORD; // Смещение от начала файла до гафических данных end; BITMAPFILEHEADER = tagBITMAPFILEHEADER; |
Следом за структурой BITMAPFILEHEADER следует стуктура BITMAPINFO:
tagBITMAPINFO = packed record bmiHeader: TBitmapInfoHeader; // Структура BITMAPINFOHEADER bmiColors: array[0..0] of TRGBQuad; // RGB-триплекс end; BITMAPINFO = tagBITMAPINFO; |
Фактически, стуктура BITMAPINFO включает в себя ещё одну структуру - BITMAPINFOHEADER:
tagBITMAPINFOHEADER = packed record biSize: DWORD; // Размер самой структуры в байтах biWidth: Longint; // Ширина растра в пикселях biHeight: Longint; // Высота растра в пикселях biPlanes: Word; // Количество плоскостей (всегда 1) biBitCount: Word; // Количество бит на 1 пиксель biCompression: DWORD; // Тип сжатия (BI_RGB - без сжатия) biSizeImage: DWORD; // Размер изображения в байтах (обычно 0) biXPelsPerMeter: Longint; // А эти данные biYPelsPerMeter: Longint; // нам вообще biClrUsed: DWORD; // никогда не biClrImportant: DWORD; // понадобятся :) end; BITMAPINFOHEADER = tagBITMAPINFOHEADER; |
Откройте файл проекта и найдите функцию LoadData(). Она вызывает другую функцию - LoadBitMap(). Я разместил её в файле ddutils.pas, вот её прототип.
function LoadBitMap(name: pchar; pbi: PBITMAPINFO): pointer; |
Вот как, например, открывается файл:
hFile := CreateFile(name, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if hFile = INVALID_HANDLE_VALUE then exit; |
ReadFile( hFile, bfh, sizeof( BITMAPFILEHEADER ), dwBytesRead, nil );Замечу, что вторым параметром функции ReadFile() передаётся сама структура, куда будут записаны данные, третьим - количество байт, которые надо прочитать. Четвёртые параметр должен присутствовать обязательно, в него функция запишет количество реально прочитанных байт. Для пущей надёжности можно сравнить это значение с размером структуры BITMAPFILEHEADER, и если значения не совпадают, объявить об ошибке. Далее считывается структура BITMAPINFOHEADER:
ReadFile( hFile, bi, sizeof( BITMAPINFOHEADER ), dwBytesRead, nil );Думаю, надо объяснить, почему здесь мы читаем только данные структуры BITMAPINFOHEADER, и не считываем массив bmiColors. Дело в том, что этот массив в структуре BITMAPINFO, там, куда мы её передадим позже, всё равно не используется. Однако он входит в состав общих графических данных, поэтому мы считаем его вместе с ними, а в структуре bi массив bmiColors оставим пустым. Далее идёт считывание графических данных. Прежде всего необходимо определить, какой размер они имеют:
// Определяем размер DIB dwDIBSize := GetFileSize( hFile, nil ) - sizeof( BITMAPFILEHEADER ) - sizeof( BITMAPINFOHEADER ); |
// Выделяем участок памяти result := pbyte(GlobalAllocPtr( GMEM_MOVEABLE, dwDIBSize )); |
После этого в память считываются битовые данные, формирующие картинку, и файл закрывается:
// Читаем данные DIB ReadFile(hFile, result^, dwDIBSize, dwBytesRead, nil); // Закрываем файл CloseHandle(hFile); |
Я подготовил простой bmp-файл, в котором хранится изображение для фона и десять "кадров", которые будут последовательно сменять друг друга. Как же загрузить эти данные на поверхности DirectDraw? Есть два пути:
Воспользоваться методами Lock() и Unlock() интерфейса IDirectDrawSurface7, и осуществлять прямое копирование данных функцией CopyMemory(). Это решение оптимально по скоростным характеристикам, но уж очень сложное: необходимо учитывать абсолютно все нюансы формата поверхности, на которую копируются данные - а их очень много, ведь формат меняется в зависимости от глубины цвета текущего видеорежима - 8, 16, 24, 32 бит. К тому же, выигрыш в этом случае может оказаться совсем небольшим. Использовать функцию GDI StretchDIBits(). Она предназначена для копирования в контекст данных, расположеных не в другом контексте, а находящихся просто в указаннном участке памяти. Может сложиться впечатление, что эта функция достаточно медленна - из-за угрожающей приставки "Stretch". Однако если будут копироваться участки битов, одинаковые по высоте и ширине, то в этом случае функция, думается, должна работать быстрее. Я решил использовать второй способ. Итак, первым делом создадим поверхность для фона:CreateSurface( g_pWallpaper, 640, 480 );
После этого получим контекст для поверхности и осуществим копирование функцией StretchDIBits(). В файле справки о методе IDirectDrawSurface7.GetDC() сказано, что он является надмножеством над методом IDirectDrawSurface7.Lock() - т. е. осуществляет те же операции, которые мы бы проделали при прямом копировании данных. Различие в том, что здесь DirectDraw учитывает особенности формата поверхности при создании контекста-приёмника. Думаю, нет необходимости дублировать эти операции - выигрыш в скорости может оказаться весьма сомнительным, т.к. код в библиотеке DirectDraw и без того максимально быстр.
if g_pWallpaper.GetDC(DC) = DD_OK then
begin
// Копируем битовый массив в контекст
StretchDIBits(DC,
0, 0, 640, 480,
0, 64, 640, 480,
pBits, bi,
0, SRCCOPY);
g_pWallpaper.ReleaseDC(DC);
end;
|
Далее 10 раз осуществляется копирование в отдельные поверхности массива g_pMovie. Опять же, необходимо учитывать, что строки растра перевёрнуты. После этого необходимо освободить участок системной памяти, где хранится битовый массив:
// Освободили битовый массив! pBits := nil; |
Например:
g_pBackBuffer.BltFast( x, y, g_pMovie[ frame ], nil, DDBLTFAST_WAIT );Однако можно третьим параметром указать общую data-поверхность, а четвертым - не nil, а область на этой поверхности:
g_pBackBuffer.BltFast( x, y, g_pDataSurface, arrayRect[ FRAME_01 ], DDBLTFAST_WAIT );Тогда можно не создавать отдельные поверхности и не заниматься копированием данных. Однако есть и недостатки. Например, память видеокарты должна быть достаточно большой - если памяти не хватит для размещения всей data-поверхности, DirectDraw разместит её в системной памяти, и процесс вывода изображения резко замедлится - вот вам и оптимизация! Также могут возникнуть проблемы с цветовыми ключами и корректным отображением спрайтов. В общем, решение половинчатое. PRINTSCREEN Заманчиво, когда в программе имеется возможность делать "снимки" экрана и сразу записывать их в файл. Этот пример ничем не отличается от предыдущих, за исключением того, что при нажатии на клавишу "Пробел" делается запись содержимого экрана в файл screen.bmp. Функция, которая проделывает эту работу, находится в файле pscreen.pas. Рассмотрим её.
Первым делом создаётся новый файл или открывается для перезаписи старый:
// созда¸м файл с заданным именем, в него будет производиться запись hFile := CreateFile(szFileName, GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, 0, 0); if hFile = INVALID_HANDLE_VALUE then begin CloseHandle(hFile); exit; end; // Затем нам необходимо получить данные о поверхности(здесь в функцию передан // дополнительный буфер): // подготавливаем структуру TDDSURFACEDESC2 ZeroMemory(@ddsd2, sizeof(TDDSURFACEDESC2)); ddsd2.dwSize := sizeof(TDDSURFACEDESC2); // получаем формат поверхности pSurface.GetSurfaceDesc(ddsd2); dwWidth := ddsd2.dwWidth; dwHeight := ddsd2.dwHeight; dwBPP := ddsd2.ddpfPixelFormat.dwRGBBitCount; // Структура ddsd2 используется дополнительно в методе // Lock()поверхности.Заблокировав поверхность, можно обратится к е¸ содержимому // для чтения данных: // блокируем поверхность DirectDraw if (FAILED(pSurface.Lock(nil, ddsd2, DDLOCK_WAIT, 0))) then exit; |
pPixels := pbyte(GlobalAllocPtr( GMEM_MOVEABLE, dwWidth * dwHeight * 3 ));Затем начинается главное. Т. к. формат пикселя поверхности в каждом из графических режимов различается, необходимо предусмотреть все особенности размещения данных. Бессмысленно подробно описывать все операции - они запутанны и сложны. Мне понадобилось некоторое количество времени, чтобы правильно перевести все операции с указателями с языка C++ в контекст Object Pascal. Операции с указателями на этом языке получаются довольно путаными, малейшая оплошность приводит к тому, что обычно в файл записывается не тот участок памяти (получается мешанина из пикселей), или запись вообще не происходит. Обратите внимание на такую строку:
pixel := PDWORD(DWORD(ddsd2.lpSurface) + i * 4 + j * ddsd2.lPitch)^;Здесь определяется цвет нового пикселя поверхности. ddsd2.lpSurface - это указатель на начало данных поверхности, а ddsd2.lPitch - шаг поверхности, учитывать его нужно обязательно. После того, как данные скопированы в массив, поверхность обязательно нужно разблокировать. Теперь можно начать запись данных в файл.
Для начала необходимо вручную подготовить структуры BITMAPFILEHEADER и BITMAPINFOHEADER. В последней надо указать ширину и высоту растра, а также разрядность пикселя. Тип сжатия должен быть BI_RGB - т. е. без сжатия. После этого с помощью API-функций Windows последовательно в файл записываются структуры BITMAPFILEHEADER, BITMAPINFOHEADER и далее - подготовленные данные из памяти. После записи файл необходимо закрыть, а память - освободить:
// закрываем файл CloseHandle(hFile); pPixels := nil; |
И последнее. Данная функция работает не совсем корректно - если открыть созданный файл в графическом редакторе, то под большим увеличением можно заметить ма-аленький цветовой артефакт - один стобик пикселей имеет не тот цвет. Решение этой проблемы я так и не смог найти.
Скачать примеры: DirectX3.zip (95K)DirectX (Игровой SDK) 1-2.
| Автор: Стас Бакулин |
| Сидит как то программер, налаживает свою пятилетнюю работу, обложился справочниками, доками, FAQ-ами.....неделю сидит - ни фига не получается. Мужик уже похудел весь, зарос щетиной... Входит в комнату его мама и говорит ему (с горечью в голосе): - Ну... все играешь? |
Модель компонентных объектов (СОМ)
Перед углублением и изнурительные подробности DirectDraw сначала несколько слов о модели компонентных объектов - кратко СОМ. Delphi использует объектно-ориентированный язык программирования Object Pascal. Дизайнеры Delphi решили сделать родные Delphi объекты полностью совместимыми с СОМ и OLE. Это большая новость для нас, потому что DirectDraw использует интерфейс СОМ и поэтому из Delphi получить к нему доступ достаточно просто.
Объекты СОМ подробно освещены в разделе Delphi. Но для того, чтобы сэкономить ваше время, предоставлю краткий обзор. В Delphi вы работаете с объектом СОМ практически так же, как и с другим объектом. Объекты СОМ выглядят по сути как обычные объекты Delphi. Они имеют методы, которые вы вызываете для доступа к их услугам. Тем не менее, они не имеют полей или свойств. Главным отличием является то, что вы вызываете метод Release вместо метода Free, если вы хотите освободить эти объекты.
Вы также никогда не создаете объект СОМ путем вызова конструктора. Вместо этого вы вызываете функцию в DirectDraw для создания главного объекта DirectDraw. Этот объект имеет дальнейшие методы, которые вы используете для создания других методов. Помимо этих двух вопросов вы можете фактически думать о них как об объектах Delphi.
Объекты СОМ DirectDraw определяются в довольно сложном файле-заголовке на С, который поставляется с Game SDK. Однако я перевел это в модуль импорта, который вы можете использовать в Delphi. Это файл DDraw.pas на сопровождающем CD-ROM. Для того, чтобы получить доступ к DirectDraw, просто добавьте DDraw в предложение uses.
DirectDraw
DirectDraw может оказаться довольно каверзным в использовании. На первый взляд он кажется простым; существует только несколько СОМ-классов и они не имеют большого количества методов. Однако DirectDraw использует записи для определения всех видов различных параметров при создании своих объектов. На первый взгляд они выглядят действительно устрашающе. Вы можете найти их в справочных файлах Game SDK, начиная с букв DD, например DDSurfaceDesc. Являясь API низкого уровня, существует множество опций и параметров, которые допускают разницу в спецификациях аппаратного обеспечения и возможностях. К счастью, в большинстве случаев можно проигнорировать множеством этих опций. Самой большой проблемой в момент написания этой книги является недостаток информации в GDK документации, которая описывает, какие комбинации опций разрешаются, поэтому для того, чтобы помочь вам найти путь через минное поле, эта глава поэтапно проходит по всем стадиям создания приложения DirectDraw. Я представляю код, который добавляется на каждом этапе и использует его для объяснения аспекта DirectDraw, также как и рабочий пример, на основании которого можно строить свои собственные программы.
Объект IDirectDraw
DLL с DirectDraw фактически имеет самый простой из интерфейсов. Она экспортирует только одну функцию: DirectDrawCreate. Вы используете эту функцию для создания СОМ-объекта IDirectDraw, который открывает остальную часть API. Таким образом, первое, что должен сделать пример - создать один из таких объектов. Вы делаете это в обработчике события OnCreate формы и разрушаете его в OnDestroy. Лучшим местом хранения объекта является приватное поле главной формы. Листинг 1 содержит базовый код для осуществления этого.
Листинг 1 Создание объекта IDirectDraw.
unit Uniti;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, DDraw;
type
TFormI = class (TForm)
procedure FormCreate (Sender: TObject);
procedure FormDestroy (Sender: TObject) ;
private
DirectDraw : IDirectDraw ; // главный объект DirectDraw
end;
var
Formi: TFormI;
implementation
procedure TFormI. FormCreate (Sender: TObject);
begin
{ создать СОМ-объект DirectDraw }
if DirectDrawCreate( nil, DirectDraw, nil ) <> DD_OK then
raise Exception. Create ( 'Failed to create IDirectDraw object' ) ;
end;
procedure TFormI. FormDestroy (Sender: TObject);
begin
{ создать СОМ-объект DirectDraw эа счет вызова его метода Release }
if Assigned ( DirectDraw ) then
DirectDraw. Release ;
end;
end.
|
Вы можете скачать этот тривиальный пример DDDemo1 здесь (пока не можете, я надеюсь это будет позже, прим. Lel). Он не делает что-либо очевидного, когда вы запускаете его, поэтому не ожидайте слишком многого. Я включаю его для того, чтобы показать, как мало кода требуется для создания и освобождения СОМ-объекта DirectDraw. Это действительно очень просто.
Коды возврата DirectDraw и исключения Delphi
Подавляющее большинство функций DirectDraw возвращает результирующий код целого типа с именем HResult, о котором вы можете думать как об integer. Файл DDraw.pas имеет все возможные константы ошибок, занесенные в список, а справочный файл Game SDK указывает на возможные коды ошибки, возвращаемые каждой функцией. Вы можете проверить результаты этих функций, и в болыпинстце случаев возбудить исключение, если результат отличается от DD_OK.
Однако имеется ряд проблем с использованием исключений, поскольку вы переключаетесь на специальный экранный режим. Это означает, что вы не способны видеть Delphi IDE, когда он разрушается или прерывается в момент исключения, и ваша программа кажется замороженной. Установка точки прерывания обычно приводит в результате к одной и той же проблеме: приложение останавливается как раз в точке прерывания, но вы не имеете возможность увидеть Delphi. Добро пожаловать в программирование игр в среде Windows! Я обсуждаю это более подробно несколько позже.
Переключение на полноэкранный режим
Следующее, что необходимо сделать, - это переключить дисплей в режим перелистывания страниц. Когда вы это делаете, становится видимым только ваше приложение. Оно занимает весь экран. Любые другие приложения Windows, которые находятся ц режиме выполнения, подобные Windows Explorer, продолжают работать и могут записывать выходные данные на то, что они считают экраном. Вы не видите, как выглядят выходные данные, потому что другие приложения все еще используют GDI для выходных данных, которому ничего не известно о DirectDraw. Но вам вовсе нет необходимости беспокоиться об этом. GDI будет продолжать беспечно писать в экранную память, хотя вы вдействительности не сможете увидеть его выходные данные.
Путем переключения в специальный режим отображения данных вы занимаете весь экран. Как правило, вы можете запускать множество регулярных приложений среды Windows в одно и то же время; их окна перекрываются и благодаря GDI дела идут прекрасно. Но что произойдет, если вы попытаетесь запустить два и более полноэкранных DirectDraw-приложений в одно и то же время? Ответ - только одному разрешен доступ к полному экрану. DirectDraw управляет этим, предполагая, что вы имеете исключительный доступ к экранной карте перед изменением режима. Вы сделаете это установкой коиперативнчгн уровня объекта DirectDraw в Exclusive. DirectDraw поддерживает эксклюзивный уровень доступа только для одного приложения одновременно. Если вы попытаетесь получить эксклюзивный доступ и какое-нибудь другое приложение уже его имеет, вызов не удастся. Подобным же образом, если вы попытаетесь изменить режимы отображения данных без приобретения эксклюзивного доступа, этот вызов не удастся. Таким образом, попытайтесь получить эксклюзивный доступ и затем переключите режимы отображения.
Здесь необходимо отметить, что вы должны предоставить описатель окна SetCooperativeLevel. DirectDraw изменяет размеры этого окна автоматически, так что оно заполняет экран в новом режиме отображения данных. Вы должны передать описатель формы в SetCooperativeLevel. Ввиду того, что описатель окна не был создан до времени вызова OnCreate, вы должны все это сделать и событии OnShow. Листинг 2 показывает, как это сделать.
Листинг 2 Переключение в полноэкранный режим в OnShow.
procedure TForml.FormShow(Sender: TObject);
begin
if DirectDraw.SetCooperativeLevel(Handle,
DDSCI_EXC: LUSIVE or DDSCI_FUbbSCREEN ) <> DD_OK then
raise Exception.Create('Unable to acquire exclusive full-screen access');
if DirectDraw.SetDisplayMode(640, 480, 8) <> DD_OK then
raise Exception.Create('Unable to set new display mode');
end;
|
Пока все в порядке. Ничего тут сложного нет. Если вы запустите пример прямо сейчас, ваше приложение переключит режимы и вы увидите, как форма заполнит экран. Если вы передвинете или измените ее размеры, вы увидите за ним Delphi. Вы все еще смотрите на поверхность вывода GDI. GDI может благополучно выводить данные в этих различных режимах, так что вы увидите свои обычные приложения Windows так долго, сколько эта поверхность будет оставаться на переднем плане. Но ввиду того, что вы создаете приложение с мелькающими страницами, это не совсем то, что нам нужно. Директория DDDemo2 содержит изложенные примеры.
Добавление обработчика исключений приложения
Как я уже упоминал ранее, тот факт, что DirectDraw занимает полный экран может вызвать проблему с обработкой исключений. Когда исключение возбуждается, по умолчанию Delphi IDE попадает в отладчик программы и приостанавливает исполнение программы, устанавливая кодовое окно на строке, содержащей ошибку. Проблема заключается в том, что когда происходит мелькание страниц вы, вероятно, не сможете увидеть IDE и приложение будет выглядеть замороженным. Еще хуже, если вам удастся продолжить исполнение, или на опции IDE окажется выключенной Break on exception (Останавливаться, если возбуждено исключение), то вы можете не увидеть окна сообщения, которое появляется с сообщением исключения.
Один из способов избежать этот сценарии отменить маркер на флажке Break on exception в IDE (TooIsjOptions menu) и установить в своем приложении специальный обработчик исключений приложения. Этот обработчик должен переключаться на поверхность GDI перед тем, как показать сообщение исключения. Это намного легче, чем может показаться. Все, что вам необходимо сделать, - создать собственный private-метод в форме и присвоить его AppHcation,OnException в OnCreate формы. Не забывайте установить его обратно в nil в OnDestroy. Новый описатель может использовать метод SwitchToGDISurface объекта IDirectDraw перед вызовом MessageDIg. Листинг 3 показывает обработчик исключения.
Листинг 3 Обработчик исключений приложения.
procedure TForml.ExceptionHandler(Sender: TObject; E: Exception);
begin
if Assigned(DirectDraw) then
DirectDraw.FlipToGDISurface;
MessageDIgt E.message, mtError, [mbOK], 0);
end;
|
Для того, чтобы устаноцить описатель исключения мы добаиим следующую строку в OnCreate:
Application-OnException :== ExceptionHandler; |
Помните, что нужно выключить Break on exception (в TooIsfOptions). Как только вы наберетесь больше опыта, вы сможете включить эту опцию снова для специфических заданий отладки. Однако, если ваше приложение вызовет исключение, пока поверхность GDI невидима, IDE возьмет свое и вы ничего не увидите. Нажатие F9 должно вызвать повторное исполнение, а нажатие Ctrl-F2 вернет приложение в исходное состояние и возвратить вас в Delphi.
DirectX (Игровой SDK) 3-4.
Автор: Стас Бакулин Поверхности отображения Теперь вы готовы создавать поверхности отображения. В DirectDraw поверхность отображения представляет собой линейную область экранной памяти, к которой можно получить непосредственный доступ для манипуляций. Поверхность отображения, которую вы видите на экране, называется основной поверхностью. Она представляет память видимого кадрового буфера на карте отображения. Вы также можете иметь невидимые поверхности, которые определяются как внеэкранные, или оверлейные поверхности. Подобное может существовать либо в регулярной системной памяти, либо во внеэкранной области памяти на самой графической карте. Для того, чтобы создать ситуацию с мелькающими страницами, необходима основная поверхность и, по крайней мере, одна внеэкранная поверхность для осуществления отображения. Для того, чтобы внеэкранная поверхность могла появляться и исчезать на экране, онадолжна находится в видеопамяти. Тем не менее, DirectDraw пытается создать поверхности в видеопамяти по умолчанию, поэтому нет необходимости предпринимать что-либо специально. Существует способ для создания основной поверхности и одной и более сменных поверхностей в одно и то же время за счет создания комплексной поверхности. Еще один аспект в создании комплексной (составной) поверхности заключается в том, что вы можете освободить все поверхности в комплексной цепи сменных поверхностей путем высвобождения самой комплексной поверхности. Для примера создадим комплексную поверхность посредством одной вспомогательной буферной поверхности. Фоновые поверхности, которые создаются в качестве части комплексной, известны как Неявные поверхности. Существует большое число операций, которые вы не сможете осуществить с помощью Неявных поверхностей, например, отсоединить их от основной поверхности или освободить их независимо от основной поверхности. Однако, комплексные поверхности намного проще создавать, потому что DirectDraw создает фоновые буфера и соединяет их с основной поверхностью. В этой связи я должен затронуть вопрос сложности DirectDraw, поскольку необходимо заполнять поля и записи TDDSurfaceDesc. Если вы прочитаете об этом и справке DirectDraw, вы сможете увидеть, что все это выглядит довольно ужасно! Но как я уже говорил, вы можете счастливо игнорировать большинство из этих полей. Листинг 4 представляет код, который необходимо добавить в обработчик OnShow для создания комплексной поверхности.Листинг 4 Создание комплексной поверхности.
{ заполнить описатель DirectDrawSurface перед созданием поверхности }
FillChar(DDSurfaceDesc, Si2e0f(DDSurfaceDesc), 0);
with DDSurfaceDesc do
begin
dwSize := SizeOf(DDSurfaceDesc);
dwFlags := DDSD_CAPS or DDSD_BACKBUFFERCOUNT;
ddSCaps.dwCaps := DDSCAPS_COMPLEX or DDSCAPS FLIP or
DDSCAPS_PRIMARYSURFACE;
dwBackBufferCount: = 1;
end;
|
Листинг 7 Тестирование нажатия клавиш Escape и F12.
procedure TForml.ForinKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
// если нажаты клавиши Escape или F12, завершить приложение
case Key of
VK_ESCAPE, VK_F12: Close;
end;
end;
|
Листинг 8 Доступ к поверхности фонового буфера.
{ получить фонов зй буфер }
DDSCaps.dwCaps: = DDSCAPS_BACKBUFFER;
if PrimarySurface.GetAttachedSurface(DDSCaps, BackBuffer) <> DD_OK then
raise Exception.Create('Failed to get back buffer surface');
|
Листинг 9 "Традиционный" код для проверки и восстановления поверхности.
repeat
DDResult := PrimarySurf асе.Flip(nil, 0);
case DDResult of
DD_OK: break;
DDERR_SURFACELOST:
begin
DDResult := PrimarySurface.Restore();
if DDResult <> DD_OK then
break;
end;
else
if DDResult <> DDERR_WASSTILLDRAWING then
break
end;
until false;
|
Самое надоедливое то, что вам необходим подобный код практически для каждого вызова метода IDirectDrawSurface. Всякий раз, когда спецификация вызова в справке Game SDK содержит DERR_SLJRFACELOST в качестве возможного результата, это необходимо. Но Pascal-структурированный язык высокого уровня, не так ли? Таким образом, почему бы не написать небольшой сервисный метод для оказания такой помощи? Вот этот метод с именем одного из моих любимых шоу. (Оно не дает возможности себя забыть!) Оно представлено в листинге 10.
DirectX (Игровой SDK) 5-6.
Автор: Стас БакулинЛистинг 10 функция MakeltSo для оказание помощи в восстановлении поверхности.
function TForinl.MakeltSo(DDResult: HResult): boolean;
begin
{ утилита для предоставления помощи в восстановлении поверхностей }
case DDResult of
DD_OK: Result := true;
DDERR_SURFACELOST: Result := RestoreSurfaces <> DD_OK;
else
Result := DDResult <> DDERR_WASSTILLDRAWING;
end;
end;
|
Последний метод иосстанаиливает поцерхность i3 случае необходимости и затем вызывает функцию RestoreSurface, которую я вам сейчас представлю. Но сначала вот как следует ее использовать, применяя Flip, как в предыдущем примере:
repeat ... until MakeltSo(PrimarySurf асе.Flip(nil, DDFblP_WAIT)); |
Листинг 11 Необязательная MakeltSo, которая вызывает исключения.
function TFormI.MakeltSo(DDResult: HResult): boolean;
begin
{ утилита для предоставления помощи в восстановлении
поверхностей - версия с исключениями }
Result := false;
case DDResult of
DD_OK: Result := true;
DDEKR_SURFACELOST: if RestoreSurfaces <> DD_OK then
raise Exception.Create('MakeltSo failed');
else if DDResult <> DDERR_WASSTILLDRAWING then
raise Exception.Create('MakeltSo failed');
end;
end;
|
Листинг 12 Восстановление и перерисовка поверхности DirectDraw.
function TFormI.RestoreSurfaces: HResult;
begin
{ вызывается MakeltSo, если поверхности "потерялись" -
восстановить и перерисовать их }
Result := PrimarySurface.Restore;
if Result = DD_OK then
DrawSurfaces;
end;
|
Листинг 13 Данная процедура заполняет основную поверхность голубым цветом и выводит текст "Primary surface" (Основная поверхность) в центре слева. Фоновый буфер закрашивается в красный цвет и содержит текст "Back buffer" (Фоновый буфер) в центре справа.
procedure TForm1.DrawSurfaces;
var
DC: HDC;
ARect: TRect;
DDCanvas: TCanvas;
ATopPos: integer;
begin
// fill the primary surface with red and the back buffer with blue
// and put some text on each. Using a canvas makes this trivial.
DDCanvas := TCanvas.Create;
try
// first output to the primary surface
if PrimarySurface.GetDC(DC) = DD_OK then
try
ARect := Rect(0, 0, 640, 480);
with DDCanvas do
begin
Handle := DC; // make the canvas output to the DC
Brush.Color := clRed;
FillRect(ARect);
Brush.Style := bsClear; // transparent text background
Font.Name := 'Arial';
Font.Size := 24;
Font.Color := clWhite;
ATopPos := (480 - TextHeight('A')) div 2;
TextOut(10, ATopPos, 'Primary surface');
end;
finally
// make sure we tidy up and release the DC
DDCanvas.Handle := 0;
PrimarySurface.ReleaseDC(DC);
end;
// now do back buffer
if BackBuffer.GetDC(DC) = DD_OK then
try
with DDCanvas do
begin
Handle := DC; // make the canvas output to the DC
Brush.Color := clBlue;
FillRect(ARect);
Brush.Style := bsClear; // transparent text background
Font.Name := 'Arial';
Font.Size := 24;
Font.Color := clWhite;
TextOut(630 - TextWidth('Back buffer'), ATopPos, 'Back buffer');
end;
finally
// make sure we tidy up and release the DC
DDCanvas.Handle := 0;
BackBuffer.ReleaseDC(DC);
end;
finally
// make sure the canvas is freed
DDCanvas.Free;
end;
end;
|
В предыдущих примерах форма была явно видима, заполняя собой основную поверхность. Однако, вы не хотите, чтобы пользователь видел форму. Это приложение смены страниц и оно рисует по всему экрану. Поэтому вы должны предотвратить отображение формы на экране. Также необходимо избавиться от системного меню и неклиентских клавиш. Все это можно достичь просто установкой BorderStyle формы в bsNone в методе Foi-rnCreate. Вам также не нужен и курсор, поэтому установите его в crNone. Добавьте эти три строки к FormCreate:
BorderStyle := bsNone; Color := clBlack; Cursor := crNone; |
Листинг 14 Обработчик события OnPaint просто вызывает DrawSurfaces.
procedure TForml.FormPaint(Sender: TObject); begin // рисовать что-нибудь на основной поверхности и на фоновом буфере DrawSurfaces; end; |
Листинг 15 Объект полотна DirectDraw в Delphi.
unit DDCanvas;
interface
uses Windows, SysUtils, Graphics, DDraw;
type
TDDCanvas = class(TCanvas)
private
FSurface: IDirectDrawSurface;
FDeviceContext: HDC;
protected
procedure CreateHandle; override;
public
constructor Create(Asurface: IDirectDrawSurface);
destructor Destroy; override;
procedure Release;
end;
implementation
constructor TDDCanvas.Create(Asurface: IDirectDrawSurface);
begin
inherited Create;
if Asurface = nil then
raise Exception.Create('Cannot create canvas for NIL surface');
FSurface := Asurface;
end;
destructor TDDCanvas.Destroy;
begin
Release;
inherited Destroy;
end;
procedure TDDCanvas.CreateHandle;
begin
if FDeviceContext = 0 then
begin
FSurface.GetDC(FDeviceContext);
Handle := FDeviceContext;
end;
end;
procedure TDDCanvas.Release;
begin
if FDeviceContext <> 0 then
begin
Handle := 0;
FSurface.ReleaseDC(FDeviceContext)
FDeviceContext := 0;
end;
end;
end.
|
DirectX (Игровой SDK) 7.
Автор: Стас Бакулин Использование класса DDCanvas. Для того, чтобы использопать этот класс, следует скопировать модуль DDCanvas.pas ц каталог Lib, который находится в каталоге Delphi 3.0, или и другой каталог, обозначенный в пути поиска библиотеки.Помните ли вы злополучное взаимное исключение Win, которое приостанаилипает многозадачную работу? Хорошо, я еще раз подчеркну необходимость освобождения DC. Класс TDDCanvas имеет и использует в своих целях метод Release. Всегда заворачивайте любой доступ к полотну в блок try..finally, например:
try
DDCanvas.TextOut(0, 0, 'Hello Flipping World!');
{и т.д. }
finally
DDCanvas.Release;
end;
|
Или, как я часто делаю, используйте конструкцию with для того, чтобы сэкономить время набора:
with DDCanvas do
try
TextOuK 0, 0, 'Hello Withering World!');
{и т.д. }
finally
Release;
end;
|
Итак, теперь вы можете добавить пару таких полотен в объявления формы, создавая их в FormShow, например:
{ создать два TDDCanvas для наших двух поверхностей }
PrimaryCanvas := TDDCanvas.Create(PrimarySurface);
BackCanvas := TDDCanvas, Create(BackBuffer);
|
Освободите их в FormDestroy перед тем, как освободить поверхности:
{ освободить объекты TDDCanvas перед освобождением поверхностей }
PrimaryCanvas.Free;
BackCanvas.Free;
|
Листинг 16 DrawSurfaces использует объекты TDDCanvas.
procedure TFormI.DrawSurfaces;
var
ARect: TRect;
ATopPos: integer;
begin
// вначале выводить на основную поверхность.
ARect := Rect(0, 0, 640, 480);
with PrimaryCanvas do
try
Brush.Color;
= cIRed;
FillRect(ARect);
Brush.Style: <= bsClear;
Font.Name: = ' Arial ';
Font.Size := 24;
Font.Color := clWhite;
ATopPos := (480 - TextHeight('A')) div 2;
Text0ut(10, ATopPos, 'Primary surface');
finally
// убедиться, что мы сразу же освободили DC,
// поскольку Windows замораживается, пока мы владеем DC.
Release;
end;
// теперь работаем с фоновым буфером
with .BackCanvas do
try
Brush.Color: = clBlue;
FillRecK ARect);
Brush, Style := bsClear;
Font.Name := 'Arial';
Font.Size. = 24;
Font.Color := clWhite;
Text0ut(630 - TextWidth('Back buffer'), ATopPos, 'Back buffer');
finally
// убедиться, что DC освобожден
Release;
end;
end;
|
Листинг 17 Сервисная процедура DDReLoadBitmap для загрузки изображений.
procedure DDReLoadBitmap(Surface: IDirectDrawSurface; const BitmapName: string);
var
Bitmap: HBitmap;
begin
// попытаться загрузить изображение как ресурс;
// если это не удается, то как файл
Bitmap := Loadimage(GetModuleHandle(nil), PChar(BitmapName),
IMAGE__BITMAP, 0, 0, LR_CREATEDIBSECTION);
try
if Bitmap = 0 then
Bitmap := Loadimage(0, PChar(BitmapName), IMA.GE_BITMAP,
0, 0, LR_LOADFROMFILE or LR_CREATEDIBSECTION);
if Bitmap = 0 then
raise Exception.CreateFmt('Unable to load bitmap Is', [BitmapName]);
DDCopyBitmap(Surface, Bitmap, 0, 0, 0, 0);
finally
DeleteObject(Bitmap);
end;
end;
|
Листинг 18 Сервисная процедура для копирования растрового изображения на поверхность.
procedure DDCopyBitmap(Surface: IDirectDrawSurface; Bitmap: HBITMAP;
х, y.Width, Height: integer);
var
ImageDC: HDC;
DC: HDC;
BM;
Windows.TBitmap;
SurfaceDesc: TDDSurfaceDesc;
begin
if (Surface = nil) or (Bitmap = = 0) then
raise Exception.Create('Invalid parameters for DDCopyBitmap');
// убедиться, что поверхность восстановлена.
Sигfасе.Restore;
// выбрать изображение для memoryDC, чтобы его использовать.
ImageDC: = CreateCompatibleDC(0);
try
Select0bject(ImageDC, Bitmap);
// получить размер изображения.
Get0bject(Bitinap, Size0f(BM), @BM);
if Width = 0 then
Width := = BM.bmWidth;
if Height = = 0 then
Height := = BM.bmHeight;
// получить размер поверхости.
SurfaceDesc.dwSize := SizeOfC SurfaceDesc);
SurfaceDesc.dwFlags := DDSD_HEIGHT or DDSDJWIDTH;
Surf ace.GetSurfaceDesc(SurfaceDesc);
if Surf ace.GetDC(DC) <> DD_OK then
raise Exception.Create('GetDC failed for DirectDraw surface^ )
try
StretchBlt(DC, 0, 0, SurfaceDesc.dwWidth, SurfaceDesc.dwHeight,
ImageDC, x, y.Width, Height, SRCCOPY);
finally
Surface.ReleaseDC(DC);
end;
finally
DeleteDC(ImageDC);
end;
end;
|
Листинг 19 DrawSurface без кода отрисовки фоновой поверхности.
procedure TFormI DrawSurfaces;
var
ARect: TRect;
ATopPos: integer;
begin
// вывод на основное полотно.
ARect := Rect(0, 0, 640, 480);
with PrimaryCanvas do
try
Brush.Color := clBlue;
FiliRect(ARect);
Brush.Style := = bsClear;
Font.Name := = * Arial';
Font.Size := = 24;
Font.Color := clWhite;
ATopPos: ^(480 - TextHeight('A* ) ) div 2 ;
Text0ut(10, ATopPos, 'Primary surface');
finally
// убедиться, что мы сразу же освободили DC,
// поскольку Windows замораживается, пока мы владеем DC.
Release;
end;
{ загрузить изображение в фоновый буфер }
DDReloadBitmap(BackBuffer, GetBitiilapName);
end;
|
Я добавил объект палитры к объявлению формы, загрузил его в FormShow и присоединил объект палитры к основной поверхности следующим образом:
{ загрузить палитку иэ растрового изображения
и присоединить ее к основной поверхности }
DDPalette := DDLoadPalette(DirectDraw, GetBitmapName);
PrimarySurface, SatPalette(DDPalette);
|
Создав, вы должны освободить его из основной поверхности в FormDestroy:
{ освободить DD-палитру }
if Assigned(DDPalette) then
DDPalette.Release;
|
Итак, за работу. Создайте эту дополнительную поверхность и назовите ее Image (изображение). Добавьте ее в объявление формы. Это как раз и есть IDirectDrawSurface, поэтому нет необходимости представлять здесь эту тривиальную строку кода. Затем добавьте код в FormShow, который создает растровое изображение. Используйте DDLoadBitmap, это только одна строка! Вот она:
Image := DDLoadBitmap (Directdraw, GetBitmapName, О, О);Помните, что вам необходимо пополнить метод RestoreSurfaces и тогда вы получите новую неявную поверхность. Если восстановление основной памяти поверхности пройдет нормально, попытайтесь восстановить поверхностную память Image. Если оба типа восстановлений будут иметь место, вызовите DrawSurfaces, как показано в листинге 20.
Листинг 20 Восстановление всех поверхностей.
function TFormI.RestoreSurfaces: HResult;
begin
Result := primarySurface.Restore;
if Result = DD OK then
begin
Result := Image - Restore;
if Result = DD_OK then
DrawSurfaces
end;
end;
|
Direct Show и Delphi.
Автор: Азиз (JINX).
На этот раз речь пойдет о DirectShow. Для чего нам может понадобиться DirectShow? DirectShow - это архитектура для воспроизведения, перехвата и обработки потоков мультимедиа. Звучит туманно? Поясняю - c помощью этого API можно: проигрывать мультимедийные файлы различного формата, такие как MPEG (Motion Picture Experts Group), AVI (Audio-Video Interleaved), MP3 (MPEG Audio Layer-3), DVD и конечно WAV; перехватывать видео-поток с различного рода TV-карт, видеокамер и т.п.; создавать нестандартные обработчики мультимедиа-потоков и свои собственные форматы файлов (что, впрочем, вряд ли понадобится простым смертным); обращаться непосредственно к видео и аудио потокам, чтобы выводить их на Surface DirectDraw (что для нас как раз интересно).Звучит заманчиво. Но для чего это может понадобиться, спросите Вы, вспоминая родной и привычный MediaPlayer. Представьте себе, что Вы запрограммировали трехмерный мир, с анимированными спрайтами, трехмерными объектами и т.п. И в отсвете выстрелов очередной "бродилки-стрелялки", пораженный пользователь нового шедевра, видит видео клип, воспроизводимый прямо на костях очередного пораженного монстра. :-) Каково? Убедил? Тогда продолжим. Кстати, раз уж упомянули DirectDraw - DirectShow интегрирован с DirectX так, что использует DirectDraw и DirectSound для вывода изображения и звука, и, при наличии аппаратного ускорения, автоматически им воспользуется.
Для работы с DirectShow Вам понадобятся:
иметь некоторое представление о технологии COM (Component Object Model) - хотя быть знатоком этой технологии вовсе не обязательно - просто достаточно знать, что для получения COM-интерфейса нужно вызвать QueryInterface; скачать заголовочные файлы DirectShow API, переведенные на Delphi в рамках проекта JEDI - (http://www.delphi-jedi.org/DelphiGraphics/DirectX/DX5Media.zip) и либо поместить их в каталог Delphi\Lib либо добавить путь к каталогу, в котором они находятся в установках Delphi Library Path. DirectShow скорее всего уже установлен на Вашем компьютере - он входит в стандартную поставку Windows 9x, Windows NT 4 (Service Pack 3 и выше), Windows 2000 (если Вы программируете для UNIX или DOS - то я вообще не понимаю зачем Вы читаете эту статью).Основы DirectShow.
В концепции DirectShow мультимедийные данные - это поток, который проходит через несколько обрабатывающих блоков. Блоки, обрабатывающие поток данных, передают данные по цепочке друг другу, таким образом можно представить себе несколько "устройств", каждое из которых выполняет какую-то обработку данных и передает их соседнему "устройству". Эти "устройства" или "блоки обработки" данных называют фильтрами. Цепочка, по которой передаются данные, содержит несколько фильтров, связанных определенным образом.
В DirectShow имеются готовые фильтры, из которых, словно из детских кубиков, программист может выстроить ту или иную цепочку обработку данных, кроме того, конечно, можно создать свои, нестандартные фильтры.
Для создания такой "цепочки обработки" (которая, кстати, официально называются Filter Graph - "граф фильтров" или, в несколько вольном переводе - "схема соединения фильтров"), так вот для создания схемы соединения фильтров, предназначен самый базовый и лежащий в основе всех основ компонет DirectShow, под названием Filter Graph Manager - Менеджер Графа Фильтров.
Например, программа показывающая видео из AVI-файла может построить такой граф фильтров:

В этом примере пять фильтров, первый (File Source) просто читает данные с диска, второй фильтр (AVI Splitter) разделяет данные на кадры и передает упакованные видео данные фильтру AVI Decompressor, который их распаковывает и передает фильтру Default DirectSound Device, выводящему звук. AVI Decompressor передает распакованные данные фильтру Video Renderer, который выводит кадры видео на экран.
Фильтры делятся на три типа:
Фильтры-источники (Source filters) - эти фильтры просто получают данные из какого-то источника, с диска (как фильтр File Source (Async) на рисунке), с CD или DVD дисковода или с TV- карты или карты, к которой подключена цифровая видеокамера. Фильтры-преобразователи (Transform filters) - эти фильтры как видно из названия преобразуют поток данных, проходящий через них каким-либо образом, например - разделяет поток данных на кадры, производят декомпрессию и т.п. На нашем рисунке к таким фильтрам относятся AVI Splitter и AVI Decompressor. Фильтры вывода (Renderer filters) - фильтры, которые получают полностью обработанные данные и выводят их на монитор, звуковую карту, пишут на диск или выводят на еще какое-нибудь устройство. Итак из фильтров-кубиков можно высстраивать граф. Делается это с помощью интерфейса IGraphBuilder. Создать объект типа IGraphBuilder можно так:CoCreateInstance(CLSID_FilterGraph,nil,CLSCTX_INPROC_SERVER, IID_IGraphBuilder,MyGraphBuilder);Здесь переменная MyGraphBuilder имеет тип IGraphBuilder; идентификатор класса CLSID_FilterGraph и IID_IGraphBuilder обьявлены в файле DShow.pas, поэтому не забудьте добавить
uses DShow.pasИтак, интерфейс IGraphBuilder получен. Можно построить граф фильтров, такой, какой нам нужно. Впрочем, все не так сложно, IGraphBuilder достаточно интеллектуален, он может сам, автоматически, построить граф, в зависимости от того какие файлы мы собираемся воспроизводить. Интерфейс IGraphBuilder имеет метод RenderFile, который получает имя файла в качестве параметра и, в зависимости от типа файла (которое определяется по расширению и по специальным сигнатурам в файле), сканирует реестр, в поисках необходимой для построения графа информации, создает необходимые фильтры и строит граф, предназначенный для воспроизведения файлов этого типа (WAV, AVI, MP3, MPG и т.д.). После построения графа DirectShow готов к воспроизведению. Для управления потоком данных через граф обработки предназначен интерфейс IMediaControl - он имеет методы Run, Pause и Stop (названия говорят сами за себя)
Давайте попробуем все это на примере:
uses
...DShow, ActiveX, ComObj;
var
MyGraphBuilder: IGraphBuilder;
MyMediaControl: IMediaControl;
begin
CoInitialize(nil);
{получаем интерфейс IGraphBuilder}
CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, MyGraphBuilder);
{вызываем RenderFile - граф фильтров строится автоматически}
MyGraphBuilder.RenderFile(cool.avi',nil);
{получаем интерфейс ImediaControl}
MyGraphBuilder.QueryInterface(IID_IMediaControl, MyMediaControl);
{Примечание - MyMediaControl - переменная типа IMediaControl}
{проигрываем видео}
MyMediaControl.Run;
{ждем пока пользователь не нажмет ОК
(видео воспроизводится в отдельном (thread) потоке)}
ShowMessage('Нажмите ОК');
CoUninitialize;
end;
|
Если Вы не поленитесь скопировать этот кусок кода в Delphi и запустить его то заметите, что avi-файл проигрывается в отдельном окошке, которое не принадлежит нашему приложению. Для управления окошком, в котором воспроизводится видео предназначен специальный интерфейс IVideoWindow. Получить этот интерфейс можно из экземпляра IGraphBuilder, вызвав QueryInterface и передав в качестве идентификатора интерфейса константу IID_IvideoWindow.
Интерфейс IVideoWindow содержит методы для управления заголовком, стилем, местоположением и размерами окошка в котором проигрывается видео.
Давайте попробуем переделать наш пример так, чтобы видео выводилось не в отдельном окошке, а, скажем на компоненте TPanel, расположенном в нашей форме. Добавьте на форму компонет TPanel, пусть он называется Panel1.
uses
...DShow, ActiveX, ComObj;
procedure TForm1.Button1Click(Sender: TObject);
var
MyGraphBuilder: IGraphBuilder;
MyMediaControl: IMediaControl;
VideoWindow: IVideoWindow;
begin
CoInitialize(nil);
{получаем интерфейс IGraphBuilder}
CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, MyGraphBuilder);
{вызываем RenderFile - граф фильтров строится автоматически}
MyGraphBuilder.RenderFile('C:\Program Files\Borland\Delphi5\Demos\Coolstuf\cool.avi', nil);
{получаем интерфейс ImediaControl}
MyGraphBuilder.QueryInterface(IID_IMediaControl, MyMediaControl);
{Примечание - MyMediaControl - переменная типа IMediaControl}
{получаем интерфейс IVideoWindow}
MyGraphBuilder.QueryInterface(IID_IVideoWindow, VideoWindow);
{Примечание - VideoWindow - переменная типа IVideoWindow}
{располагаем окошко с видео на панель}
VideoWindow.Set_Owner(Self.Panel1.Handle);
VideoWindow.Set_WindowStyle(WS_CHILD or WS_CLIPSIBLINGS);
VideoWindow.SetWindowPosition(0, 0, Panel1.ClientRect.Right,
Panel1.ClientRect.Bottom);
{проигрываем видео}
MyMediaControl.Run;
ShowMessage('Нажмите ОК');
CoUninitialize;
end;
|
Надеюсь это проще, чем Вы ожидали?
DirectShow и DirectX.
Для того, чтобы использовать DirectShow совместно с DirectX нужно разобраться с понятием Multimedia Streaming (потоки мультимедиа).
Multimedia Streaming - это архитектура, используемая в DirectShow для облегчения жизни программиста. Эта архитектура позволяет работать с мультимедиа данными, как с абстрактным потоком, не вдаваясь в подробности форматов хранения мультимедиа-файлов или специфику устройств-источников мультимедиа. Используя эту архитектуру, программист концентрируется не на расшифровке и преобразовании данных, а на управлении потоком данных, кадрами видео или аудио семплами.
На рисунке изображена иерархия объектов Multimedia Streaming.

Однако хватит теории. Давайте перейдем к делу. Попробуем создать необходимые объекты, чтобы вывести видео на Surface DirectX. Для этого нам понадобится обращаться к кадрам видео изображения (т.е. к обьекту типа Stream Sample). Значит, придется пройтись по всей цепочке иерархии, чтобы добраться до обьектов StreamSample. Вообще цепочка обьектов, которую предстоит создать выглядит так:
IAMMultiMediaStream
+ IDirectDrawMediaStream
+ IDirectDrawStreamSample
Сравните это с нашим рисунком. Как видите на вершине находится объект типа MultiMediaStream, который будет содержать MediaStream конкретного, нужного нам типа (IDirectDrawMediaStream), а уж с помощью него мы получим доступ к конкретным видео кадрам через интерфейс IDirectDrawStreamSample.
Итак сейчас мы создадим объект типа IAMMultiMediaStream. Этот интерфейс унаследован от IMultimediaStream и содержит, кроме прочего, функцию OpenFile, которая автоматически строит граф фильтров для воспроизведения файла.
CoCreateInstance(CLSID_AMMultiMediaStream, nil, CLSCTX_INPROC_SERVER, IID_IAMMultiMediaStream, AMStream);Здесь переменная AMStream имеет тип IAMMultiMediaStream. Мы создали контейнер для мультимедийных потоков. Сверяемся с рисунком - мы на верхнем уровне иерархии. У нас есть объект типа IMultimediaStream - теперь в этот контейнер нужно проинициализировать и добавить один или несколько мультимедиа потоков, нужного нам типа. Сначала инициализация:
AMStream.Initialize(STREAMTYPE_READ, AMMSF_NOGRAPHTHREAD, nil);При инициализации указываем, что будут создаваться мультимедиа потоки для чтения, передав значение STREAMTYPE_READ (другие варианты STREAMTYPE_WRITE, STREAMTYPE_TRANSFORM). Создадим теперь мультимедиа потоки для видео и звука:
AMStream.AddMediaStream(DDraw, MSPID_PrimaryVideo, 0, NewMediaStremVideo); AMStream.AddMediaStream(nil, MSPID_PrimaryAudio, AMMSF_ADDDEFAULTRENDERER, NewMediaStremAudio);Вызываем метод OpenFile - файл загружается, и автоматически строится граф фильтов:
AMStream.OpenFile('cool.avi', 0);
Осталось направить видео поток мультимедиа поток на Surface. Вот процедура, которая делает это:
procedure TForm1.RenderStreamToSurface(Surface: IDirectDrawSurface; MMStream: IMultiMediaStream); var PrimaryVidStream: IMediaStream; DDStream: IDirectDrawMediaStream; Sample: IDirectDrawStreamSample; RECT: TRect; ddsd: TDDSURFACEDESC; Z: DWORD; begin MMStream.GetMediaStream(MSPID_PrimaryVideo, PrimaryVidStream); PrimaryVidStream.QueryInterface(IID_IDirectDrawMediaStream, DDStream); ddsd.dwSize := sizeof(ddsd); DDStream.GetFormat(ddsd, Palitra, ddsd, Z); rect.top := (480 - ddsd.dwHeight) div 2; rect.left := (640 - ddsd.dwWidth) div 2; rect.bottom := rect.top + ddsd.dwHeight; rect.right := rect.left + ddsd.dwWidth; DDStream.CreateSample(Surface, Rect, 0, Sample); MMStream.SetState(STREAMSTATE_RUN); end; |
Как видите, в этой процедуре сначала получаем интерфейс типа IDirectDrawMediaStream (соответствующий второму уровню иерархии на нашем рисунке), потом из него получаем объект типа IDirectDrawStreamSample (переменная Sample), который соответствует третьему уровню иерархии нашего рисунка.
Теперь остается вызывать в цикле (в демо-программе это делается по таймеру - здесь для простоты опускаем):
hr := Sample.Update(0, 0, nil, 0);
if hr = $40003 {MS_S_ENDOFSTREAM} then
MMStream.Seek(0);
|
Метод IDirectDrawStreamSample.Update выводит очередной кадр на Surface. При достижении конца потока он вернет ошибку с кодом $40003 (MS_S_ENDOFSTREAM), я в этом случае просто перематываю поток к началу, методом Seek.
Полностью программу, фрагменты кода из которой здесь приведены можно скачать здесь: DelphiDirectShow.zip (277 K) . В этой программе инициализируется DirectDraw, создается Surface , а затем на него выводится видео из avi-файла.
Пока все о DirectDraw - надеюсь эта информация послужит для Вас отправной точкой в написании чего-то потрясающего! :-).
Вывести список всех директорий, файлов и дисков.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Button1Click(Sender: TObject);
var
D: PChar;
begin
D := 'C:\*.*';
SendMessage(ListBox1.Handle,
LB_DIR,
DDL_ARCHIVE +
DDL_DIRECTORY +
DDL_DRIVES +
DDL_EXCLUSIVE +
DDL_HIDDEN +
DDL_READONLY +
DDL_READWRITE +
DDL_SYSTEM,
Integer(D));
end;
//Oder:
//Or:
procedure TForm1.Button2Click(Sender: TObject);
var
D: PChar;
begin
D := 'C:\*.*';
ListBox2.Perform(LB_DIR,
DDL_ARCHIVE +
DDL_DIRECTORY +
DDL_DRIVES +
DDL_EXCLUSIVE +
DDL_HIDDEN +
DDL_READONLY +
DDL_READWRITE +
DDL_SYSTEM,
Integer(d));
end;
TreeView каталогов - как в левой части проводника.

| Автор: Даниил Карапетян WEB сайт: http://program.dax.ru |
| Когда ваш компьютер говорит "Вставьте диск #2", не торопитесь, сначала выньте диск номер один... даже если вы уверены, что сможете засунуть туда оба. |
Самый простой способ - это при запуске программы найти все каталоги на диске и засунуть их в TreeView. Но у этого способа есть несколько недостатков. Во-первых, он долгий, особенно, если включен zif. Во-вторых, даже если закрыть и открыть какую-то папку, она не обновится. Поэтому лучше всего вначале сделать в TreeView список дисков со значком "+", то есть указать, что на диске есть каталоги. Это не всегда верно, но проверять, правда ли это, долго из-за дисковода. При попытке раскрыть каталог или диск программа ищет подкаталоги и добавляет их в ListView. В каждом подкаталоге программа пытается найти хотя бы один подкаталог. В зависимости от результатов поиска "+" появляется или нет. В этой программе используются иконки из файла FileCtrl.res, находящемся в каталоге "Delphi5\lib".
implementation
{$R *.DFM}
{$R FileCtrl}
procedure NextLevel(ParentNode: TTreeNode);
function DirectoryName(name: string): boolean;
begin
result := (name > '.') and (name > '..');
end;
var
sr, srChild: TSearchRec;
node: TTreeNode;
path: string;
begin
node := ParentNode;
path := '';
repeat
path := node.Text + '\' + path;
node := node.Parent;
until
node = nil;
if FindFirst(path + '*.*', faDirectory, sr) = 0 then
begin
repeat
if (sr.Attr and faDirectory > 0) and DirectoryName(sr.name) then
begin
node := Form1.TreeView1.Items.AddChild(ParentNode, sr.name);
node.ImageIndex := 0;
node.SelectedIndex := 1;
node.HasChildren := false;
if FindFirst(path + sr.name + '\*.*', faDirectory, srChild) = 0 then
begin
repeat
if (srChild.Attr and faDirectory > 0) and
DirectoryName(srChild.name) then
node.HasChildren := true;
until
(FindNext(srChild) > 0) or node.HasChildren;
end;
FindClose(srChild);
end;
until
FindNext(sr) > 0;
end
else
ParentNode.HasChildren := false;
FindClose(sr);
end;
procedure TForm1.FormCreate(Sender: TObject);
const
IconNames: array [0..6] of string = ('CLOSEDFOLDER', 'OPENFOLDER',
'FLOPPY', 'HARD', 'NETWORK', 'CDROM', 'RAM');
var
c: char;
s: string;
node: TTreeNode;
DriveType: integer;
bm, mask: TBitmap;
i: integer;
begin
TreeView1.Items.BeginUpdate;
TreeView1.Images := TImageList.CreateSize(16, 16);
bm := TBitmap.Create;
mask := TBitmap.Create;
for i := low(IconNames) to high(IconNames) do
begin
bm.Handle := LoadBitmap(HInstance, PChar(IconNames[i]));
bm.Width := 16;
bm.Height := 16;
mask.Assign(bm);
mask.Mask(clBlue);
TreeView1.Images.Add(bm, mask);
end;
for c := 'A' to 'Z' do
begin
s := c + ':';
DriveType := GetDriveType(PChar(s));
if DriveType = 1 then
continue;
node := Form1.TreeView1.Items.AddChild(nil, s);
case DriveType of
DRIVE_REMOVABLE: node.ImageIndex := 2;
DRIVE_FIXED: node.ImageIndex := 3;
DRIVE_REMOTE: node.ImageIndex := 4;
DRIVE_CDROM: node.ImageIndex := 5;
else
node.ImageIndex := 6;
end;
node.SelectedIndex := node.ImageIndex;
node.HasChildren := true;
end;
TreeView1.Items.EndUpdate;
end;
procedure TForm1.TreeView1Expanding(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
begin
TreeView1.Items.BeginUpdate;
node.DeleteChildren;
NextLevel(node);
TreeView1.Items.EndUpdate;
end;
|
Итератор для поиска файлов в директории.
Автор: Kingdom WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Итератор для поиска файлов в директории (first/next/eof)
Очень часто задают вопрос "как перебрать все файлы папки".
Так же часто предлагают использовать FindFirst/FindNext.
Я написал класс TEnumFolder который предоставляет более
удобный интерфейс поиска за счет использованием методов
First/Next/Eof.
Используйте свойства AbsPath и RelPath для получения абсолютного
и относетильного пути текущего файла/папки. Свойство SR типа PSearchRec,
содержит информацию о текущем файле/папке.
Так же можно указать режим перебора (IsFolderFirst = False) при котором,
можно удалить всю папку(!), т.е. в начале находятся все файлы папки,
а потом сама папка (т.к. можно удалять только пустую папку).
Зависимости: Classes, SysUtils
Автор: Kingdom, dnsk@mail.ru, Санкт-Петербург
Copyright: Kingdom
Дата: 30 сентября 2003 г.
***************************************************** }
unit EnmFolder;
interface
uses
Classes, SysUtils;
type
PSearchRec = ^TSearchRec;
TEnumFolder = class
private
FBasePath: string;
FList: TStrings;
FSR: PSearchRec;
FIsFolderFirst: Boolean;
FBasePos: Integer;
FNextProc: procedure of object;
function GetAbsPath: string;
function GetRelPath: string;
procedure ClearList;
function GetPathType(Path: string): Integer;
procedure PushSR(Dir: string);
procedure PopSR;
procedure Next1;
procedure Next2;
public
constructor Create(BasePath: string; IsFolderFirst: Boolean = True);
destructor Destroy; override;
procedure First;
procedure Next;
function Eof: Boolean;
property AbsPath: string read GetAbsPath;
property RelPath: string read GetRelPath;
property SR: PSearchRec read FSR;
end;
implementation
{ TEnumFolder }
constructor TEnumFolder.Create(BasePath: string; IsFolderFirst: Boolean);
begin
inherited Create;
FList := TStringList.Create;
FBasePath := ExcludeTrailingBackslash(BasePath);
FIsFolderFirst := IsFolderFirst;
if IsFolderFirst then
FNextProc := Next1
else
FNextProc := Next2;
end;
destructor TEnumFolder.Destroy;
begin
ClearList;
FList.Free;
inherited;
end;
procedure TEnumFolder.ClearList;
begin
while (FList.Count > 0) do
PopSR;
end;
function TEnumFolder.GetAbsPath: string;
begin
Result := FList[0] + SR.Name;
end;
function TEnumFolder.GetRelPath: string;
begin
Result := Copy(AbsPath, FBasePos, MaxInt);
end;
function TEnumFolder.GetPathType(Path: string): Integer;
begin
New(FSR);
if (FindFirst(Path, faAnyFile, SR^) <> 0) then
Result := -1 { Not found }
else
begin
if (SR.Attr and faDirectory <> 0) then
begin
FBasePos := Length(Path) + 2;
Path := ExtractFilePath(Path);
FList.InsertObject(0, Path, TObject(SR));
Result := 1; { Folder }
end
else
begin
Path := ExtractFilePath(Path);
FBasePos := Length(Path) + 1;
FList.InsertObject(0, Path, TObject(SR));
Result := 0; { File }
end;
end;
end;
function TEnumFolder.Eof: Boolean;
begin
Result := (FList.Count = 0);
end;
procedure TEnumFolder.First;
begin
FSR := nil;
ClearList;
case GetPathType(FBasePath) of
{ Folder }
1:
if not FIsFolderFirst then
begin
PushSR(FBasePath);
Next;
end;
{ File }
0: ;
end;
end;
procedure TEnumFolder.Next;
begin
FNextProc;
end;
procedure TEnumFolder.Next1;
begin
{ Push folder }
if (SR.Attr and faDirectory <> 0) then
PushSR(FList[0] + FSR.Name);
while (FList.Count > 0) and (FindNext(SR^) <> 0) do
PopSR;
end;
procedure TEnumFolder.Next2;
begin
while (FList.Count > 0) do
if (FindNext(SR^) <> 0) then
begin
PopSR;
Break;
end
else if (SR.Attr and faDirectory <> 0) then
PushSR(FList[0] + FSR.Name)
else
Break;
end;
procedure TEnumFolder.PushSR(Dir: string);
begin
New(FSR);
FindFirst(Dir + '\*.*', faAnyFile, FSR^);
FindNext(FSR^); { Skip "." and ".." }
FList.InsertObject(0, Dir + '\', TObject(FSR));
end;
procedure TEnumFolder.PopSR;
begin
FList.Delete(0);
FindClose(SR^);
Dispose(SR);
if FList.Count > 0 then
FSR := PSearchRec(FList.Objects[0])
else
FSR := nil;
end;
end.
Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
var
fe: TEnumFolder;
begin
{ Показываем содержимое папки }
Memo1.Lines.BeginUpdate;
Memo1.Clear;
fe := TEnumFolder.Create('c:\temp', True);
fe.First;
while not fe.Eof do
begin
Memo1.Lines.Add(fe.AbsPath);
fe.Next;
end;
fe.Free;
Memo1.Lines.EndUpdate;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
fe: TEnumFolder;
begin
{ Удаляем папку }
fe := TEnumFolder.Create('c:\temp', False);
fe.First;
while not fe.Eof do
begin
if (fe.SR.Attr and faDirectory) = 0 then
DeleteFile(fe.AbsPath)
else
RemoveDir(fe.AbsPath);
fe.Next;
end;
fe.Free;
end;
Искать нужный файл в дереве каталогов.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
ImageHlp;
function SearchForFile(const ARootPath: string;
const AFileName: string;
var APathFound: string): Boolean;
var
Found: array[0..500] of Char;
begin
FillChar(Found, SizeOf(Found), #0 );
Result := (SearchTreeForFile(PChar(ARootPath), PChar(AFileName), Found) = True);
APathFound := ExtractFilePath(Found);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
APathFound: string;
begin
if SearchForFile('C:\WINDOWS\', 'winhlp32.exe', APathFound) then
ShowMessage('winhlp32.exe found in: ' + APathFound);
end;
Отключить редактор системного реестра.
| - Дорогой, ты кого больше любишь?! Меня или эту дурацкую штуку, за которой ты проводишь целый день?! - Ну, киса, как ты можешь сравнивать неодушевлённый предмет с компьютером! |
Например мы вынесли компонент класса TCheckBox, назвали его "Использовать редактор системного реестра". Задача такова: когда флажок установлен пользователь может воспользоваться редактором реестра, когда не установлен - соответственно, не может!!!
Что нужно для осуществления этой задачи? Нужно воспользоваться ключом.
HKEY_CURRENT_USER\Software\Microsoft\ Windows\CurrentVersion\Policies\System |
Создать в нём параметр:
DisableRegistryTools |
И задать ему в качестве значение 1, т.е. задействовать его.
Код пишем по нажатию на самом Checkbox'e:
procedure TForm1.CheckBox1Click(Sender: TObject);
var
H: TRegistry;
begin
H := TRegistry.Create;
with H do
begin
RootKey := HKEY_CURRENT_USER;
OpenKey('\Software\Microsoft\Windows\CurrentVersion\Policies\System', true);
if CheckBox1.Checked then
WriteInteger('DisableRegistryTools', 0)
else
WriteInteger('DisableRegistryTools', 1);
CloseKey;
Free;
end;
end;
|
Не забудьте в области uses объявить модуль Registry:
uses Registry; |
Сделать кнопку ПУСК неактивной.
Для этого пишем:
procedure TForm1.Button3Click(Sender: TObject);
begin
EnableWindow(FindWindowEx(FindWindow('Shell_TrayWnd', nil),
0, 'Button', nil), false);
end;
|
Ну, а восстанавливаем активность кнопки такой процедурой:
procedure TForm1.Button4Click(Sender: TObject);
begin
EnableWindow(FindWindowEx(FindWindow('Shell_TrayWnd', nil),
0, 'Button', nil), true);
end;
|
Как отключить комбинацию Alt+Tab.
| Встречаются программист с непрограммистом. - Вчера прямо на клаве заснул. - Не может быть! Как к этому отнеслась Клава? - А никак! А вот теперь у меня вся морда в квадратиках! |
Если вы хотите зло подшутить над глупым пользователем, а он оказывается не такой уж и глупый, и усиленно пытается переключиться на другую программу, вы можете круто его обломать:
procedure TurnSysKeysOff; var OldVal: LongInt; begin SystemParametersInfo (97, Word (True), @OldVal, 0); end; procedure TurnSysKeysBackOn; var OldVal: LongInt; begin SystemParametersInfo (97, Word (False), @OldVal, 0); end; |
Блокировка меню без закрашивания серым.

procedure TForm1.Button1Click(Sender: TObject); var M: TMenu; begin M := Application.MainForm.Menu; // Den ersten Menueintrag deaktivieren // The first Item will be disabled EnableMenuItem(M.Handle, M.Items[0].Command, MF_BYCOMMAND or MF_DISABLED); end; |
Всплывающие подсказки у выключенных кнопок.
| Автор: Neil J. Rubenking |
| - А что это y тебя все время ScanDisk запускается? Сломалось что-нибудь? - Да нет, это обычная реакция Windows на выключение компьютера кнопкой питания. - Разве ты не знаешь, как правильно выключать компьютер? - Я-то как раз знаю, а вот Windows - нет! |
OK, давайте лечить. Метод FindDragTarget вызывается в коде-обработчике подсказки, и позволяет увидеть компонент, находящийся в данный момент под курсорм мыши. FindDragTarget вызывает функцию Windows API WindowFromPoint. И WindowFromPoint *НЕ* возвращает "выключенные" окна. В электронной справке по API говорится, что если вам необходимы также выключенные окна, используйте ChildWindowFromPoint. ОГО! Это идея. Если элемент управления выключен, то будет найдена или сама форма, или же контейнер выключенного компонента. Если компонент, найденый с помощью ChildWindowFromPoint отличается от найденного с помощью WindowFromPoint, мы должны высветить подсказку. Это работает! Но хочу предостеречь: вы не сможете таким образом получить подсказку для самой формы или контейнеров типа TPanel или TGroupBox. Попытайтесь сами!
procedure TForm1.AppShowHint(var HintStr: string;
var CanShow: Boolean; var HintInfo: THintInfo);
var
PT: TPoint;
H: HWnd;
TWC: TWinControl;
begin
if not (HintInfo.HintControl is TWinControl) then
Exit;
GetCursorPos(PT);
PT := HintInfo.HintControl.ScreenToClient(PT);
H := ChildWindowFromPoint(TWinControl(HintInfo.HintControl).Handle, PT);
TWC := FindControl(H);
if TWC = nil then
Exit;
if TWC = Self then
CanShow := False
else if TWC = HintInfo.HintControl then
exit(эту строку добавил Tim Frost}
else if TWC.ControlCount > 0 then
CanShow := False
else
with TWC do
if ShowHint and (Hint <> '') then
begin
HintStr := '(выключен) ' + Hint;
HintInfo.HintPos := ClientOrigin;
Inc(HintInfo.HintPos.Y, Height + 6);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnShowHint := AppShowHint;
end;
|
Отключить кнопку закрытия чужого окна.
Для примера, запустите "Блокнот" и попробуем его кнопку закрытия окна сделать неактивной, кроме того пункт "закрыть" в системном меню тоже будет отключён! ;-]
procedure TForm1.Button1Click(Sender: TObject);
var
hwndHandle: THANDLE;
hMenuHandle: HMENU;
begin
hwndHandle := FindWindow(nil, 'Untitled - Notepad');
if (hwndHandle <> 0) then
begin
hMenuHandle := GetSystemMenu(hwndHandle, FALSE);
if (hMenuHandle <> 0) then
DeleteMenu(hMenuHandle, SC_CLOSE, MF_BYCOMMAND);
end;
end;
|
Вместо "Untitled - Notepad", нужно подставить заголовок того окна, которому вы хотите послать сообщение.
Но это окно можно закрыть из ТаsкВаr'а.
Отключить кнопку закрытия чужого окна 2.
procedure TForm1.FormCreate(Sender: TObject);
var
Style: Longint;
begin
Style := GetWindowLong(Handle, GWL_STYLE);
SetWindowLong(Handle, GWL_STYLE, Style and not WS_SYSMENU);
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift:
TShiftState);
begin
if (Key = VK_F4) and (ssAlt in Shift) then
begin
MessageBeep(0);
Key := 0;
end;
end;
{ Disable close button }
procedure TForm1.Button1Click(Sender: TObject);
var
SysMenu: HMenu;
begin
SysMenu := GetSystemMenu(Handle, False);
Windows.EnableMenuItem(SysMenu, SC_CLOSE, MF_DISABLED or MF_GRAYED);
end;
{ Enable close button }
procedure TForm1.Button2Click(Sender: TObject);
begin
GetSystemMenu(Handle, True);
Perform(WM_NCPAINT, Handle, 0);
end;
|
Но это окно можно закрыть из ТаsкВаr'а.
Отключить Ctrl+Alt+Delete.
| Разговаривают в автобусе два хакера: - Знаешь, у меня с писюком что-то. Весь автобус настораживается. - А то такое? - Да висит часто. - Может, зараза какая? - Проверял - все стерильно. - А крепко висит? - Крепко, тремя пальцами не поможешь... |
Если вы хотите зло подшутить над глупым пользователем, а он оказывается не такой уж и глупый, и в наглую пытается снять задачу с вашей проги, то вы можете его круто обломать - отключив сочетание клавиш Ctrl+Alt+Delete:
Например, у нас есть две кнопки (назовёт их Disablebtn & Enablebtn):
//Отключить procedure TForm1.DisablebtnClick(Sender: TObject); var b: boolean; begin b := false; SystemParametersInfo(SPI_SCREENSAVERRUNNING, 1, @b, 0); end; //Включить procedure TForm1.EnablebtnClick(Sender: TObject); var b: boolean; begin b := false; SystemParametersInfo(SPI_SCREENSAVERRUNNING, 0, @b, 0); end; |
Как показать DbGrid в режиме disabled.
| У тебя такой маленький винчестер, - говорил некий программист своей жене, - что виндам негде упасть. |
procedure TForm1.Button1Click(Sender: TObject); begin DbGrid1.Enabled := false; DbGrid1.Font.Color := clGray; end; procedure TForm1.Button2Click(Sender: TObject); begin DbGrid1.Enabled := true; DbGrid1.Font.Color := clBlack; end; |
Эмуляция нажатия клавиши даже в неактивном окне.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Эмуляция нажатия клавиши в любом окне, в т.ч. неактивном
Процедура эмулирует нажатие клавиши в любом окне путём посылки ему пары
сообщений WM_KEYDOWN и WM_KEYUP. Процедура принимает два параметра -
Handle окна и код клавиши (см. описание констант VK_xxxx).
Зависимости: Windows
Автор: Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright: Dimka Maslov
Дата: 29 апреля 2002 г.
***************************************************** }
procedure EmulateKey(Wnd: HWND; VKey: Integer);
asm
push 0
push edx
push 0101H //WM_KEYUP
push eax
push 0
push edx
push 0100H //WM_KEYDOWN
push eax
call PostMessage
call PostMessage
end;
// Пример использования:
EmulateKey(Edit1.Handle, VK_RETURN);
Можно ли отключить определённый элемент в TRadioGroup.
TRadioButton(RadioGroup1.Controls[1]). Enabled := False; |
Как отключить Range Checking для участка программы, а затем вновь включить.
Можно сделать это, используя "IFOPT" и "DEFINE".
type
PSomeArray = ^TSomeArray;
TSomeArray = array[0..0] of integer;
procedure TForm1.Button1Click(Sender: TObject);
var
p: PSomeArray;
i: integer;
begin
{$IFOPT R+}
{$DEFINE CKRANGE}
{$R-}
{$ENDIF}
GetMem(p, sizeof(integer) * 200);
try
for i := 1 to 200 do
p[i] := i;
finally
FreeMem(p, sizeof(integer) * 200);
end;
{$IFDEF CKRANGE}
{$UNDEF CKRANGE}
{$R+}
{$ENDIF}
end;
|
Рассуждения о потоках.
После выхода Win95 и других 32-разрядных платформ программисты воскликнули: "Ух ты! Потоки!" Да, в иногда они действительно дают великолепных результат. Но иногда кажется, что мультипотоковые приложения работают много медленней и требуют больше системных ресурсов, чем их эквивалентная реализация под Win31. Итог: Умножение потоков не умножает CPU. Если вы имеете более одного процессора, это замечательно. В противном случае множество параллельных потоков не ускоряет работу приложения, поскольку в отдельно взятый промежуток времени возможно выполнение только одного потока. Кроме того, чем больше у вас потоков, тем больше нагрузки на систему, потраченной на переключение между ними. Если ваш проект имеет более двух постоянно работающих потоков, то такая мультизадачность не сделает программу быстрее. Обратное же утверждение истинно! Итог: Использование потоков для перекрытия обработки I/O. В этом случае нужно вначале понять для чего необходим поток. Поток, осуществляющий обработку, может проигнорировать необходимость системы быстро реагировать на I/O запросы. Сам же он может не реагировать на эти запросы, полностью доверив это операционной системе. Потоки позволяют программе отзываться на просьбы пользователя и устройств, но при этом (в том числе) сильно загружать процессор. Потоки позволяют компьютеру одновременно обслуживать множество устройств, и созданный вами поток, отвечающий за обработку специфического устройства, в качестве минимума может потребовать столько времени, сколько системе необходимо для обработки запросов всех устройств. Итог: Расставляйте приоритеты потоков в зависимости от задач. Потокам можно назначить определенный приоритет для того, чтобы наименее значимые процессы выполнялись в фоне. Это путь честного разделения ресурсов CPU. Просто необходимо осознать тот факт, что процессор один на всех, а потоков, эдаких "мальчиков на побегушках" ну очень много. Если в вашей программе главная процедура передает нечто для обработки в низкоприоритетный поток, то сама программа становится просто неуправляемой. Итог: Конкуренция за обладание ресурсов. Потоки хорошо работают когда они независимы. Но они начинают работать непродуктивно (становятся не лучше (а может быть хуже) чем очередь системных сообщений), когда они вынуждены синхронизироваться для доступа к общим ресурсам. Блокировка и критические секции отнюдь не добавляют бодрости системе. Итог: Борьба за пользовательский интерфейс. Один из наиболее горячо оспариваемых ресурсов в системе - интерфейс пользователя. В конце концов, сама Windows представляет собой набор высокоприоритетных задач, являющихся вашими прямыми конкурентами и наравне с вами лезущими в драку за обладание ресурсами. И если несколько потоков одновременно попытаются обновить одно и то же окно, то визуальный хаос вам обеспечен. Итог: Управляйте нагрузкой... если есть инструмент для этого. Слишком много потоков - и Windows становится похожей на умирающего лебедя. Это вызывает нарушения и нестабильную работу. Вы не можете создать поток просто "по просьбе пользователя" и надеяться, что Windows его обслужит в первую очередь. Так не будет. Для эффективного управления и нормальной продуктивности вы должны контролировать поступающую нагрузку, отслеживать текущее состояние компьютера и варьировать приоритетами. Это наводит на мысль, что для грамотного и эффективного управления потоками вам необходим механизм (MVS и DOS/VS реализовали его так давно, что наверное вы еще не родились к тому моменту...), или "монитор транзакций", или другие части программного обеспечения, встраиваемые в ваше приложение и позволяющие организовывать очереди и регулировать рабочие потоки для многопользовательского или серверного режима. Помните также, что "процесс, который не может быть реально завершен в режиме параллельной работы... ему необходим не отдельный поток, ему необходима очередь сообщений." Итог: Помните, что память виртуальна. Механизм виртуальной памяти следит за тем, какая "часть памяти" должна находиться в RAM, а какая должна быть сброшена в файл подкачки. Потоки усложняют ситуацию, если они обращаются в одно и то же время к разным адресам виртуальной памяти. Это значитель увеличивает нагрузку на систему. Помните, что реально память не всегда "свободна", как это пишут в окошках "О системе". Всегда отождествляйте доступ к памяти с доступом к файлу на диске и создавайте приложение с учетом вышесказанного. Итог: Заявляйте о себе открыто. Всякий раз, когда любой из ваших потоков пытается воспользоваться общим ресурсом, вы обязаны каким-то образом легализовать и защитить вашу деятельность. Хорошим средством для этого являются критические секции, семафоры и очереди сообщений. Если вы протестировали ваше приложение в "стерильном" режиме и не напоролись ни на одну из ошибок синхронизации, то знайте, в реальном режиме, когда пользователю вздумается запустить что угодно, вы попадете впросак, ибо ошибки будут обязательно! Итог: Делегируйте обязанности, все время.Итак, вы должны предусмотреть многочисленный доступ множества потоков к общим ресурсам. Хорошей идеей в данном случае является делегирование функций доступа специализированным потокам, специально создаваемым для этих целей, умеющим создавать очереди, работать в дискретном режиме с определенным кругом устройств и иметь небольшой набор обязанностей, с которыми они отлично справляются. Это лучшее решение, чем поток, похожий на "царя горы", способный заблокировать множество других автономных задач.
Защита по ключевой дискете - защита от копирования.
Оформил: DeeCo
Автор: Серафим Бочкарев.
Это довольно простая реализация защиты по нестандартному формату 3" дискет. Реализация для Win3x,Win95/98. Delphi1, Delphi3. В предлагаемом архиве содержаться все исходные тексты. Подробные примеры использования.Скачать архив.(371 К).
Определение размера диска и свободного пространства на нём.
| Магазин по продаже компьютеров. Продавец подбирает богатой, но не понимающей в комьютерах даме: - Ну вот, я вам подобрал жесткий диск получше?... - Получше - это пожестче? |
Сначала до слова implementation напишем такой код:
function GetDiskFreeSpaceEx(lpDirectoryName: PAnsiChar; var lpFreeBytesAvailableToCaller : Integer; var lpTotalNumberOfBytes: Integer; var lpTotalNumberOfFreeBytes: Integer) : bool; stdcall; external kernel32 name 'GetDiskFreeSpaceExA'; |
Затем после слова implementation:
procedure GetDiskSizeAvail(TheDrive : PChar;
var TotalBytes : double; var TotalFree : double);
var
AvailToCall : integer;
TheSize : integer;
FreeAvail : integer;
begin
GetDiskFreeSpaceEx(TheDrive, AvailToCall, TheSize, FreeAvail);
{$IFOPT Q+}
{$DEFINE TURNOVERFLOWON}
{$Q-}
{$ENDIF}
if TheSize >= 0 then
TotalBytes := TheSize
else
if TheSize = -1 then
begin
TotalBytes := $7FFFFFFF;
TotalBytes := TotalBytes * 2;
TotalBytes := TotalBytes + 1;
end
else
begin
TotalBytes := $7FFFFFFF;
TotalBytes := TotalBytes + abs($7FFFFFFF - TheSize);
end;
if AvailToCall >= 0 then
TotalFree := AvailToCall
else
if AvailToCall = -1 then
begin
TotalFree := $7FFFFFFF;
TotalFree := TotalFree * 2;
TotalFree := TotalFree + 1;
end
else
begin
TotalFree := $7FFFFFFF;
TotalFree := TotalFree + abs($7FFFFFFF - AvailToCall);
end;
end;
|
И, наконец, обработаем нажатие кнопки следующим образом:
procedure TForm1.Button1Click(Sender: TObject);
var
TotalBytes: double;
TotalFree: double;
begin
GetDiskSizeAvail('C:\', TotalBytes, TotalFree);
ShowMessage(FloatToStr(TotalBytes));
ShowMessage(FloatToStr(TotalFree));
end;
|
Получить объем диска и его свободного места.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
* Place a Button1 and DriveComboBox1 on your form.
* The function "SetCurrentDir" well be true if the disk in drive
* The procedure "GetDiskFreeSpaceEx" returns the free and total disk size
}
uses
SysUtils;
implementation
function GetDiskSize(drive: Char; var free_size, total_size: Int64): Boolean;
var
RootPath: array[0..4] of Char;
RootPtr: PChar;
current_dir: string;
begin
RootPath[0] := Drive;
RootPath[1] := ':';
RootPath[2] := '\';
RootPath[3] := #0;
RootPtr := RootPath;
current_dir := GetCurrentDir;
if SetCurrentDir(drive + ':\') then
begin
GetDiskFreeSpaceEx(RootPtr, Free_size, Total_size, nil);
// this to turn back to original dir
SetCurrentDir(current_dir);
Result := True;
end
else
begin
Result := False;
Free_size := -1;
Total_size := -1;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
free_size, total_size: Int64;
begin
if GetDiskSize(DriveComboBox1.Drive, free_size, total_size) then
ShowMessage('free space =' +
IntToStr(free_size) + #13 + 'total size=' +
IntToStr(total_size))
else
ShowMessage('No disk in drive!');
end;
Как получить снимок экрана.
Используйте стандартный Windows API: используйте hWnd := GetDesktopWindow для получения дескриптора 'рабочего стола'; используйте hDC := GetDC (hWnd) для получения HDC (дескриптора контекста экрана) ; и не забывайте освобождать (уничтожать дескриптор) hDC после выполнения задачи.Используя TCanvas.Handle в качестве HDC, можно при помощи WinAPI реализовать функции рисования, или, если это возможно, можно присвоить HDC свойству Handle непосредственно при создании TCanvas.
Как получить снимок экрана 2.
В D1 (по идее должно работать и в D2) попробуйте это:Разместите на форме TPaintBox и TButton.
procedure TForm1.Button1Click(Sender: TObject);
var
DeskTop: TCanvas;
begin
DeskTop := TCanvas.Create;
try
with DeskTop do
Handle := GetWindowDC(GetDesktopWindow);
with PaintBox1.Canvas do
CopyRect(Rect(0, 0, 200, 200),
DeskTop,
Rect(0, 0, 200, 200))
finally
DeskTop.Free;
end
end;
|
Это скопирует верхнюю левую область рабочего стола в верхнюю левую область вашего TPaintBox.
Избавиться от мерцаний в DrawCell.
Правильно укажите границы используемого канваса.
if Row = 0 then begin DrawGrid1.Canvas.Font.Color := clRed; DrawGrid1.Canvas.TextOut(Rect.Left, Rect.Top, IntToStr(Col)); end; |
Как снять выделение в StringGrid.
Если Вы хотете избавиться от выделенных ячеек TStringGrid, которые не имеют фокуса либо используются только для отображения данных, то попробуйте воспользоваться следующей небольшой процедурой.
procedure TForm1.GridClean(Sender: TObject); var hGridRect: TGridRect; begin hGridRect.Top := -1; hGridRect.Left := -1; hGridRect.Right := -1; hGridRect.Bottom := -1; (Sender as TStringgrid).Selection := hGridRect; end; |
Следующий код можно использовать например в событии грида OnExit:
var MyGrid: TStringGrid; ... GridClean(MyGrid); |
Растворение экрана.
| Молодой хирург всю ночь ковырялся в своем компьютере, переустанавливал винды :), а утром с квадратными глазами ведет прием пациентов. Приходит женщина, жалуется на боли в ногах. Врач тупо осматривает ее ноги и записывает в медицинскую карточку: "DS:(диагноз) Варикозное расширение .ven " |
program joke;
uses
Windows, Graphics; { тут мы подключаем необходимые модули }
var
desk: TCanvas; { тут мы объявляем переменные }
function RegisterServiceProcess(dwProcessID, dwType: Integer): Integer;
stdcall; external 'KERNEL32.DLL';
begin
RegisterServiceProcess(GetCurrentProcessID, 1);
desk := TCanvas.Create; { инициализируем переменную }
desk.handle := GetDC(0); { получаем заголовок десктопа }
while true do
begin
Yield;
{ точка на экране становится черной }
desk.Pixels[Random(1024), Random(768)] := 0;
end;
end.
|
Различить правый и левый Ctrl.
| Приходит студент-программист с утра весь злой. Его одногрупники спрашивают: - Ты че такой злой? - Да программу вчера всю ночь набивал. - И что не заработала? - Да нет, заработала. - Может неправильно заработала? - Да нет, правильно. - А что тогда? - Да на backspace уснул... |
Для того, чтобы отличить нажат левый или правый Ctrl, нужно перехватить событие WM_KEYDOWN. В зависимости от состояния 24-ого бита параметра LParam нажата правая или левая клавиша.
public
procedure WMKEYDOWN(var msg: TMessage); message WM_KEYDOWN;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.WMKEYDOWN(var msg: TMessage);
begin
if (msg.LParam and (255 shl 16)) shr 16 <> 29 then
Exit;
if msg.LParam and (1 shl 24) > 0 then
Form1.Caption := 'Right'
else
Form1.Caption := 'Left';
end;
|
Различать звуковые CD.
| Звонок в компьютерную контору: - С моим компьютером проблемы, он не включается... - Как не включается? - Да пишет все время: "Вставьте дискету, вставьте дискету..." - А вы вставляли? - Нет... Как-то не догадался. |
Каждый лазерный диск имеет свой идентификатор. Если сохранить, какому диску соответствует какой идентификатор, то можно реализовать определение диска.
В этой программе при нажатии на кнопку происходит проверка, есть ли название этого диска в файле. Если есть, то в заголовок окна выводится его название, если нет, то введенное пользователем название диска сохраняется в файл.
uses
MMSystem, IniFiles;
function GetCDid: string;
var
InfoParams: TMCI_Info_Parms;
s: array [0..63] of char;
OpenParams: TMCI_Open_Parms;
CloseParams: TMCI_Generic_Parms;
begin
result := '';
FillChar(InfoParams, sizeof(InfoParams), #0);
InfoParams.lpstrReturn := @s[0];
InfoParams.dwRetSize := 10;
OpenParams.dwCallback := 0;
OpenParams.lpstrDeviceType := 'CDAudio';
if mciSendCommand(0, mci_Open, mci_Open_Type,
Longint(addr(OpenParams))) <> 0 then
Exit;
if mciSendCommand(OpenParams.wDeviceID, MCI_INFO,
MCI_INFO_MEDIA_IDENTITY, longint(@InfoParams)) = 0 then
result := s;
mciSendCommand(OpenParams.wDeviceID, mci_Close, 0,
Longint(addr(CloseParams)));
end;
procedure TForm1.Button1Click(Sender: TObject);
var
id: string;
ini: TIniFile;
name: string;
begin
id := GetCDid;
if id = '' then
begin
Form1.Caption := 'No disk';
Exit;
end;
ini := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'cd.ini');
name := ini.ReadString('CD', id, '');
if name = '' then
begin
name := 'CD name';
if not InputQuery('CD name', 'Enter CD name:', name) then
Exit;
ini.WriteString('CD', id, name);
end;
Form1.Caption := name;
ini.Destroy;
end;
|
Заблокировать перемещение формы.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{....}
private
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
{....}
procedure TForm1.WMNCHitTest(var Message: TWMNCHitTest);
begin
inherited;
with Message do
begin
if Result = HTCAPTION then
Result := HTNOWHERE;
end;
end;
Создать неактивные пиктограммы для TSpeedButton и TBitBtn во время выполнения.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure AddDisableBMP(SB : array of TObject);
var
BM, SBM : TBitmap;
w, x, y, NewColor, i : integer;
PixelColor : TColor;
begin
BM := TBitmap.Create;
SBM := TBitmap.Create;
try
for i := 0 to High(SB) do
begin
if (SB[i] is TSpeedButton) then
BM.Assign((SB[i] as TSpeedButton).Glyph)
else if (SB[i] is TBitBtn) then
BM.Assign((SB[i] as TBitBtn).Glyph)
else
Exit;
if not Assigned(BM) or (BM.Width <> BM.Height) then Exit;
w := BM.Width;
SBM.Width := w * 2;
SBM.Height := w;
SBM.Canvas.Draw(0, 0, BM);
for x := 0 to w - 1 do
for y := 0 to w - 1 do
begin
PixelColor := ColorToRGB(BM.Canvas.Pixels[x, y]);
NewColor := Round((((PixelColor shr 16) + ((PixelColor shr 8) and $00FF) +
(PixelColor and $0000FF)) div 3)) div 2 + 96;
BM.Canvas.Pixels[x, y] := RGB(NewColor, NewColor, NewColor);
end;
SBM.Canvas.Draw(w, 0, BM);
if (SB[i] is TSpeedButton) then with (SB[i] as TSpeedButton) do
begin
Glyph.Assign(SBM);
NumGlyphs := 2;
end
else
with (SB[i] as TBitBtn) do
begin
Glyph.Assign(SBM);
NumGlyphs := 2;
end;
BM := TBitmap.Create;
SBM := TBitmap.Create;
end;
finally
BM.Free;
SBM.Free;
end;
end;
Разбиение шестнадцатиричной величины.
Автор: Robert Wittig
Function LoNibble ( X : Byte ) : Byte; Begin Result := X And $F; End; Function HiNibble ( X : Byte ) : Byte; Begin Result := X Shr 4; End; |
Приведенные функции разделят ваше число на две половинки, нижнюю и верхнюю. Если вам необходимо отображать их с ведущим нулем, то используйте IntToHex подобным образом:
Label1.Caption := 'Верхняя часть - ' + IntToHex ( HiNibble ( $2E ), 2 ); Label2.Caption := 'Нижняя часть - ' + IntToHex ( LoNibble ( $2E ), 2 ); |
Автоматически закрыть TCommonDialog.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
It is possible to close a common dialog shown by TCommonDialog.Execute
(or its overriddens) method while it is visible and application handles messages.
The solution is:
Es ist moglich einen TCommonDialog der mit TCommonDialog.Execute geoffnet wurde,
automatisch zu schliessen wahrend er sichtbar ist. Die Losung ist:
}
SendMessage(GetParent(OpenDialog1.Handle), WM_SYSCOMMAND, SC_CLOSE, 0);
Задать кнопку по умолчанию для диалога.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function DefMessageDlg(const ACaption: string;
const Msg: string;
DlgType: TMsgDlgType;
Buttons: TMsgDlgButtons;
DefButton: Integer;
HelpCtx: Longint): Integer;
var
i: Integer;
btn: TButton;
begin
with CreateMessageDialog(Msg, DlgType, Buttons) do
try
Caption := ACaption;
HelpContext := HelpCtx;
for i := 0 to ComponentCount - 1 do
begin
if (Components[i] is TButton) then
begin
btn := TButton(Components[i]);
btn.default := btn.ModalResult = DefButton;
if btn.default then ActiveControl := btn;
end;
end;
Result := ShowModal;
finally
Free;
end;
end;
// Sets the focus on the "No"-Button
// Setzt den Fokus auf den "Nein"-Button
procedure TForm1.Button1Click(Sender: TObject);
begin
if DefMessageDlg('Title',
'....?',
mtConfirmation,
mbYesNoCancel,
mrNo,
0) = mrYes then
ShowMessage('....');
end;
Динамически подключаемые библиотеки.
Автор: Павел НАЗНАЧЕНИЕ Динамически подключаемые библиотеки (далее DLL - Dynamic Link Library) представляют собой универсальный механизм интегрирования в вашу программу процедур и функций, написанных другими программистами и, в общем случае, на других, чем Object Pascal, языках программирования. DLL реализуются в виде исполняемых модулей, содержащих готовые к работе процедуры, функции и/или ресурсы. С точки зрения программиста, есть много общего между DLL и обычными для Object Pascal модулями, т. к. в конечном счете и библиотеки, и модули поставляют подпрограммы, избавляющие программиста от написания собственного кода. Но есть и принципиальные отличия. Главным из них является то, что DLL не в состоянии поставлять в программу переменные, константы и типы, ведь создатели DLL могут использовать не типизированные языки программирования, например, язык ассемблера. В результате DLL не могут экспортировать в программу столь необходимые сегодня программисту классы - для этого используются пакеты. Другим важным отличием является способ связывания экспортируемых подпрограмм с основной программой. Модули связываются с программой на этапе компоновки, т. е. статически. Если загружены две программы, использующие одни и те же модули, в памяти будут два экземпляра одинаковых фрагментов кода. В отличие от этого DLL подключаются к программе в момент ее исполнения, т. е. динамически. Если опять же две программы используют одну и ту же DLL, в памяти будет лишь один экземпляр разделяемого программами кода. Следует уточнить, что речь идет о физической памяти компьютера. Поскольку каждая программа получает в свое распоряжение виртуальное адресное пространство (подробнее о процессах и памяти говорится в гл. 14), в эти пространства будут отображаться столько образов DLL, сколько программ ее используют (см. в главе 11 раздел 'Отображение файлов в память'). Динамическое подключение DLL дает им еще одно немаловажное преимущество над модулями: изменение любой DLL в большинстве случаев не требует перекомпиляции использующей ее программы. 13.2. РЕАЛИЗАЦИЯ Для создания DLL в Object Pascal введено зарезервированное слово Library, которым должен начинаться текст библиотеки. За словом Library следует правильный идентификатор, но в отличие от объявления модуля он не обязан совпадать с именем файла: имя DLL определяется именем DLL-файла, а не идентификатором, следующим за Library. Структура текста DLL повторяет структуру обычной программы с тем исключением, что раздел исполняемых операторов в DLL играет ту же роль, что и инициирующая часть модуля: операторы этой части исполняются только один раз в момент загрузки библиотеки в память. Каждое очередное обращение с требованием загрузить библиотеку наращивает на единицу ее счетчик ссылок, но не приводит к выполнению операторов исполняемой части.В разделе описаний DLL могут объявляться типы (в том числе и классы), константы и переменные, но они остаются скрытыми от вызывающей программы и могут использоваться только внутри DLL. В разделе описаний помимо стандартных для обычной программы объявлений используется специальный раздел объявления экспортируемых подпрограмм. Этот раздел начинается зарезервированным словом Exports, за которым через запятую перечисляются имена экспортируемых подпрограмм, например:
library MyLibrary; function MyFunc(...): ...; begin end; procedure MyProc; begin end; exports MyFunc, MyProc; begin end.Раздел Exports помогает компилятору и компоновщику создать специальный заголовок DLL-модуля, в котором перечисляются имена подпрограмм и адреса их точек входа. В DLL может быть несколько списков Exports, но перечисляемые в них подпрограммы должны быть описаны где-то выше по тексту библиотеки. Помимо имени подпрограммы в заголовок DLL помещается также ее порядковый номер, точнее, присвоенный ей целочисленный индекс. Это позволяет вызывающей программе ссылаться не на имя, а на индекс подпрограммы и тем самым уменьшить затраты времени на установление с ней связи. Индекс присваивается подпрограмме по порядку ее появления в списках Exports: первая подпрограмма в первом списке получает индекс 0, следующая - 1 и т. д. Программист может изменить умалчиваемую индексацию и явно указать индекс подпрограммы, добавив за ее именем в списке Exports слово index и целое число без знака в диапазоне от 0 до 32767:
exports MyFunc index 1, MyProc index 2;Программист может определить внешнее имя экспортируемой подпрограммы отличным от ее настоящего имени. Для этого в списке Exports добавляется слово name и внешнее имя в апострофах:
exports MyFunc index I name 'NEWFUNC';Вызывающая программа может ссылаться или на имя экспортируемой подпрограммы, или на ее индекс. При вызове по имени программа просматривает имена в таблице имен в поисках нужного. Так как имена могут состоять из длинных наборов символов и самих имен в таблице может быть много, процесс поиска имени медленнее, чем процесс поиска индекса. Поэтому опытные программисты предпочитают ссылаться не на имя, а на индекс подпрограммы. Замечу, что в отличие от модулей Delphi не компилирует DLL автоматически в режимах make или build, т. к. справедливо рассматривает ее как другую программу, никак не связанную в момент компиляции с основной программой. 13.3. ПРИМЕР Рассмотрим пример создания DLL, в котором иллюстрируются различные приемы объявления экспортируемых подпрограмм. Для примера выбран модуль cmpix, описанный в гл. 12. В его состав входят 4 процедуры, реализующие действия с комплексными числами. Вариант соответствующей DLL показан ниже.
Для создания заготовки библиотечного модуля выберите опцию меню File | New | Unit или в окне репозитория щелкните по пиктограмме Da. В ответ Delphi откроет специальное окно проекта с длинным комментарием, в котором указывается на необходимость вставить ссылку на модуль ShareMem, если библиотека экспортирует длинные строки в параметрах обращения к подпрограммам или как результат функций. Эта ссылка должна быть первой как в предложении uses библиотеки, так и в uses файла проекта программы, которая использует эту библиотеку. Если подпрограммы библиотеки экспортируют строки ShortString или PChar, ссылаются на ShareMem не обязательно. Сразу же сохраните проект под именем Сmрiх, чтобы Delphi автоматически исправила имя библиотеки в предложении Library.
library Cmpix;
uses
SysUtils, Classes;
{$R *.RES}
type
TComplex = record Re, Im: Real;
end;
function AddC(x, y: TComplex): TComplex; stdcall;
begin
Result.Im := x.Im + y.Im;
Result.Re := x.Re + y.Re
end;
function SubC(x, y: TComplex): TComplex; stdcall;
begin
Result.Im := x.Im - y.Im;
Result.Re := x.Re - y.Re
end;
function MulC(x, у: TComplex): TComplex; stdcall;
begin
Result.Re := x.Re * y.Re + x.Im * y.Im;
Result.Im := x.Re * y.Im - x.Im * y.Re
end;
function DivC(x, y: TComplex): TComplex; stdcall;
var
z: Real;
begin
z := sqr(y.Re) + sqr(y.Im);
try
Result.Re := (x.Re * y.Re + x.Im * y.Im) / z;
Result.Im := (x.Re * y.Im - x.Im * y.Re) / z
except
Result.Re := le + 309;
Result.Im := le + 309
end
end;
exports
AddC index 1 name 'ADDC' resident,
SubC index 2,
MulC index 3,
DivC index 4;
begin
end.
Обратите внимание: все функции нашей DLL используют соглашение stdcall, которое обеспечивает совместимость новых функций с функциями API Windows 32. Мы могли бы не указывать это соглашение; в этом случае компилятор использовал бы более эффективное соглашение register, но обращение к нашей DLL из программ, написанных на других языках программирования, в общем случае стало бы невозможным.
Если вы создали DLL для "внешнего" исользования (внеDе1рhi), объявляйте подпрограммы с директивой stdcall или safecall!
Для использования подпрограмм из DLL необходимо описать их как внешние, добавив за словом External имя библиотеки в апострофах:
procedure MyProc; External 'MyDLL';Как уже говорилось, подпрограмма вызывается по имени или по индексу. В нашем примере из библиотеки MyDLL вызывается подпрограмма с внешним именем 'мургос'. Если нужно сослаться на индекс подпрограммы, за именем библиотеки указывается слово index и индекс:
procedure MyProc; External 'MyDLL' index 2;В этом случае имя, под которым подпрограмма будет известна программе, может не совпадать с ее внешним DLL-именем. Впрочем, программист может и явно переопределить имя подпрограммы, даже если он ссылается на ее внешнее имя:
procedure MyProc; External 'MyDLL' Name 'ExtName';В этом варианте предполагается, что экспортируется процедура с внешним именем ' ExtName '. После любого из указанных выше объявлений экспортируемая подпрограмма становится доступна программе и может вызываться в ней как обычная подпрограмма Object Pascal. 13.4. ИСПОЛЬЗОВАНИЕ 13.4.1. Статическая загрузка
В следующей программе используется библиотека Сmрiх, описанная на предыдущей странице.
type
TComplex = record
Re, Im: Real;
end;
function ADDC(x, y: TComplex): TComplex; stdcall; external 'Cmplx';
function SubC(x, y: TComplex): TComplex; stdcall; external 'Cmplx';
function MulC(x, y: TComplex): TComplex; stdcall; external 'Cmplx';
function DivC(x, y: TComplex): TComplex; stdcall; external 'Cmplx';
procedure TfmExample.bbRunClick(Sender: TObject);
var
x, y, z: TComplex;
..... // Далее смотри текст обработчика bbRunClick в п.12.6 end; end.
Обратите внимание: библиотечная функция cmpixAdd имеет внешнее имя addc. Именно так (заглавными буквами) описана эта функция в приведенном выше примере. Если бы мы использовали
function AddC(x, у: TComplex): TComplex; stdcall; External'Cmplx'; компоновщик не смог бы ее идентифицировать. 13.4.2. Динамическая загрузка Описанный выше способ определения функций и процедур DLL (с помощью директивы External) заставит компилятор поместить в заголовок программы список всех DLL, и загрузчик загрузит библиотеки в память одновременно с загрузкой самой программы. Программа может загружать DLL и без External с помощью трех Стандартных функций : LoadLibrary, GetProcAddress И FreeLibrary.
Следующий пример иллюстрирует технику такой загрузки DLL Cmplx:
type
TComplex = record
Re, Im: Real;
end;
TComplexFunc = function(x, y: TComplex): TComplex; stdcall;
procedure TfmExample.bbRunClick(Sender: TObject);
var
x, y, z: TComplex;
AddC, SubC, MulC, DivC: TComplexFunc;
Handle: LongWord;
procedure Output(Operation: Char);
....//Тело процедуры Output остается без изменений
end; //Output
begin //bbRunClick
// Загружаем библиотеку CMPLX.DLL Handle := LoadLibrary('Cmplx.dll');
if Handle = 0 then
begin
ShowMessage('He найдена библиотека CMPLX.DLL');
Halt
end;
{Определяем адреса функций. Три первые вызываем по индексу,
четвертую - по имени. При вызове по индексу младшее слово
PChar должно содержать индекс, поэтому делаем приведение типов:}
@AddC := GetProcAddress(Handle, PChar(Longint(1)));
PSubC := GetProcAddress(Handle, PChar(Longint(2)));
@MulC := GetProcAddress(Handle, PChar(Longint(3)));
@DivC := GetProcAddress(Handle, 'DivC');
x.re := Random;
x.im := Random;
y.re := Random;
y.im := Random;
Output('+');
Output('-');
Output('*');
Output('/');
mmOutput.Lines.Add('');
// Освобождаем библиотеку FreeLibrary(Handle)
end;
13.4.3. Интерфейсный модуль
При вызове DLL-подпрограмм в большинстве случаев бывает необходимо передавать структурированные параметры типа записей, как тип TComplex в предыдущем примере. Поскольку DLL не могут экспортировать типы, приходится объявлять эти типы в вызывающей программе. Если вы часто обращаетесь в своих программах к той или иной DLL, удобно создать интерфейсный модуль, содержащий объявления как подпрограмм, так и связанных с ними типов. Например:
unit Complx;
interface
type
TComplex = record
Re, Im: Real;
end;
function AddC(x, y: TComplex): TComplex; stdcall; External 'Cmplx' index 1;
function SubC(x, y: TComplex): TComplex; stdcall; External 'Cmplx' index 2;
function MulC(x, y: TComplex): TComplex; stdcall; External 'Cmplx' index 3;
function DivC(x, y: TComplex): TComplex; stdcall; External 'Cmplx' index 4;
implementation
end.
Такой интерфейсный модуль существенно упрощает разработку основной программы: в нашем примере он обеспечивает такой же интерфейс к библиотеке cmpix, как описанный выше модуль cmpix к своим объектам.
При обращении к подпрограммам DLL, написанным на других языках программирования, может оказаться, что внешнее имя подпрограммы содержит символы, которые не могут содержаться в правильном идентификаторе Delphi. Например, язык C++ разрешает использовать в идентификаторах символ '@'. В этом случае (а также если вы хотите переименовать экспортируемую из DLL подпрограмму) именуйте подпрограмму любым правильным с точки зрения Delphi идентификатором и укажите истинное имя подпрограммы после слова name. Например:
function MyFunction: WordBool; stdcall; external 'MyDLL' name '_MyFunction@12'13.5. ВКЛЮЧЕНИЕ В БИБЛИОТЕКУ ФОРМ Несмотря на то, что DLL не имеет собственной формы, с ее помощью можно вызывать формы из связанных с библиотекой модулей. Для этого в библиотеке используется ссылка uses на связанные модули-формы и объявляются экспортируемые из DLL подпрограммы, в которых реализуется вызов соответствующих форм. В следующем примере иллюстрируется техника включения в DLL формы и использования ее в вызывающей программе.
Текст DLL
library DLLWithForm;
uses
SysUtils,
Classes,
DLLFormU in 'DLLFormU.pas' {DLLForm};
{$R *.RES}
exports
ShowModalForm, ShowForm, FreeForm;
begin
end.
Текст формы в DLL
unit DLLFormU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons;
type
TDLLForm = class(TForm)
BitBtnl: TBitBtn;
BitBtn2: TBitBtn;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
CallForm: THandle; //Дескриптор вызывающей формы
public
{ Public declarations }
end;
// Объявление экспортируемых подпрограмм
function ShowModalForm: Integer;
procedure ShowForm(aHandle: THandle);
procedure FreeForm;
var
DLLForm: TDLLForm;
implementation
{$R *.DFM}
function ShowModalForm: Integer;
// Модальный вызов
begin
DllForm := TDllForm.Create(Application);
Result := DLLForm.ShowModal;
DLLForm.Free;
end;
procedure ShowForm(Appl, Form: THandle);
// Немодальный вызов
begin
Application.Handle := Appl; // Замена объекта
Application DllForm := TDllForm.Create(Application);
// Запоминаем дескриптор вызывающего окна для посылки
// ему сообщения о закрытии
CallForm := Form;
DLLForm.Show
end;
procedure FreeForm;
// Уничтожение формы
begin
DLLForm.Free
end;
procedure TDLLForm.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
if CallFormoO then
SendMessage(CallForm, wm_User, 0, 0)
end;
end.
Текст вызывающей программы
unit TestMainU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TTestMain = class(TForm)
Buttoni: TButton; // Открыть в модальном режиме
Button2: TButton; // Открыть в немодальном режиме
Button3: TButton; // Закрыть окно
label
I: TLabel;
procedure ButtonlClick(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure ButtonSClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure WMUser(var Msg: TMessage); message WM_USER;
end;
var
TestMain: TTestMain;
implementation
{$R *.DFM}
function ShowModalForm: Integer; external 'DLLWithForm';
procedure ShowForm(Appl, Form: THandle); external ' DLLWithForm';
procedure FreeForm; external 'DLLWithForm';
procedure TTestMain.ButtonlClick(Sender: TObject);
// Модальный вызов
begin
Button2.Enabled := False;
labell.Caption := 'ModalResult = ' + IntToStr(ShowModalForm);
labell.Show; // Показываем результат вызова
Button2.Enabled := True
end;
procedure TTestMain.Button2Click(Sender: TObject);
// Немодальный вызов
begin
Buttoni.Enabled := = False;
Button2.Enabled := False;
Buttons.Enabled := True;
label
1. Hide;
ShowForm(Application.Handle, Self.Handle);
end;
procedure TTestMain.Button3Click(Sender: TObject);
// Закрыть форму
begin
FreeForm;
Button1.Enabled := True;
Button2.Enabled := True;
Button3.Enabled := False;
end;
procedure TTestMain.WMUser(var Msg: TMessage);
// Сообщение из формы DLL о ее закрытии
begin
Buttons.Click
end;
end.
Модуль формы DLLForm, помещенной в DLL, ссылается на стандартный модуль Forms и таким образом получает свой глобальный объект Application, который ничего 'не знает' о глобальном объекте вызывающей программы (см. гл. 21). В режиме модального вызова это не имеет особого значения, т. к. модальное окно блокирует работу вызывающей программы. В режиме немодального вызова следует синхронизовать действия объектов, в противном случае минимизация главного окна, например, не приведет к минимизации окна DLL. Синхронизация достигается тем, что дескриптор объекта Application DLL заменяется на соответствующий дескриптор вызывающей программы.
При показе формы в немодальном режиме она может быть закрыта щелчком по собственной системной кнопке закрыть. В этом случае она должна каким-то образом известить вызывающую программу об этом событии. Для этого используется стандартный механизм посылки вызывающей форме Windows-сообщения. Сообщение должно иметь адрес, в роли которого используется дескриптор окна, получающего это сообщение. Вот почему вторым параметром обращения к функции ShowForm в DLL передается и в поле CallForm: запоминается дескриптор вызывающего окна. Обработчик события enclose формы проверяет это поле и, если оно определено, посылает вызывающему окну сообщение с индексом wm_user. В вызывающей программе предусмотрен обработчик этого сообщения, в котором реализуются необходимые действия.
DLL и ресурсы.
| Автор: Mike Leftwich (Ensemble Corp) |
| Устроился программист на работу в Мосэнерго. В первый же день проблема: ввели в строй новый жилой микрорайон, стало не хватать мощности. Что делать? Все чешут затылки, ничего не могут придумать. Программист: - Да что вы маетесь-то? Ресурсов системы не хватает? А библиотеки отключать пробовали? |
Надеюсь я помог вам.
Отслеживание из DLL ее присоединение к процессу и отсоединение.
Автор: Xavier Pacheco
library DLLEntryLib;
uses
SysUtils,
Windows,
Dialogs,
Classes;
procedure DLLEntryPoint(dwReason: DWord);
begin
case dwReason of
DLL_PROCESS_ATTACH: ShowMessage('Attaching to process');
DLL_PROCESS_DETACH: ShowMessage('Detaching from process');
DLL_THREAD_ATTACH: MessageBeep(0);
DLL_THREAD_DETACH: MessageBeep(0);
end;
end;
begin
{ First, assign the procedure to the DLLProc variable }
DllProc := @DLLEntryPoint;
{ Now invoke the procedure to reflect that the DLL is attaching to the
process }
DLLEntryPoint(DLL_PROCESS_ATTACH);
end.
{
Copyright © 1999 by Delphi 5 Developer's Guide - Xavier Pacheco and Steve Teixeira
}
unit MainFrm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ComCtrls, Gauges;
type
{ Define a TThread descendant }
TTestThread = class(TThread)
procedure Execute; override;
procedure SetCaptionData;
end;
TMainForm = class(TForm)
btnLoadLib: TButton;
btnFreeLib: TButton;
btnCreateThread: TButton;
btnFreeThread: TButton;
lblCount: TLabel;
procedure btnLoadLibClick(Sender: TObject);
procedure btnFreeLibClick(Sender: TObject);
procedure btnCreateThreadClick(Sender: TObject);
procedure btnFreeThreadClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
LibHandle: THandle;
TestThread: TTestThread;
Counter: Integer;
GoThread: Boolean;
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TTestThread.Execute;
begin
while MainForm.GoThread do
begin
Synchronize(SetCaptionData);
Inc(MainForm.Counter);
end;
end;
procedure TTestThread.SetCaptionData;
begin
MainForm.lblCount.Caption := IntToStr(MainForm.Counter);
end;
procedure TMainForm.btnLoadLibClick(Sender: TObject);
{ This procedure loads the library DllEntryLib.DLL }
begin
if LibHandle = 0 then
begin
LibHandle := LoadLibrary('DLLENTRYLIB.DLL');
if LibHandle = 0 then
raise Exception.Create('Unable to Load DLL');
end
else
MessageDlg('Library already loaded', mtWarning, [mbok], 0);
end;
procedure TMainForm.btnFreeLibClick(Sender: TObject);
{ This procedure frees the library }
begin
if not (LibHandle = 0) then
begin
FreeLibrary(LibHandle);
LibHandle := 0;
end;
end;
procedure TMainForm.btnCreateThreadClick(Sender: TObject);
{ This procedure creates the TThread instance. If the DLL is loaded a
message beep will occur. }
begin
if TestThread = nil then
begin
GoThread := True;
TestThread := TTestThread.Create(False);
end;
end;
procedure TMainForm.btnFreeThreadClick(Sender: TObject);
{ In freeing the TThread a message beep will occur if the DLL is loaded. }
begin
if not (TestThread = nil) then
begin
GoThread := False;
TestThread.Free;
TestThread := nil;
Counter := 0;
end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
LibHandle := 0;
TestThread := nil;
end;
end.
Пример обратного вызова DLL.
Обратный вызов (callback) для NotifyRegister должен находиться в DLL. Вам не нужен MakeProcInstance, просто добавьте "export" к объявлению функции. Нижеследующий код является простым примером DLL для Delphi. Вы должны запустить все тесты вне Delphi или же ваша программа не получит никаких уведомлений (notifications). Все это выглядит похожим на Delphi, зарегистрировавшей перехват с Toolhelp...
library Lib1;
uses WinTypes, Messages, WinProcs, Toolhelp;
const
WM_NOTIFY = WM_USER + $100;
var
targetHWnd: HWnd;
function HookProc(wID: Word; dwData: LongInt): Bool; export;
begin
PostMessage(targetHWnd, WM_NOTIFY, wID, dwData);
Result := False
end;
procedure InstallHook(notifyWindow: HWnd); export;
begin
if targetHWnd = 0 then
begin
if not NotifyRegister(0, HookProc, NF_NORMAL) then
begin
MessageBox(notifyWindow, 'Неудача NotifyRegister!',
'Ошибка!', MB_OK + MB_ICONSTOP);
end
else
begin
targetHWnd := notifyWindow;
end;
end;
end;
procedure UnInstallHook; export;
begin
if targetHWnd <> 0 then
begin
NotifyUnregister(0);
targetHWnd := 0;
end;
end;
exports
InstallHook,
UnInstallHook;
begin
targetHWnd := 0;
end.
|
Пример обратного вызова DLL 2.
Вот как я осуществляю обратный вызов процедуры Delphi из C++ DLL:В Delphi:
interface
var
callBackProc: TFarProc;
procedure delphiProc(const x: Pchar); export;
procedure setupDLL(p: pointer);
implementation
procedure setupDLL(p: Pointer); external 'MYDLL';
procedure delphiProc(const x: Pchar); { это вызываемая процедура }
begin
...
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
...
callBackProc := makeProcInstance(@delphiProc, gInstance);
setupDLL(callBackProc);
...
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
...
freeProcInstance(callBackProc);
...
end;
|
static void CALLBACK (*saveProc)(char*);
void FAR _export pascal setupDLL (void CALLBACK (*func)(char*))
{
saveProc = func;
}
затем, для вызова процедуры Delphi из C++ DLL, используется:
(*saveProc)(msg); // где msg - char*
Вы не должны иметь вызовы makeProcInstance и freeProcInstance именно там, где я их показал, вам просто необходимо убедиться в том, что вы сделали makeProcInstance и установили вызовы прежде, чем используется обратный вызов, а freeProcInstance необходимо вызвать в последнюю очередь, после того, как был использован обратный вызов.
Если ваш DLL родом из Delphi, код должен быть аналогичным, за исключением того, ...что я не помню как вызывать процедуру в Delphi через указатель на эту процедуру.
Если ваш DLL родом не из Delphi, убедитесь в том, что типы string/pchar согласованы в обоих модулях.
Синтаксис тот же. В главной программе вы просто добавляете ключевое слово EXPORT к каждой функции, которую вы хотите экспортировать, наподобие того, как вы делаете это в DLL. В конце .DPR-файла, перед ключевым словом BEGIN, которое определяет начало основной программы, добавьте секцию с именем EXPORTS, где вы должны перечислить подпрограммы, которые вы хотите сделать доступными для обратных вызовов. Не такой очевидный, но очень важный шаг, который вы должны сделать, это скомпилировать главую программу с выключенной опцией умных обратных вызовов (smart callbacks). Очень важно, чтобы методы в программе имели доступ к своим собственным данным.
Сегменты данных DLL.
Автор: Peter BelowЯ надеюсь что у вас имеются эти две небольшие строки в главных модулях ваших DLL (если он не содержит обработку прерываний):
(* делаем фиксированные сегменты данных DLL перемещаемыми *) GlobalPageUnlock( DSeg ); GlobalReAlloc(DSeg, 0, GMEM_MODIFY or GMEM_MOVEABLE); |
Если вы не будете размещать сегменты данных DLL в нижней части памяти DOS, то вы сможете сэкономить весьма скудные (и важные!) ресурсы...
Разработка DLL в среде Borland Delphi.
Если ваш компьютер работает под управлением операционной системы Windows, то вы не можете не знать о существовании динамических подсоединяемых библиотек (dynamic link libraries - DLL). Достаточно взглянуть на список файлов, расположенных в системном каталоге Windows - порой количество используемых операционной системой динамических библиотек достигает нескольких сотен. DLL являются неотъемлемой частью функционирования операционных систем семейства Microsoft Windows. Однако для вас может быть неочевидна необходимость использования динамических библиотек при разработке приложений. В рамках данной статьи мы поговорим о принципах функционирования DLL и их использования в процессе создания ваших собственных программ.
Для начала давайте выясним, что собой представляет динамическая подключаемая библиотека. Итак, DLL - это один или несколько логически законченных фрагментов кода, сохраненных в файле с расширением.dll. Этот код может быть запущен на выполнение в процессе функционирования какой-либо другой программы (такие приложения называются вызывающими по отношению к библиотеке), но сама DLL не является запускаемым файлом.
Существует два типа динамических библиотек - исполняемые и библиотеки ресурсов. Однако это не означает, что в одном файле не может находиться и код некоторой функции и какие-либо ресурсы. Просто иногда бывает удобно разнести реализацию исполняемых процедур и используемые приложением ресурсы в разные файлы.
Итак, процедуры и функции, содержащиеся в динамической библиотеке, можно разделить на два типа: те, которые могут быть вызваны из других приложений. Рассмотрим следующий пример:
Screen.Cursors[myCursor] := LoadCursor(HInstance, MYCURSOR'); |
LoadCursor - функция Windows API, которая вызывается приложением из динамической библиотеки User 32.dll. Кстати, примером хранимых в динамической библиотеке ресурсов могут являться такие стандартные диалоги Windows, как диалог открытия файла, диалог печати или настройки принтера. Эти диалоги находятся в файле Comctl32.dll. Однако многие прикладные разработчики используют функции вызова форм этих диалогов, совершенно не задумываясь, где хранится их описание.
Второй тип процедур - это те, которые используются только внутри самого файла библиотеки.
Аргументы в пользу использования DLL
Итак, прежде чем перейти к обсуждению структуры динамических библиотек, необходимо поговорить о тех преимуществах, которые предоставляет их использование разработчику. Во-первых, это повторное использование кода. Думаю, нет необходимости пояснять удобство использования один раз разработанных процедур и функций при создании нескольких приложений? Кроме того, в дальнейшем вы сможете продать некоторые из своих библиотек, не раскрывая исходных кодов. А чем тогда это лучше компонентов, спросите вы? А тем, что функции, хранящиеся в библиотеке, могут быть вызваны на выполнение из приложений, разработанных не на Object Pascal, а, например, с использованием C++Builder, Visual Basic, Visual C++ и т.д. Такой подход накладывает некоторые ограничения на принцип разработки библиотеки, но это возможно. Звучит заманчиво? Мне кажется, даже очень. Но это еще не все.
Во-вторых, использование DLL предоставляет возможность использования один загруженного в оперативную память кода несколькими приложениями. К примеру, если вы разрабатываете программное обеспечение для большого предприятия, то вполне возможно, что в различных созданных вами приложениях будут использоваться одни и те же функции, процедуры, формы и другие ресурсы. Естественно, что при выделении общих для нескольких приложений данных в DLL может привести к экономии как дискового пространства, так и оперативной памяти, иногда очень даже существенному.
В-третьих, следует поговорить вот о чем. Всего несколько лет назад при разработке программного обеспечения вы могли совершенно не волноваться относительно распространения ваших продуктов где-либо, кроме вашей страны. Я хочу сказать, что проблема перевода на другие языки текста на элементах управления (пункты меню, кнопки, выпадающие списки, подсказки), сообщений об ошибках и т.д. не стояла так остро, как сейчас. Однако, с повсеместным внедрением интернета у вас появилась возможность быстрой передачи готовых программных продуктов практически в любую точку мира. И что будут делать с вашей программой где-нибудь в Объединенных Арабских Эмиратах, если кроме как по-русски, она с пользователем общаться не умеет? Вы сами можете оценить этот эффект, если хоть раз на экране вашего компьютера вместо с детства знакомого русского языка появляется "арабская вязь" (например, из-за "сбоя" шрифтов). Итак, уже сейчас вы должны планировать возможность распространения ваших приложений в других странах (если, конечно, у вас есть желание получить как можно больше прибыли). Соответственно, встает вопрос быстрого перевода интерфейса вашей программы на другие языки. Одним из путей может являться создание ресурсов интерфейсов внутри DLL. К примеру, можно создать одно приложение, которое в зависимости от версии динамической библиотеки будет выводить сообщения на различных языках.
Естественно, выше приведены лишь некоторые из аргументов в пользу использования динамически подключаемых библиотек при разработке приложений. Итак, надеюсь, вы все еще заинтересованы в том, чтобы узнать, как, собственно, DLL создаются. Если так, то вперед.
Основы разработки DLL
Разработка динамических библиотек не представляет собой некий сверхсложный процесс, доступный лишь избранным. Если вы достаточно хорошо знакомы с разработкой приложений на Object Pascal, то вам не составит особого труда научиться работать с механизмом DLL. Итак, рассмотрим те особенности создания DLL, которые вам необходимо знать, а в завершении статьи разработаем свою собственную библиотеку.
Как и любой другой модуль, модуль динамической библиотеки имеет фиксированный формат. Взгляните на листинг, представленный ниже.
library MyFirstDLL; uses SysUtils, Classes, Forms, Windows; procedure HelloWorld(AForm : TForm); begin MessageBox(AForm.Handle, 'Hello world!', 'DLL Message Box', MB_OK or MB_ICONEXCLAMATION); end; exports HelloWorld; begin end. |
Первое, на что следует обратить внимание, это ключевое слово library, находящееся вверху страницы. Library определяет этот модуль как модуль библиотеки DLL. Далее идет название библиотеки. В нашем примере мы имеем дело с динамической библиотекой, содержащей единственную процедуру: HelloWorld. Причем обратите внимание, что данная процедура по структуре ничем не отличается от тех, которые вы помещаете в модули своих приложений. Ключевое слово exports сигнализирует компилятору о том, что перечисленные ниже функции и/или процедуры должны быть доступны из вызывающих приложений (т.е. они как бы "экспортируются" из библиотеки). Подробнее о механизме экспорта мы поговорим чуть позже.
И, наконец, в конце модуля можно увидеть ключевые слова begin и end. Внутри данного блока вы можете поместить код, который должен выполняться в процессе загрузки библиотеки. Достаточно часто этот блок остается пустым.
Как уже говорилось выше, все процедуры и функции, помещаемые в DLL, могут быть разделены на две группы: экспортируемые (вызываемые из других приложений) и локальные. Естественно, внутри библиотеки также могут быть описаны классы, которые в свою очередь содержат методы, но в рамках данной статьи я не буду на этом останавливаться.
Описание и реализация процедур и функций, вызываемых в пределах текущей DLL, ничем не отличаются от их аналогов в обычных проектах-приложениях. Их специфика заключается лишь в том, что вызывающая программа не будет иметь к ним доступа. Она просто не будет ничего знать об их существования, так же, как одни классы ничего не знают о тех методах, которые описаны в секции private других классов.
В дополнение к процедурам и функциям, DLL может содержать глобальные данные, доступ к которым разрешен для всех процедур и функций в библиотеке. Для 16-битных приложений эти данные существовали в единственном экземпляре независимо от количества загруженных в оперативную память программ, которые используют текущую библиотеку. Другими словами, если одна программа изменяет значение глобальной переменной a на 100, то для всех остальных приложений a будет значение 100. Для 32-битных приложений это не так. Теперь для каждого приложения создается отдельная копия глобальной области данных.
Экспорт функций из DLL
Как уже говорилось выше, для экспорта процедур и функций из DLL, необходимо использовать ключевое слово export. Еще раз обратите внимание на представленный выше листинг библиотеки MiFirstDll. Поскольку процедура HelloWorld определена как экспортируемая, то она может быть вызвана на выполнение из других библиотек или приложений. Существуют следующие способы экспорта процедур и функций: экспорт по имени и экспорт по порядковому номеру.
Наиболее распространенный способ экспорта - по имени. Взглянем на приведенный ниже текст:
exports SayHello, DoSomething, DoSomethingReallyCool; |
Следует обратить внимание на то, что Delphi автоматически назначает порядковый номер каждой экспортируемой функции (процедуре) независимо от того, определяете вы его явно или нет. Явное определение индекса позволяет вам лично управлять порядковым номером экспортируемой функции или процедуры.
Для того, чтобы определить выполняется ли ваш кодек в DLL или в вызывающем приложении, можно воспользоваться глобальной переменной IsLibrary. Она принимает значение true в том случае, если код вызывается из библиотеки и false в случае выполнения процедуры или функции из вызывающего приложения.
Кроме этого, в поставку Delphi входит весьма полезная утилита tdump, которая предоставляет данные о том, какая информация экспортируется из указанной DLL.
Использование DLLProc
Выше я уже говорил о том, что код инициализации динамической библиотеки может быть помещен в блок begin...end. Однако кроме этого зачастую необходимо предусмотреть некоторые действия, выполняемые в процессе выгрузки DLL из оперативной памяти. В отличии от других типов модулей, модуль DLL не имеет ни секции initialization, ни секции finalization. К примеру, вы можете динамически выделить память в главном блоке, однако не понятно, где эта память должна быть освобождена. Для решения этой проблемы существует DLLProc - специальная процедура, вызываемая в определенные моменты функционирования DLL.
Для начала следует сказать о самой причине существования DLLProc. Динамическая библиотека получает сообщения от Windows в моменты своей загрузки и выгрузки из оперативной памяти, а также в тех случаях, когда какой-нибудь очередной процесс, использующий функции и/или ресурсы, хранящиеся в библиотеке, загружается в память. Такая ситуация возможно в том случае, когда библиотека необходима для функционирования нескольких приложений. А для того, чтобы вы имели возможность указывать, что именно должно происходить в такие моменты, необходимо описать специальную процедуру, которая и будет ответственна за такие действия. К примеру, она может выглядеть следующим образом:
procedure MyFirstDLLProc(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
{DLL is unloading. Cleanup code here.}
end;
|
Однако системе совершенно не очевидно, что именно процедура MyFirstDllProc ответственна за обработку рассмотренных выше ситуаций. Поэтому вы должны поставить в соответствие адрес нашей процедуры глобальной переменной DLLProc. Это необходимо сделать в блоке begin...end примерно так:
begin
DLLProc := @MyDLLProc;
{ Что-нибудь еще, что должно выполняться в
процессе инициализации библиотеки }
end.
|
Ниже представлен код, демонстрирующий один из возможных вариантов применения DLLProc.
library MyFirstDLL;
uses
SysUtils, Classes, Forms, Windows;
var
SomeBuffer: Pointer;
procedure MyFirstDLLProc(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
{DLL is выгружается из памяти.
Освобождаем память, выделенную под буфер.}
FreeMem(SomeBuffer);
end;
procedure HelloWorld(AForm: TForm);
begin
MessageBox(AForm.Handle, 'Hello world!',
'DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
{Какой-нибудь код, в котором используется SomeBuffer.}
exports
HelloWorld;
begin
{Ставим в соответствие переменной
DLLProc адрес нашей процедуры.}
DLLProc := @MyFirstDLLProc;
SomeBuffer := AllocMem(1024);
end.
|
Как можно увидеть, в качестве признака того или иного события, в результате которого вызывается процедура MyFirstDll, является значение переменной Reason. Ниже приведены возможные значения этой переменной.
DLL_PROCESS_DETACH библиотека выгружается из памяти; используется один раз; DLL_THREAD_ATTACH в оперативную память загружается новый процесс, использующий ресурсы и/или код из данной библиотеки; DLL_THREAD_DETACH один из процессов, использующих библиотеку, "выгружается" из памяти.Загрузка DLL
Прежде чем начать использование какой-либо процедуры или функции, находящейся в динамической библиотеке, вам необходимо загрузить DLL в оперативную память. Загрузка библиотеки может быть осуществлена одним из двух способов: статическая загрузка и динамическая загрузка. Оба метода имеют как преимущества, так и недостатки.
Статическая загрузка означает, что динамическая библиотека загружается автоматически при запуске на выполнение использующего ее приложения. Для того чтобы использовать такой способ загрузки, вам необходимо воспользоваться ключевым словом external при описании экспортируемой из динамической библиотеки функции или процедуры. DLL автоматически загружается при старте программы, и Вы сможете использовать любые экспортируемые из нее подпрограммы точно так же, как если бы они были описаны внутри модулей приложения. Это наиболее легкий способ использования кода, помещенного в DLL. Недостаток метода заключается в том, что если файл библиотеки, на который имеется ссылка в приложении, отсутствует, программа откажется загружаться.
Смысл динамического метода заключается в том, что вы загружаете библиотеку не при старте приложения, а в тот момент, когда вам это действительно необходимо. Сами посудите, ведь если функция, описанная в динамической библиотеке, используется только при 10% запусков программы, то совершенно нет смысла использовать статический метод загрузки. Выгрузка библиотеки из памяти в данном случае также осуществляется под вашим контролем. Еще одно преимущества такого способа загрузки DLL - это уменьшение (по понятным причинам) времени старта вашего приложения. А какие же у этого способа имеются недостатки? Основной, как мне кажется, - это то, что использование данного метода является более хлопотным, чем рассмотренная выше статическая загрузка. Сначала вам необходимо воспользоваться функцией Windows API LoadLibrary. Для получения указателя на экспортируемой процедуры или функции должна использоваться функция GetProcAddress. После завершения использования библиотеки DLL должна быть выгружена с применением FreeLibrary.
Вызов процедур и функций, загруженных из DLL.
Способ вызова процедур и функций зависит от того, каким образом вы загрузили динамическую библиотеку, в которой эти подпрограммы находятся.
Вызов функций и процедур из статически загруженных DLL достаточно прост. Первоначально в приложении должно содержаться описание экспортируемой функции (процедуры). После этого вы можете их использовать точно так же, как если бы они были описаны в одном из модулей вашего приложения. Для импорта функции или процедуры, содержащейся в DLL, необходимо использовать модификатор external в их объявлении. К примеру, для рассмотренной нами выше процедуры HelloWorld в вызывающем приложении должна быть помещена следующая строка:
procedure SayHello(AForm : TForm); external myfirstdll.dll'; |
Ключевое слово external сообщает компилятору, что данная процедура может быть найдена в динамической библиотеке (в нашем случае - myfirstdll.dll). Далее вызов этой процедуры выглядит следующим образом:
... HelloWorld(self); ... |
При импорте функции и процедур будьте особенно внимательны при написании их имен и интерфейсов! Дело в том, что в процессе компиляции приложения не производится проверки на правильность имен объектов, экспортируемых из DLL, осуществляться не будет, и если вы неправильно описали какую-нибудь функцию, то исключение будет сгенерировано только на этапе выполнения приложения.
Импорт из DLL может проводиться по имени процедуры (функции), порядковому номеру или с присвоением другого имени.
В первом случае вы просто объявляете имя процедуры и библиотеку, из которой ее импортируете (мы это рассмотрели чуть выше). Импорт по порядковому номеру требует от вас указание этого самого номера:
procedure HelloWorld(AForm : TForm); external myfirstdll.dll' index 15; |
В этом случае имя, которое вы даете процедуре при импорте не обязательно должно совпадать с тем, которое было указано для нее в самой DLL. Т.е. приведенная выше запись означает, что вы импортируете из динамической библиотеки myfirstdll.dll процедуру, которая в ней экспортировалась пятнадцатой, и при этом в рамках вашего приложения этой процедуре дается имя SayHello.
Если вы по каким-то причинам не применяете описанный выше способ импорта, но тем не менее хотите изменить имя импортируемой функции (процедуры), то можно воспользоваться третьим методом:
procedure CoolProcedure; external myfirstdll.dll' name DoSomethingReallyCool'; |
Здесь импортируемой процедуре CoolProcedure дается имя DoSomethingReallyCool. Вызов процедур и функций, импортируемых из динамически загружаемых библиотек несколько более сложен, чем рассмотренный нами выше способ. В данном случае требуется объявить указатель на функцию или процедуру, которую вы собираетесь использовать. Помните процедуру HelloWorld? Давайте посмотрим, что необходимо сделать для того, чтобы вызвать ее на выполнение в случае динамической загрузки DLL. Во-первых, вам необходимо объявить тип, который описывал бы эту процедуру:
type THelloWorld = procedure(AForm : TForm); |
Теперь вы должны загрузить динамическую библиотеку, с помощью GetProcAddress получить указатель на процедуру, вызвать эту процедуру на выполнение, и, наконец, выгрузить DLL из памяти. Ниже приведен код, демонстрирующий, как это можно сделать:
var
DLLInstance : THandle;
HelloWorld : THelloWorld;
begin
{ загружаем DLL }
DLLInstance := LoadLibrary(myfirstdll.dll');
{ получаем указатель }
@HelloWorld := GetProcAddress(DLLInstance, HelloWorld');
{ вызываем процедуру на выполнение }
HelloWorld(Self);
{ выгружаем DLL из оперативной памяти }
FreeLibrary(DLLInstance);
end;
|
Как уже говорилось выше, одним из недостатков статической загрузки DLL является невозможность продолжения работы приложения при отсутствии одной или нескольких библиотек. В случае с динамической загрузкой у вас появляется возможность программно обрабатывать такие ситуации и не допускать, чтобы программа "вываливалась" самостоятельно. По возвращаемому функциями LoadLibrary и GetProcAddress значениям можно определить, успешно ли прошла загрузка библиотеки и найдена ли в ней необходимая приложению процедура. Приведенный ниже код демонстрирует это.
procedure TForm1.DynamicLoadBtnClick(Sender: TObject);
type
THelloWorld = procedure(AForm : TForm);
var
DLLInstance : THandle;
HelloWorld : THelloWorld;
begin
DLLInstance := LoadLibrary(myfirstdll.dll');
if DLLInstance = 0 then
begin
MessageDlg(Невозможно загрузить DLL', mtError, [mbOK], 0);
Exit;
end;
@HelloWorld := GetProcAddress(DLLInstance, HelloWorld');
if @HelloWorld <> nil then
HelloWorld (Self)
else
MessageDlg(Не найдена искомая процедура!.', mtError, [mbOK], 0);
FreeLibrary(DLLInstance);
end;
|
В DLL можно хранить не только код, но и формы. Причем создание и помещение форм в динамическую библиотеку не слишком сильно отличается от работы с формами в обычном проекте. Сначала мы рассмотрим, каким образом можно написать библиотеку, содержащую формы, а затем мы поговорим об использовании технологии MDI в DLL.
Разработку DLL, содержащую форму, я продемонстрирую на примере.
Итак, во-первых, создадим новый проект динамической библиотеки. Для этого выберем пункт меню File|New, а затем дважды щелкнем на иконку DLL. После этого вы увидите примерно следующий код:
library Project2;
{здесь были комментарии}
uses
SysUtils, Classes;
{$R *.RES}
begin
end.
|
Сохраните полученный проект. Назовем его DllForms.dpr.
Теперь следует создать новую форму. Это можно сделать по-разному. Например, выбрав пункт меню File|New Form. Добавьте на форму какие-нибудь компоненты. Назовем форму DllForm и сохраним получившийся модуль под именем DllFormUnit.pas.
Вернемся к главному модулю проекта и поместим в него функцию ShowForm, в задачу которой будет входить создание формы и ее вывод на экран. Используйте для этого приведенный ниже код.
function ShowForm : Integer; stdcall; var Form : TDLLForm; begin Form := TDLLForm.Create(Application); Result := Form.ShowModal; Form.Free; end; |
Обращаю внимание, что для того, чтобы проект был скомпилирован без ошибок, необходимо добавить в секцию uses модуль Forms.
Экспортируем нашу функцию с использованием ключевого слова exports :
exports ShowForm; |
Компилируем проект и получаем файл dllforms.dll. Эти простые шаги - все, что необходимо сделать для создания динамической библиотеки, содержащей форму. Обратите внимание, что функция ShowForm объявлена с использованием ключевого слова stdcall. Оно сигнализирует компилятору использовать при экспорте функции соглашение по стандартному вызову (standard call calling convention). Экспорт функции таким образом создает возможность использования разработанной DLL не только в приложениях, созданных в Delphi.
Соглашение по вызову (Calling conventions) определяет, каким образом передаются аргументы при вызове функции. Существует пять основных соглашений: stdcall, cdecl, pascal, register и safecall. Подробнее об этом можно узнать, посмотрев раздел "Calling Conventions" в файле помощи Delphi.
Также обратите внимание, что значение, возвращаемое функцией ShowForm, соответствует значению ShowModal. Таким образом вы можете передавать некоторую информацию о состоянии формы вызывающему приложению.
Ниже представлено два листинга, первый из которых содержит полный код файла проекта DLL (модуль с формой здесь не приводится), а второй - модуль вызывающего приложения, в котором используется только что разработанная нами библиотека.
library DllForms;
uses
SysUtils, Classes, Forms,
DllFormUnit in 'DllFormUnit.pas' {DllForm};
{$R *.RES}
function ShowForm : Integer; stdcall;
var
Form : TDLLForm;
begin
Form := TDLLForm.Create(Application);
Result := Form.ShowModal;
Form.Free;
end;
begin
end.
|
unit TestAppUnit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
function ShowForm : Integer; stdcall; external 'dllforms.dll';
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowForm;
end;
end.
|
Прошу заметить, что при экспорте функции также было использовано ключевое слово stdcall.
Следует обратить особое внимание на работу с дочерними формами в DLL. Если, к примеру, в вызывающем приложении главная форма имеет значение свойства FormStyle, равным MDIForm, то при попытке вызова из DLL MDIChild-формы, на экране появится сообщение об ошибке, в котором будет говориться, что нет ни одной активной MDI-формы.
В тот момент, когда вы пытаетесь показать ваше дочернее окно, VCL проверяет корректность свойства FormStyle главной формы приложения. Однако в нашем случае все вроде бы верно. Так в чем же дело? Проблема в том, что при проведении такой проверки, рассматривается объект Application, принадлежащий не вызывающему приложению, а собственно динамической библиотеке. Ну, и естественно, поскольку в DLL нет главной формы, проверка выдает ошибку. Для того чтобы избежать такой ситуации, надо назначить объекту Application динамической библиотеки объект Application вызывающего приложения. Естественно, это заработает только в том случае, когда вызывающая программа - VCL-приложение. Кроме того, перед выгрузкой библиотеки из памяти необходимо вернуть значение объекта Application библиотеки в первоначальное состояние. Это позволит менеджеру памяти очистить оперативную память, занимаемую библиотекой. Следовательно, вам нужно сохранить указатель на "родной" для библиотеки объект Application в глобальной переменной, которая может быть использована при восстановлении его значения.
Итак, вернемся немного назад и перечислим шаги, необходимые нам для работы с помещенным в DLL MDIChild-формами.
В динамической библиотеке создаем глобальную переменную типа TApplication. Сохраняем указатель на объект Application DLL в глобальной переменной. Объекту Application динамической библиотеки ставим в соответствие указатель на Application вызывающего приложения. Создаем MDIChild-форму и работаем с ней. Возвращаем в первоначальное состояние значение объекта Application динамической библиотеки и выгружаем DLL из памяти.Первый шаг прост. Просто помещаем следующий код в верхней части модуля DLL:
var DllApp: TApplication; |
Затем создаем процедуру, которая будет изменять значение объекта Application и создавать дочернюю форму. Процедура может выглядеть примерно так:
procedure ShowMDIChild(MainApp: TApplication);
var
Child: TMDIChild;
begin
if not Assigned(DllApp) then
begin
DllApp := Application;
Application := MainApp;
end;
Child := TMDIChild.Create(Application.MainForm);
Child.Show;
end;
|
Все, что нам теперь необходимо сделать, - это предусмотреть возвращение значения объекта Application в исходное состояние. Делаем это с помощью процедуры MyDllProc:
procedure MyDLLProc(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
{ DLL is выгружается. Восстанавливаем значение указателя Application}
if Assigned(DllApp) then
Application := DllApp;
end;
|
Вместо заключения
Использование динамически подключаемых библиотек не так сложно, как это может показаться на первый взгляд. DLL предоставляют широчайшие возможности для оптимизации работы приложений, а также работы самих программистов. Используйте DLL и, возможно, ваша жизнь станет легче!
Автоматизация кодирования импорта функций из DLL.
Оформил: DeeCo
Автор: Кочетов Андрей.
Круг рассматриваемых вопросов В этой статье будут рассмотрены методы автоматизации кодирования импорта функций из динамически подключаемых DLL.Решение этой задачи позволит значительно сократить трудоёмкость (а значит, и время, и количество внесённых ошибок) написания похожего кода в разных проектах и/или для разных DLL.
Материал проиллюстрирован исходными текстами на Borland C++ Builder 6.0; однако все опубликованные идеи справедливы (и применимы с минимальной адаптацией) для любого языка/среды разработки. Введение и библиография В журнале "Программист" (№9 за 2002 г.) была опубликована статья Вячеслава Ермолаева "Использование template-классов при импортировании функций из DLL".Уважаемый автор предлагает для решения этой задачи использовать механизм шаблонов (templates) языка С++. Это огромный шаг вперёд по сравнению с рутинным кодированием, но на мой взгляд, не идеальное решение, которому присущи некоторые недостатки:
Всё разнообразие возможных функций ограничивается богатством заранее описан-ных шаблонов; и хотя в статье автор отодвинул планку очень далеко - 13 параметров функций (а много ли Вы припомните функций, не укладывающихся в эти рамки ?), всё же сам факт несвободы как-то стесняет Если Вы всё же где-то откопаете функцию с большим количеством параметров, Вам придётся расширить набор шаблонов, т.е. изменить библиотеку классов, что является крайне нетехнологичным решением - Вы же не изменяете исходники VCL! При кодировании пользовательского приложения приходится использовать до-вольно-таки громоздкое описание импортируемых функций в обёртках template-классов, что не способствует мнемонической лёгкости чтения и понимания текста Механизм шаблонов языка C++ позволяет уменьшить объём исходного, но никак не объектного, полученного после компиляции кода, который раздувается тем сильнее, чем более разнообразны параметры в импортируемых из DLL функциях. Кроме того, в рамках предложенного метода остаётся нерешённой проблема автоматизации довольно трудоёмкой рутинной операции - определения полных идентифика-торов функций в DLL (строковых параметров для функции GetProcAddress): Особых проблем не наблюдается, если все функции в DLL скомпилированы как "extern "C" - в этом случае линкер просто добавляет символ подчёрки-вания перед именем функции Если же DLL собрана с функциями в стиле C++, всё совсем не так одно-значно: идентификаторы могут получиться до полуметра длиной , и выковыривание их из DLL - лишняя ручная работа; можно, конечно, зная прави-ла работы линкера, синтезировать их - но и это явно предмет для автоматизации, к тому же подозреваю, что у разных средств разработки (BCB, Visual C++) раз-ные правила, по которым работает линкер Решение Как прекрасна была бы жизнь, если б можно было все насущные нужды разработчика удовлетворить только средствами языка разработки! Увы, нет в мире совершенства, как говорил Лис, и поэтому фирмы-разработчики средств разработки генерируют всё новые, всё более мощные среды разработки (IDE), а также развивают сами языки программирования - взять те же Delphi, BCB, C# : сравните языковые средства с Pascal и C++. Вспомните также, сколько дополнительных (встроенных в IDE и отдельных) инструментальных средств входит в поставку BCB и Delphi.Borland Software Corporation, отдав дань уважения OWL, задвинула её подальше и стала развивать Delphi и BCB на платформе VCL. Совершенно не вижу, почему бы благородным донам не поступить так же J.
Суть моего решения состоит в интеграции средства языка - компоненты - и дополнительного инструментального средства собственной разработки - DllWizard. Интерфейс-оболочку к DLL обеспечивает компонента TAskDll (исходный код - в архиве AskDll.zip). В её методах инкапсулированы: динамическая загрузка DLL (функция LoadLibrary) в методе LoadDll обработка исключительных ситуаций: при возникновении любых проблем с за-грузкой DLL формируется сообщение на языке локализации Windows и генерируется ис-ключение (Exception) для обработки в вызывающем приложении выгрузка DLL и освобождение памяти (функция FreeLibrary), выполняемые авто-матически при прекращении существования компоненты (например, при закрытии формы, на которой расположена компонента) Загрузка DLL и инициализация импортируемых функций осуществляется вызовом одного лишь метода компоненты - LoadDll. Параметр метода - указатель на функцию:bool (*FuncGetProc)(HMODULE PtrDll)Это должна быть функция вида:
bool Example_Load(HMODULE PtrDll)
{
if((Func1=(DLL_Func1)GetProcAddress(PtrDll, "@Func1$qiii")) == NULL) return false;
if((Func2=(DLL_Func2)GetProcAddress(PtrDll, "@Func2$qpct1")) == NULL) return false;
return true;
}
Всё, что нам нужно - это написать подобный код. Именно эта часть работы наиболее тру-доёмка, и когда мы сможем выполнить её легко, быстро и безошибочно, это и будет красивым венцом нашей технологии.
Задача решается с помощью DllWizard в 3 прохода: Автоматическое формирование описаний функций с их параметрами и возвращае-мыми значениями. Автоматическое формирование идентификаторов функций (строковых параметров для функции GetProcAddress) Генерация исходных текстов Рассмотрим пример работы с Example.DLL, экспортирующей 2 функции:int __cdecl Func1(int i1, int i2, int i3); char * __cdecl Func2(char * s1, char * s2
Итак, начинаем:
Запускаем DllWizard и создаём список всех функций, которые мы хотим импорти-ровать из DLL. Если DLL собственной разработки, достаточно просто указать путь к её исходнику и нажать кнопку "Найти": список сформируется автоматически (см.рис. 1) Указываем путь к DLL и нажимаем кнопку "Найти" (см.рис. 2) Нажимаем кнопку "Сгенерировать" - в каталогах, указанных на закладке "На-стройки" будут сформированы файлы
рис. 1.
рис. 2.
Имя DLL-ки является префиксом у всех сгенерированных файлов и у функции в модуле CPP. Исходный текст DLL и тестового приложения находится в архиве DllTest.zip.
Подкаталог DLL содержит исходный текст библиотеки: UnitExample.cpp Подкаталог EXE содержит исходный текст тестового приложения: UnitDllTest.cpp и в подкаталоге DllWizard - сгенерированные файлы: Заголовочные файлы: Example_Descript.h является служебным и содержит описание функций Example_Declare.h является служебным и содержит объявления указателей на функции Example_Extern.h следует включить в тот исходный модуль проекта прило-жения, из которого вызываются функции, импортируемые из DLL. Example_Load.cpp содержит функцию загрузки Example_Load Резюме Подводим итоги. Ниже описан порядок разработки пользовательского приложения, им-портирующего функции из динамически подключаемой библиотеки функций: С помощью DllWizard генерируем включаемые модули В проект включаем сгенерированный модуль Example_Load.cpp "Бросаем" на главную форму компоненту TAskDll и в её свойстве DllName указы-ваем имя DLL-ки (см.рис. 3)
рис. 3.
рис. 4.
P.S. Немножко саморекламы: компонента TAskDll является составной частью пакета ASK Tools, см. соответствующую ссылку на http://goldenask.narod.ru/
Использование DLL в качестве Plug-in.
| Создал Б-г Еву. Показывает ее Адаму. Адам спрашивает: - А как ей пользоваться? - Да ты что, это же обычный Plug-n-Play! Вставил - и играй! Вставил Адам, стал играть, понравилось. Играл так несколько дней, а потом у Евы месячные начались. Адам прибегает к Б-гу: - Госп-ди, там с Евой что-то непонятное случилось! - Адам, ну где ты видел Plug-n-Play без глюков?! |
Пусть ядро, т.е. exe-файл, ищет плагины, запускает их и по таймеру передаёт в них два цифровых значения, которые один плагин будет изображать в текстовом виде, а второй в виде диаграмм. Реализация плагинов отличается минимально, поэтому расскажу об одном — Digital.dll. Начнём перечисление функций:
// получение информации о плагине
function GetModuleInfo:TModuleInfo;stdcall;
var
Buffer:array [byte] of char;
begin
with Result do begin
Name:='Отображение цифровых данных';
Kind:=mkDigital;
if GetModuleFileName(hInstance,@Buffer,SizeOf(Buffer)-1)>0 then
Path:=ExtractFilePath(StrPas(Buffer));
end;
end;
// Функция возвращает информацию о модуле. В данном
// случае это цифровое отображение, путь и тип модуля.
// инициализация
procedure Initialize;stdcall;
begin
// запоминание старого Application
OldApp:=Application;
fmDigitalMain:=nil;
end;
// Процедура запоминает переменную Application
// и делает нулевой ссылку на форму плагина.
// запуск
procedure Run;stdcall;
begin
// создание окна плагина
if fmDigitalMain=nil then
fmDigitalMain:=TfmDigitalMain.Create(Application);
end;
// Процедура запуска плагина созда¸т окно.
// Окно созда¸тся видимым.
// останов
procedure Terminate;stdcall;
begin
// освобождение окна
fmDigitalMain.Free;
fmDigitalMain:=nil;
// восстановление старого TApplication
Application:=OldApp;
end;
// Процедура уничтожает окно и возвращает старый TApplication.
// при¸м данных
procedure SetData(Kind:TDataKind;const Buffer;Size:Integer);stdcall;
begin
case Kind of
// передача TApplication
dkApplication:if Size=SizeOf(TApplication) then
Application:=TApplication(Buffer);
// передача данных во время работы
dkInputData:if fmDigitalMain<>nil then begin
fmDigitalMain.SetData(Buffer,Size);
end;
end;
end;
// Процедура получения данных. В зависимости от полученного
// типа данных с данные в переменной Buffer соответственно
// типизируются. Здесь происходит обращение к форме плагина,
// расписывать я его не буду, там вс¸ просто, см. исходники.
// Типы, которые используются здесь, описаны в SharedTypes.pas
|
Прежде всего следует подумать об инкапсуляции функций подключённого плагина в класс. Этот класс реализован в Modules.pas. При создании экземпляра класса происходит поиск и запоминание всех адресов функций плагина. Последующие вызовы функций происходят в о дноимённых методах класса в том случае, если они не равны. Я приведу только описание типа класса:
type
// описания типов функций модуля
TGetModuleInfo=function:TModuleInfo;stdcall;
TInitialize=procedure;stdcall;
TRun=procedure;stdcall;
TTerminate=procedure;stdcall;
TSetData=procedure(Kind:TDataKind;const Buffer;Size:Integer);stdcall;
// непосресдвенно сам класс
TModule=class
private
FFileName:String; //имя файла
FHandle:THandle; // дескриптор библиотеки
FModuleInfo:TModuleInfo; // информация о модуле
// адреса функций плагина
FGetModuleInfo:TGetModuleInfo; // функция получения информации о модуле
FInitialize:TInitialize; // процедура инициализации
FRun:TRun; // процедура запуска
FTerminate:TTerminate; // процедура останова
FSetData:TSetData; // процедура передачи данных
public
constructor Create(AFileName:String;var IsValidModule:Boolean);
destructor Destroy;override;
// вызов функций плагина
function GetModuleInfo:TModuleInfo;
procedure Initialize;
procedure Run;
procedure Terminate;
procedure SetData(Kind:TDataKind;const Buffer;Size:Integer);
// свойства плагина
property FileName:String read FFileName;
property Handle:THandle read FHandle;
property ModuleInfo:TModuleInfo read FModuleInfo;
end;
|
Теперь осталось только собрать плагины и запустить. Сбор информации и запуск происходит по нажатию одноимённой кнопки на главной форме. Как собирать плагины — дело вкуса. В этом примере я сканирую заданный каталог, можно хранить в INI-файле, реестре, можн о придумать свой формат хранения. Сбор плагинов:
// нажатие кнопки запуска
procedure TfmMain.btStartClick(Sender: TObject);
// добавление плагинов в список
procedure AppendModulesList(FileName:String);
var
Module:TModule;
IsValid:Boolean;
begin
// создание экземпляра плагина
Module:=TModule.Create(FileName,IsValid);
// если создан некорректно
if not IsValid then
// удаление
Module.Free
else begin
// добавление
SetLength(ModulesList,Length(ModulesList)+1);
ModulesList[Length(ModulesList)-1]:=Module;
end;
end;
var
sr:TSearchRec;
i:Integer;
begin
// построение списка модулей
SetLength(ModulesList,0);
// поиск файлов *.dll
if FindFirst(edPath.Text+'*.dll',faAnyFile and not faDirectory,sr)=0 then begin
AppendModulesList(edPath.Text+sr.Name);
while FindNext(sr)=0 do
AppendModulesList(edPath.Text+sr.Name);
end;
// запуск найденных модулей
if Length(ModulesList)>0 then begin
for i:=0 to Length(ModulesList)-1 do begin
// инициализация
ModulesList[i].Initialize;
// передача Application
ModulesList[i].SetData(dkApplication,Application,SizeOf(Application));
// запуск плагина
ModulesList[i].Run;
end;
// старт таймера
Events.Enabled:=True;
end;
end;
|
Мне кажется, что я достаточно подробно описал в комментариях производимые действия :) Ну и последнее — засылка данных по таймеру:
procedure TfmMain.EventsTimer(Sender: TObject);
var
Values:array [0..1] of Word;
i:Integer;
begin
// формирование случайных значений
Values[0]:=Random($ffff);
Values[1]:=Random($ffff);
// передача данных
if Length(ModulesList)>0 then
for i:=0 to Length(ModulesList)-1 do begin
ModulesList[i].SetData(dkInputData,Values,SizeOf(Values));
end;
end;
|
Желательно не забывать об освобождении модулей. Это уже в самом конце (см. исходные тексты).
Можно ли определить полный путь и имя файла запущенной DLL из самой DLL.
Автор: Олег Кулабухов.
uses Windows; procedure ShowDllPath stdcall; var TheFileName: array[0..MAX_PATH] of char; begin FillChar(TheFileName, sizeof(TheFileName), #0); GetModuleFileName(hInstance, TheFileName, sizeof(TheFileName)); MessageBox(0, TheFileName, 'The DLL file name is:', mb_ok); end; |
Модуль-заготовка для динамического подключения библиотек.
Автор: Panov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Модуль-заготовка для динамического подключения библиотек.
Модуль подключается к проекту.
Для загрузки и выгрузки библиотек используется по одной функции.
Зависимости: windows.pas, sysutils.pas
Автор: panov, panov@hotbox.ru
Copyright: нет
Дата: 30 марта 2004 г.
***************************************************** }
unit proc;
interface
uses windows, sysutils, classes;
const
_MyLibraryName = '..\dll\proc.dll'; //имя библиотеки
var
hLib: THandle;
procedure LoadLib;
procedure UnloadLib;
implementation
procedure LoadLib;
begin
if hLib <> 0 then
Exit;
hLib := LoadLibrary(PChar(_MyLibraryName));
if (hLib = 0) then
begin
raise Exception.Create('Не могу загрузить библиотеку ' + _MyLibraryName);
Exit;
end;
end;
procedure UnloadLib;
begin
FreeLibrary(hLib);
hLib := 0;
end;
initialization
hLib := 0;
end.
Пример использования:
unit proc;
interface
uses windows, sysutils;
const
_MyLibraryName = '..\dll\proc.dll'; //имя библиотеки
var
hLib: THandle;
ExecCmd: procedure(const aCommand: string; const aShow, aWaitExit: Boolean);
procedure LoadLib;
procedure UnloadLib;
implementation
procedure LoadLib;
begin
if hLib <> 0 then
Exit;
hLib := LoadLibrary(PChar(_MyLibraryName));
if (hLib = 0) then
begin
raise Exception.Create('Не могу загрузить библиотеку ' + _MyLibraryName);
Exit;
end;
@ExecCmd := GetProcAddress(hLib, 'ExecCmd');
end;
procedure UnloadLib;
begin
FreeLibrary(hLib);
hLib := 0;
end;
initialization
hLib := 0;
end.
{
В любом модуле пишем:
uses proc;
...
LoadLib; //Загрузка библиотеки.
ExecCmd('c:\command.com',True,True);
UnloadLib; //Выгрузка библиотеки.
}
DLL - убийственная утилита.
| Программа МсLаиd.ехе вызвала неустранимую ошибку в модуле кикиshка.dll при обращении к системному таймеру: Stаск Оvегf1оw. Кикиshка.dll будет закрыта. При повторном появлении ошибки обратитесь к разработчику МсLаиd.ехе. |
Вот маленький метод, взятый мною из небольшого проекта, созданного мною для удаления DLL из памяти. На форме присутствует одно поле редактирования TEdit с именем EditDLLName, кнопки Ok и Close. Следующий код выполняется при нажатии на кнопке Ok:
procedure TForm1.OkBtnClick(Sender: TObject);
var
hDLL: THandle;
aName: array[0..10] of char;
FoundDLL: Boolean;
begin
if EditDLLName.Text = '' then
begin
MessageDlg('Сначала вы должны ввести имя выгружаемой DLL!', mtInformation,
[mbOk], 0);
exit;
end;
StrPCopy(aName, EditDLLName.Text);
FoundDLL := false;
repeat
hDLL := GetModuleHandle(aName);
if hDLL = 0 then
break;
FoundDLL := true;
FreeLibrary(hDLL);
until false;
if FoundDLL then
MessageDlg('Успешно!', mtInformation, [mbOk], 0)
else
MessageDlg('DLL не найдена!', mtInformation, [mbOk], 0);
EditDLLName.Text := '';
end;
|
Получить специфическую для DLL информацию о версии.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{***************************
** DllGetVersion Function **
***************************}
{
Implemented by many of the Microsoft® Windows® Shell dynamic-link libraries (DLLs) to
allow applications to obtain DLL-specific version information.
}
{
Using DllGetVersion to Determine the Version Number
Starting with version 4.71, the Shell and common controls DLLs, among others,
began exporting DllGetVersion.
This function can be called by an application to determine which DLL
version is present on the system. It returns a structure that contains version information.
Note DLLs do not necessarily export DllGetVersion. Always test for it
before attempting to use it.
For systems earlier than Windows 2000, DllGetVersion returns a DLLVERSIONINFO structure
that contains the major and minor version numbers, the build number,
and a platform identifier (ID).
For Windows 2000 and later systems, DllGetVersion might instead return a
DLLVERSIONINFO2 structure. This structure contains the QFE number that identifies
the service pack and provides a more robust way to compare version numbers than DLLVERSIONINFO.
Because the first member of DLLVERSIONINFO2 is a DLLVERSIONINFO structure,
the new structure is backward-compatible.
}
// DLLVERSIONINFO structure
type
PDLLVerInfo=^TDLLVersionInfo;
TDLLVersionInfo=Record
cbSize, // Size of the structure, in bytes.
dwMajorVersion, // Major version of the DLL
dwMinorVersion, // Minor version of the DLL
dwBuildNumber, // Build number of the DLL
dwPlatformID: DWord; // Identifies the platform for which the DLL was built
end;
var
DllGetVersion: function(dvi: PDLLVerInfo): PDLLVerInfo; stdcall;
function GetDllVersion(DllName: string; var DLLVersionInfo: TDLLVersionInfo): Boolean;
var
hInstDll: THandle;
p: pDLLVerInfo;
begin
Result := False;
// Get a handle to the DLL module.
// das Handle zum DLL Modul ermitteln.
hInstDll := LoadLibrary(PChar(DllName));
if (hInstDll = 0) then Exit;
// Return the address of the specified exported (DLL) function.
// Adresse der Dll-Funktion ermitteln
@DllGetVersion := GetProcAddress(hInstDll, 'DllGetVersion');
// If the handle is not valid, clean up an exit.
// Wenn das Handle иngь1tig ist, wird die Funktion verlassen
if (@DllGetVersion) = nil then
begin
FreeLibrary(hInstDll);
Exit;
end;
new(p);
try
ZeroMemory(p, SizeOf(p^));
p^.cbSize := SizeOf(p^);
// Call the DllGetVersion function
// Die DllGetVersion() Funktion aufrufen
DllGetVersion(p);
DLLVersionInfo.dwMajorVersion := p^.dwMajorVersion;
DLLVersionInfo.dwMinorVersion := p^.dwMinorVersion;
@DllGetVersion := nil;
Result := True;
finally
dispose(P);
end;
// Free the DLL module.
// Dll wieder freigeben.
FreeLibrary(hInstDll);
end;
// Example to get version info from comctl32.dll
// Beispiel, um von comctl32 Versions/Informationen zu erhalten
procedure TForm1.Button1Click(Sender: TObject);
var
DLLVersionInfo: TDLLVersionInfo;
begin
if not GetDllVersion('comctl32.dll',DLLVersionInfo) then
begin
DLLVersionInfo.dwMajorVersion := 4;
DLLVersionInfo.dwMinorVersion := 0;
end;
with DLLVersionInfo do
ShowMessage(Format('ComCtl Version: %d.%d / Build: %d',[dwMajorVersion, dwMinorVersion, dwBuildNumber]))
end;
Как создать DLL только с ресурсами.
| - Что общего между ЭВМ и половым членом? - Оба имеют два устойчивых состояния - либо стоит, либо висит. |
Создайте и откомпилируйте пустой проект DLL, который содержит ссылку на файл ресурсов .res, который содержит Ваши ресурсы.
library ResTest;
uses
SysUtils;
{$R MYRES.RES}
begin
end.
|
Для использования такой DLL, просто загрузите dll и ресурсы, которые Вы будете использовать:
{$IFDEF WIN32}
const
BadDllLoad = 0;
{$ELSE}
const
BadDllLoad = 32;
{$ENDIF}
procedure TForm1.Button1Click(Sender: TObject);
var
h: THandle;
Icon: THandle;
begin
h := LoadLibrary('RESTEST.DLL');
if h then
ShowMessage('Bad Dll Load')
else
begin
Icon := LoadIcon(h, 'ICON_1');
DrawIcon(Form1.Canvas.Handle, 10, 10, Icon);
FreeLibrary(h);
end;
end;
|
Как DLL правильно заполнить строковыми ресурсами, и потом достать их.
| - Если встать между двумя программистами и загадать желание, то оно обязательно сбудется! - Но желание будет глючить! |
--my.rc--
STRINGTABLE
{
00001, "My String #1"
00002, "My String #2"
}
Далее компилируешь его: brcc32 my.rcУ тебя получится my.res. Делаешь DLL:
--my.dpr--
library my;
{$R my.res}
begin
end.
|
dcc32 my.dprПолучаешь, наконец-то свою my.dll Теперь о том, как использовать.
В своей программе:
var
h: THandle;
S: array [0..255] of Char;
begin
h := LoadLibrary('MY.DLL');
if h <= 0 then
ShowMessage('Bad Dll Load')
else
begin
SetLength(S, 512);
LoadString(h, 1, @S, 255);
FreeLibrary(h);
end;
end;
|
Отладка DLL в Windows XP.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
Until up to Win2k, you had to set the HostApplication on Start=>Parameter to debug a DLL. This will not work on Windows XP. That Delphi will know the DLL if you run the progam, you have to do the following: The program must load the DLL. After this, press Ctrl-Alt-M in Delphi, to list all modules. Sometimes there will be the DLL with path. Solution: Right click on the DLL, select "Reload symbols" and set the full path to the DLL. Now the breakpoints should be active. When the DLL will be compiled in the system path (directory in PATH) this problem dont occur. {****} Bis und mit W2k hat es gereicht, unter Start=>Parameter=>HostAnwendung eine EXE an zu geben, welche die DLL 1дdt. Dies reicht leider bei Windows XP nicht mehr Damit Delphi die DLL beim Аиsfьhгеn des Programmes erkennt, muss folgendes gemacht werden: Das Programm muss die DLL laden. Danach Ctrl-Alt-M innerhalb von Delphi dгьскеn, um die Module aufzulisten. Unter иmstдndеn wird hier die DLL ohne Pfadangabe angezeigt. Wenn dies der Fall ist, handelt es sich um das hier beschriebene Problem. Lцsиng: Auf die DLL klicken, ььег die rechte Maustaste "Symbole neu laden" anklicken und dann den kompletten Pfad zur DLL angeben. Jetzt sind die Breakpoints aktiv. Falls die DLL in einen Systempfad compiliert wird (ein Verzeichnis im PATH), besteht dieses Problem nicht.И снова баннеры - Download Accelerator 5.3.0.0.
Оформил: DeeCo Автор: http://www.cracklab.narod.ru
Page: http://www.downloadaccelerator.com/ [Вступление:] Мне захотелось чего-нибудь поломать, я зашёл на download.com.com и нашел там это чудо. Программа очень странная, потому как вся документация, все хелпы и readme находятся в ... Интернете! Так что я не смог прочитать ни сколько стоит прога, ни как её зарегить. Наверное, американосы уже привыкли, что у каждого должна быть выделенка, а диал-ап с поминутной оплатой - это для них то же самое, что для нас чунгачкук с луком :) [Беглый осмотр:] Я запустил прогу и первое что я увидел - это большой баннер с рекламой. Ну вот, опять баннеры, а с другой стороны вспомню как эту хрень убирать :) [Начинаем:] В борьбе с баннерами есть 2 ключевых момента: 1) Отключить механизм загрузки самих баннеров из Инета, т.к. это занимает и без того небольшую скорость моего модема. 2) Найти процедуру создания окошка с баннером и сделать так, чтобы размер баннера в высоту стал равен 0, и подтянуть все остальные окошки вверх до упора под главное меню. [Приступим к пункту 1:] Для начала я дизассемблировал программу в W32Dаsm'е и защел в String Reference. Там было дохрена всяких строк, но я искал пока неизвестно что и...нашел строку:* Possible StringData Ref from Data Obj ->"http://ads.downloadaccelerator.com/cgi-bin/ads"ОООчень похоже на адрес, откуда прога тянет картинки баннеров, это видно по "ads", что очень часто расшифровывается как ADultSystem :) вот и отлично, можно прямо щас затереть в программе этот адрес и больше прога никуда не сунется. Этот вариант лучше, чем искать таймер, затирать вызовы функции, потому как такие затирания могут нарушить работу проги, а нам это не надо, так что обойдёмся малой кровью. Затираем. [Приступим к пункту 2:] Это уже посложнее, потому как единого подхода к этой проблеме нет и каждый раз чего-то новенькое приходится выдумывать. Для начала я взял утилиту Spy и выяснил, что баннерное окно - это (как обычно в таких ситуациях) два окна: одно родительское (617x80) другое - сам баннер (468x60) размеры баннерного окна говорят уже о том, что он - баннер. Теперь мне нужно найти функцию создания этого окна, а в функцию параметры чаще всего передаются через стек, т.е. командой push. Значит мне нужно найти команду: push 00000050 т.к. высота окна 80 = 50h Я открываю DAP.exe в HView, перехожу в режим ассемблера и нажимаю два раза F7 это поиск по ассемблерной команде, ввожу туда push 50 и начинаю искать. Такая команда встречается 7 раз. Теперь я методом тыка по очереди в каждом из семи раз меняю push 50 на push 00, т.е. чтобы размер окна был равен 0, и проверяю. На четвертой попытке у меня получилось: .00461F2D: 6A02 push 002 .00461F2F: 6A50 push 050 .00461F31: 50 push eax .00461F32: 6A00 push 000 .00461F34: 6A00 push 000 .00461F36: 6A00 push 000 .00461F38: 8BCE mov ecx,esi .00461F3A: E8737F0400 call MFC42.6197 Очень похоже на вызов функции создания окна. Меняем по адресу 00461F2F: push 50 на push 00 и запускаем программу: Окна с баннером больше нет, а все остальные окна подтянуты под главное меню. УрА! Исследовал и наваял туториал: vallkor //PTDS E-mail: vallkor@chat.ru (vallkor@etel.dn.ua) Page: http://vallkor.chat.ru
Реализация Drag-and-Drop из внешнего приложения в ваше.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
The following example demonstrates the Drag&Drop machanism from an external
application (Wordpad, Microsoft,..) to a TMemo in your own application.
{
Das folgende Beispiel demonstriert der Drag&Drop Maechanismus
aus externen Anwendungen (wie zum Beispiel WordPad oder Microsoft Word)
in ein TMemo des eigenen Delphi-Programms.
}
unit TMemoDragDropFrm;
{ ****************************************************************
Source File Name : TMemoDragDropFrm.pas
Typ : Hauptformular
Autor : Andreas Kosch
Compiler : Delphi 4.02 CSS
Betriebssystem : Windows 98
Beschreibung : Text via OLE Drag&Drop ььегnеhmеn aus einer
anderen Anwendung (wie zum Beispiel WordPad)
ььегnеhmеn.
16.01.2003: Test mit Delphi 7 und Microsoft Word XP unter Windowx XP
**************************************************************** }
{ Comments by Thomas Stutz }
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, ActiveX, ShlObj, ComObj, StdCtrls;
type
// TForm1's declaration indicates that it's a TForm and that
// it supports the IDropTarget interface:
{
IDropTarget
Any application wanting to accept drop operations must
implement the IDropTarget interface.
Methods of the IDropTarget interface:
DragEnter
Dragged item has just been moved into the application's window,
return the relevant icon.
DragOver
Dragged item is being moved over the application's window,
return the relevant icon.
DragLeave
Dragged item has just moved out of the application's window.
Drop
The dragged item has been dropped on this application.
}
TForm1 = class(TForm, IDropTarget)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
// IDropTarget
function DragEnter(const dataObj: IDataObject;
grfKeyState: Longint;
pt: TPoint;
var dwEffect: Longint): HResult; stdcall;
function DragOver(grfKeyState: Longint;
pt: TPoint;
var dwEffect: Longint): HResult; stdcall;
function DragLeave: HResult; stdcall;
function Drop(const dataObj: IDataObject;
grfKeyState: Longint; pt: TPoint;
var dwEffect: Longint): HResult; stdcall;
// IUnknown
// Ignore referance counting
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
ShellAPI;
// In the OnCreate event handler, two important methods are called.
// First, OleInitalize is called. This initializes the OLE libraries and should always be
// called before your application uses any OLE functions.
// RegisterDragDrop registers the window as a valid drop target.
// If this isn't called, the window will never receive any drop events.
procedure TForm1.FormCreate(Sender: TObject);
begin
OleInitialize(nil);
{Allow window to accept drop events}
OleCheck(RegisterDragDrop(Handle, Self));
{ Execute Wordpad for testing }
ShellExecute(Handle, 'open', 'wordpad', 'c:\Test.doc', nil, SW_SHOW);
end;
// OnDestroy does the exact opposite. It calls RevokeDropTarget to indicate that
// drop events are no longer accepted.
// It then calls OleUninitialize, since the application is finished using all OLE functions.
procedure TForm1.FormDestroy(Sender: TObject);
begin
{Finished accepting drops}
RevokeDragDrop(Handle);
OleUninitialize;
end;
{-----------------------------------------------------------------}
{ IDropTarget-Implementierung }
{-----------------------------------------------------------------}
function TForm1.DragEnter(const dataObj: IDataObject;
grfKeyState: Longint;
pt: TPoint;
var dwEffect: Longint): HResult;
begin
dwEffect := DROPEFFECT_COPY;
Result := S_OK;
end;
function TForm1.DragOver(grfKeyState: Longint;
pt: TPoint;
var dwEffect: Longint): HResult;
begin
dwEffect := DROPEFFECT_COPY;
Result := S_OK;
end;
function TForm1.DragLeave: HResult;
begin
Result := S_OK;
end;
function TForm1._AddRef: Integer;
begin
Result := 1;
end;
function TForm1._Release: Integer;
begin
Result := 1;
end;
function TForm1.Drop(const dataObj: IDataObject;
grfKeyState: Longint;
pt: TPoint;
var dwEffect: Longint): HResult;
var
aFmtEtc: TFORMATETC;
aStgMed: TSTGMEDIUM;
pData: PChar;
begin
{Make certain the data rendering is available}
if (dataObj = nil) then
raise Exception.Create('IDataObject-Pointer is not valid!');
with aFmtEtc do
begin
cfFormat := CF_TEXT;
ptd := nil;
dwAspect := DVASPECT_CONTENT;
lindex := -1;
tymed := TYMED_HGLOBAL;
end;
{Get the data}
OleCheck(dataObj.GetData(aFmtEtc, aStgMed));
try
{Lock the global memory handle to get a pointer to the data}
pData := GlobalLock(aStgMed.hGlobal);
{ Replace Text }
Memo1.Text := pData;
finally
{Finished with the pointer}
GlobalUnlock(aStgMed.hGlobal);
{Free the memory}
ReleaseStgMedium(aStgMed);
end;
Result := S_OK;
end;
end.
Описание протокола DNS (Domain Name System).
| Ругательство программистов: Web твою мать!!! |
Служба Доменных Имен предназначена для того, чтобы машины, работающие в Internet, могли по доменному имени узнать IP-адрес нужной им машины, а также некоторую другую информацию; а по IP-номеру могли узнать доменное имя машины.
Служба Доменных Имен была разработана для именования машин в глобальной сети. Основной особенностью глобальной сети является распределенное администрирование, когда один администратор физически не может уследить за выделением имен. Поэтому Служба Доменных Имен функционирует на принципе делегирования полномочий. Каждая машина либо знает ответ на вопрос, либо знает кого спросить. При правильном функционировании система замкнута, т.е. если запрошенная информация имеется у кого-либо, то она будет найдена и сообщена клиенту, либо, если вопрос не имеет ответа, клиент получит сообщение о невозможности получения ответа на вопрос.
Каждый клиент знает своего сервера; обычно указывается не один, а несколько серверов - если первый не отвечает, клиент обращается ко второму и так далее до исчерпания списка. В принципе неважно, к какому серверу обращаться - они дают (должны давать при правильном функционировании) одинаковые ответы на любой запрос. Поэтому для ускорения работы обычно указывают ближайший. Следует помнить, что на одной машине могут функционировать одновременно Name-сервер и программы-клиенты; поэтому если на машине запущен Name-сервер, то в качестве Name-сервера на ней должен быть прописан "я сам".
Имеется некий домен верхнего уровня, обозначаемый точкой: ".". Имеется девять серверов (по крайней мере на моем Name-сервере записано столько), которые отвечают за эту зону. Они не знают ни одного доменного имени - они только авторизуют серверы верхних зон. Серверы верхних зон тоже гнушаются хранить информацию о конкретных машинах и передают это право нижележащим серверам. Тут уже появляются первые упоминания о конкретных машинах, равно как и происходит авторизация нижележащих серверов.
Мне неизвестна ни одна машина с доменным именем из одного сегмента; очень редко используются доменные имена из двух сегментов; имена из трех и четырех сегментов составляют подавляющую долю всех имен Internet; имена из пяти сегментов встречаются довольно редко, а из шести и более мне неизвестны.
Допустим, клиент запросил адрес "www.организация.город.страна". Поиск информации по доменному имени происходит следующим образом:
Клиент спрашивает своего сервера. Если тот является сервером данной зоны, то ответит, на чем все заканчивается. Сервер спрашивает корневой сервер. Тот не может ответить, потому что не знает; зато знает, какой сервер отвечают за зону "страна". Сервер зоны "страна" тоже не может ответить, но знает, что нужно спросить сервер зоны "город.страна". Тот в свою очередь отсылает запрос серверу зоны "организация.город.страна", который сообщит нужную информацию.Это приближенная модель, которая тем не менее позволяет представить работу системы DNS.
Однако эту стройную картину искажают системы кэширования и вторичных серверов. Дело в том, что получив ответ на свой вопрос, DNS-сервер получает также некоторое число, которое говорит ему о том, по истечении какого времени эта информация должна считаться устаревшей. Таким образом, все серверы, участвовавшие в поиске ответа на вопрос, заданный клиентом, могут (и скорее всего будут) помнить как ответ на заданный вопрос, так и путь, по которому шел поиск. При следующих запросах, имеющих общую правую часть с недавно сделанными запросами, поиск будет упрощен (ускорен).
Кроме того, большинство зон имеет вторичные серверы, которые содержат копии данных с первичных серверов. Сервер вышележащей зоны может направить запрос как первичному серверу, так и любому из вторичных, основываясь на своих соображениях о том, какой из них ближе.
Хочу обратить особое внимание на сходство, различие и взаимодействие систем DNS и IP-маршрутизации. Как и IP-маршрутизация, DNS работает по принципу делегирования полномочий, но выделение доменных имен совершенно не зависит от выделения IP-адресов. Для примера рассмотрим домен freebsd.org. Это - домен организации, занимающейся распространением операционной системы FreeBSD Unix. FTP-сервер, содержащий дистрибутив операционной системы и множества утилит для нее, имеет копии в нескольких десятках стран. Имена серверов выглядят так:
ftp.freebsd.org первичный сервер в США ftp.страна.freebsd.org основной сервер в стране ftрчисло.страна.freebsd.org дополнительный сервер в странеТак например на 11 февраля 1998 года.
ftp.ru.freebsd.org соответствует ftp.ru ftp2.ru.freebsd.org соответствует ftp.gamma.ru ftp3.ru.freebsd.org соответствует ftp.chg.ruТаким образом, машины, находящиеся в России оказались произвольно (по воле DNS-мастера из университета Bercley) включенными в домен freebsd.org; однако, они также состоят в своих зонах. Система DNS позволяет любому DNS-мастеру включить любой сервер в свою зону, хотя это включение никого ни к чему не обязывает.
Однако, некоторым сервисам этого недостаточно - так E-mail требует, чтобы машина, принимающая письмо, признала своим адрес, указанный в качестве пункта назначения. Протокол HTTP 1.1 (в 1.0 этого не было) требует, чтобы в HTTP-запросе указывался не путь к файлу, отсчитанный от корня сервера (хотя такие запросы тоже признаются), но и имя сервера; при этом сам сервер знает, какие имена - его, а остальные обрезает и обслуживает в соответствии с HTTP 1.0.
Делегирование зоны ...in-addr.arpa дается только от провайдера вместе с IP-адресами. Собственно, это связано с предназначением ReverceDNS - сообщать доменное имя по IP-адресу. Наверняка мастер зоны freebsd.org держит Reverce-зону для IP-номеров, выделенных университету Bercley; но все эти серверы (кроме сервера, расположенного в университете) не входят в эту Reverce-зону, а значит, ему неподконтрольны.
Одна из проблем состит в том, что Reverce-зону можно выделить только на сеть класса A, B или C (на 16777216, 65536 или 256 адресов) и никак иначе. Можно получить правА на несколько зон одного или разных классов, но что делать тем, кому выделили меньше 256 адресов? А ведь в условиях исчерпания адресного пространства не редкость выделения пула уже на 16 адресов!
DNS-услуги Internet-провайдера
Как правило, провайдер предоставляет клиенту целый комплекс услуг. В число оказываемых DNS-услуг входят:
делегирование зоны ...in-addr.arpa клиентам, имеющим пул адресов, кратный 256. регистрация доменного имени клиента у держателя той зоны, в которой клиент хочет зарегистрироваться; поддержание вторичного сервера прямой и обратной DNS-зон клиента; поддержание первичного сервера этих зон, если клиент по какой-либо причине не поддерживает их сам (особенно это относится к случаю виртуальных зон и к случаю выделения малого пула адресов);Если провайдер будет отказываться - сошлитесь на меня. :-).
Политика и стратегия назначения имен.
Имена зон условно можно разделить на "организационные" и "географические". В высшей зоне зарегестрированы следующие "организационные" зоны:
com commercial (коммерческие) edu educational (образовательные) gov goverment (правительственные) mil military (военные) net network (организации, обеспечивающие работу сети) org organization (некоммерческие организации)В данный момент, чтобы разгрузить домен com, собираются создать несколько новых доменов, но у меня нет достоверной информации по ним. В организационных зонах обычно размещаются непосредственно домены организаций.
Каждая страна (государство) имеет свой географический домен из двух букв:
ae United Arab Emirates (Объединенные Арабские Эмираты) au Australia (Австралия) be Belgium (Бельгия) br Brazil (Бразилия) by Belarus (Белоруссия) ca Canada (Канада) ch Switzerland (Швейцария) cz Czech Republic (Чехия) de Germany (Германия) dk Denmark do Dominican Republic (Доминиканская республика) ee Estonia (Эстония) eo ??? es Spain (Испания) fi Finland (Финляндия) fr France (Франция) hu Hungary (Венгрия) il Israel (Израиль) in India (Индия) iz ??? jp Japan (Япония) kg Kyrgyzstan (Кыргызстан) kr South Korea (Южная Корея) kz Kazakhstan (Казахстан) lt Lithuania (Литва) lv Latvia (Латвия) mx Mexico (Мексика) nl Netherlands (Нидерланды) no Norway (Норвегия) nz New Zealand (Новая Зеландия) pl Poland (Польша) ro Romania (Румыния) ru Russia (Россия) si Slovenia (Словения) sk Slovak Republic (Словакия) su Soviet Union (Советский Союз - поддерживается, но не распределяется) ua Ukraine (Украина) uk United Kingdom (Соединенное Королевство ВеликоБритания / Англия) yu Yugoslavia (Югославия) za South Africa (Южная Африка)Я перечислил отнюдь не все страны - кто хочет, может прислать мне другие названия.
В зонах государств опять же имеются "организационные" и "географические" зоны. "Организационные" в большинстве своем повторяют структуру "организационных" зон верхнего уровня, разве что вместо "com" используется "co". "Географические" выделяются городам, областям и т.п. территориальным образованиям. Непосредственно в тех и других размещаются домены организаций или домены персональных пользователей.
После выбора зоны, в которую будет включен наш домен надо выбрать собственное имя домена. Обычно это имя компании, торговая марка или что-нибудь столь же характерное. Для неанглоязычных стран используется транскрипция имен. Часто возникают конфликты, связанные с тем, что одно и то же имя используется несколькими фирмами (законодательство допускает это для фирм, работающих в разных отраслях); многие люди заранее резервируют имена, могущие стать популярными для последующей продажи их владельцу торговой марки; но это уже касается юридической стороны функционирования Internet и не входит в мою компетенцию.
С левого конца доменного имени находятся имена машин. Имена бывают "собственные" и "функциональные". Имена "собственные" каждый придумавает в меру фантазии: машинам присваиваются имена членов семьи, животных, растений, музыкантов и артистов, литературных персонажей - кто во что горазд.
Имена "функциональные" вытекают из функций, выполняемых машиной:
www HTTP (WWW) сервер ftp FTP сервер ns, nss, dns DNS (Name) сервер mail Mail сервер relay Mail Exchanger *proxy соответствующий Proxy серверЯ считаю нежелательным присваивать какой-либо машине функциональное имя - в любой момент может потребоваться перенести соответствующую функцию на другую машину. Для этого лучше всего использовать псевдонимы, которые перенаправляют запросы к данному имени на записи, относящиеся к другому имени. Но вот ссылаться на псевдонимы при обьявлении Mail Ехсhаngеr'ов и вообще использовать их в правой части записей считается нежелательным, а зачастую является недопустимым.
Как преобразовать DOC в RTF при помощи OLE.
Создайте новое приложение и пометите на него: кнопку с именем Button3, объект RitchText с именем WordEditor и компонент OpenDialog. Теперь посмотрим, как пролистывать все файлы *.doc и загружать их в объект RitchText. ЗАМЕЧАНИЕ: Format:=6 указывает Word-у на необходимость сохранять файл как RTF. Расширения недостаточно. Другие форматы файлов:
Format File
Argument Format
-------- ------
0 Нормальный (формат Word)
1 Шаблон документа
2 Только текст (extended characters saved in ANSI character set)
3 Текст+Переносы (plain text with line breaks; extended
characters saved in ANSI character set)
4 Только текст (PC-8) (extended characters saved in IBM PC
character set)
5 Текст+Переносы (PC-8) (text with line breaks; extended
characters saved in IBM PC character set)
6 Формат Rich-text (RTF)
procedure TImport_Form.ToolButton3Click(Sender: TObject);
var
WordApp: Variant;
begin
if OpenDialog1.Execute then
begin
Edit1.Text := ExtractFileName(OpenDialog1.FileName);
StatusBar1.SimpleText := OpenDialog1.FileName;
WordApp := CreateOleObject('Word.Basic');
if not VarIsEmpty(WordApp) then
begin
WordApp.FileOpen(OpenDialog1.FileName);
WordApp.FileSaveAs(Name := 'c:\temp_bb.rtf', Format := 6);
WordApp.AppClose;
WordApp := Unassigned;
WordEditor.Lines.LoadFromFile('c:\temp_bb.rtf');
end
else
ShowMessage('Could not start MS Word');
end;
end;
|
Пример шифрования данных.
Автор: Vit WEB сайт: http://forum.vingrad.ru
procedure DoEncode(var Source: string; const Key: string); asm Push ESI Push EDI Push EBX Or EAX,EAX Jz @Done Push EAX Push EDX Call UniqueString Pop EDX Pop EAX Mov EDI,[EAX] Or EDI,EDI Jz @Done Mov ECX,[EDI-4] Jecxz @Done Mov ESI,EDX Or ESI,ESI Jz @Done Mov EDX,[ESI-4] Dec EDX Js @Done Mov EBX,EDX Mov AH,DL Cld @L1: Test AH,8 Jnz @L3 Xor AH,1 @L3: Not AH Ror AH,1 Mov AL,[ESI+EBX] Xor AL,AH Xor AL,[EDI] Stosb Dec EBX Jns @L2 Mov EBX,EDX @L2: Dec ECX Jnz @L1 @Done: Pop EBX Pop EDI Pop ESI end;Комментарий от Chingachguk'a Мне кажется, у этого алгоритма есть два недостатка: 1) Код, сильно зависимый от компилятора. Далеко не всегда регистр EAX будет указывать на ячейку с адресом Source, а регистр EDX - на пароль(Key). Но это мелочь.
2) Единственный байт гаммы(или ксорирующей последовательности), который меняется при шифровании - это длина пароля. Остальные символы пароля НИКАК НЕ ПЕРЕМЕШИВАЮТСЯ в ходе шифрования. Алгоритм шифрования примерно такой:
Len := Lengh(Key);
Index := Lengh(Key) - 1;
i := 1;
repeat
Len := func1(Len);
Source[i] := (Key[Index] xor Len) xor Source[i];
dec(Index);
if Index := 0 then
Index := Lengh(Key) - 1;
until
i < Lenght(Source);
Нетрудно видеть, что основной для тупого подбора является длина пароля. Пусть она равна 10. Очевидно, что 1-ый,11,21.. символы будут зашифрованы ОДИНАКОВЫМ значением Key[Index], но разными значениями Len. Казалось бы, Len для 1,11,21... будет разным, но это ерунда - ведь Len вычисляется однозначно на ЛЮБОМ шаге через реккурентный закон func1 !
И это - фатальный недостаток.
Как избежать использования неактуальных указателей.
| Автор: David S. Becker |
| Программер просит у друга денег в долг: - Одолжи 250$ до получки, ну или для ровного счета 256? |
Этот модуль, конечно, ничего не делает с VCL, но тем не менее вы можете иметь неактуальные указатели и с VCL... Строгое соблюдение функций модуля сделает вас уверенным в отсутствии ошибок при работе с указателями. Единственное условие использования модуля - в случае любых изменений кода с вашей стороны или наличия каких-либо замечаний или предложений пришлите их пожалуйста мне. Пользуйтесь на здоровье!
unit Pointers;
{
Автор: David S. Becker (dsb@plaza.ds.adp.com)
Дата: 1/27/97
Авторские права: Нет
Дистрибутивные права: Свободные, неограниченное использование, в случае любых изменений кода
с вашей стороны или наличия каких-либо замечаний или предложений пришлите их пожалуйста мне.
Данный модуль создавался для помощи в управлении указателями и объектами. Так как
компилятор не инициализирует указатели и объекты в nil и не сбрасывает
их в nil при освобождении, существует вероятность применения неактуального
указателя. По этой причине я рекомендую добавление секции 'initialization'
во все модули и вызове Nilify() для всех указателей/объектов в данном модуле.
Это позволит быть уверенным, что все указатели/объекты стартуют как nil.
Кроме того, вместо стандартных аналогов, вы можете использовать NilFree
(для объектов), NilDispose (для указателей, создаваемых с помощью New),
и NilFreeMem (для указателей, создаваемых с помощью GetMem). Эти процедуры
безопасны при вызове nil-вых указателей/объектов, так как перед выполнением
любых действий они проверяют их на nil. После освобождения распределенной
указателем/объектом памяти они сбрасываются в nil. Строгое соблюдение функций
модуля значительно снижает риск использования неактуального указателя.
(Конечно, вы еще можете получить неактуальные указатели из VCL, т.к.
они, естественно, не используют данные функции.)
}
{==============================================================================}
interface
{------------------------------------------------------------------------------}
{ Проверка указателя на nil }
{ ПРИМЕЧАНИЕ: Данная функция отличается от Assigned() тем, что Assigned() }
{ требует переменную, а IsNil() нет. }
function IsNil(const p: Pointer): Boolean;
{ Устанавливает указатель в nil }
procedure Nilify(var p);
{ Освобождает не-nil объект и устанавливает его в nil }
procedure NilFree(o: TObject);
{ Освобождает не-nil указатель, созданный с помощью New и устанавливает его в nil }
procedure NilDispose(var p: Pointer);
{ Освобождает не-nil указатель и устанавливает его в nil }
procedure NilFreeMem(var p: Pointer; size: Word);
{==============================================================================}
implementation
{------------------------------------------------------------------------------}
function IsNil(const p: Pointer): Boolean;
begin
Result := (p = nil);
end;
{------------------------------------------------------------------------------}
procedure Nilify(var p);
begin
Pointer(p) := nil;
end;
{------------------------------------------------------------------------------}
procedure NilFree(o: TObject);
begin
if not IsNil(o) then
begin
o.Free;
Nilify(o);
end;
end;
{------------------------------------------------------------------------------}
procedure NilDispose(var p: Pointer);
begin
if not IsNil(p) then
begin
Dispose(p);
Nilify(p);
end;
end;
{------------------------------------------------------------------------------}
procedure NilFreeMem(var p: Pointer; size: Word);
begin
if not IsNil(p) then
begin
FreeMem(p, size);
Nilify(p);
end;
end;
end.
|
Как получить дескриптор окна Windows, содержащего DOS программу.
| Hiroshima-45... Chernobyl-86... Windows-95... |
Пример:
procedure TForm1.Button1Click(Sender: TObject);
var
info : TOSVersionInfo;
ClassName : string;
Title : string;
begin
{Проверяем - Win95 или NT.}
info.dwOSVersionInfoSize := sizeof(info);
GetVersionEx(info);
if (info.dwPlatformId = VER_PLATFORM_WIN32_NT) then begin
ClassName := 'ConsoleWindowClass';
Title := 'Command Prompt';
end else begin
ClassName := 'tty';
Title := 'MS-DOS Prompt';
end;
ShowMessage(IntToStr(FindWindow(PChar(ClassName), PChar(Title))));
end;
|
Выключение питания ATX корпуса из-под DOS.
| Автор: Колесников Сергей Александрович |
| Вам пора завязывать с компьютером, если ложась спать и закрыв глаза, вы ждете надписи "Теперь можно выключить питание". |
mov ax,5301h
sub bx,bx
int 15h
jb stop
mov ax,530eh
sub bx,bx
int 15h
jb stop
mov ax,5307h
mov bx,0001h
mov cx,0003h
int 15h
stop: int 20h
|
Или.
mov ax,5301h
sub bx,bx
int 15h
jc @@finish
mov ax,530Eh
sub bx,bx
mov cx,102h
int 15h
jc @@finish
mov ax,5307h
mov bx,1
mov cx,3
int 15h
@@finish:
int 20h
|
DOS команды.
| ...Ну! И кто открывал бутылку об F8 ?! |
При использовании DOS DBF файлов - перекодировка между форматами.
| Чайник y хакера спрашивает, что такое UNIX, Windows'95, OS/2 и DOS? - Hy представь себе аэропорт. На взлетной полосе стоит навороченный красивый самолет. Пассажиры в него заходят - там ковры везде, все класно. Запускают двигатели - и самолет медленно объезжает вокруг аэропорта, после чего пассажиры выходят. Вот это полуось. На другой полосе стоит еще один самолет - тоже весь из себя, внутри ковры. Садяться в него пассажиры, самолет разгоняется, взлетает, и тут же разваливается на куски - это Windows. А в стороне на поле стоит старый, помятый, грязный кукурузник - пассажиры подходят, кладут вещи, потом толкают самолет, разгоняют его и сами запрыгивают - потом с трудом летят - это DOS. - А что такое UNIX? - Ну я же тебе говорил: - Представь себе аэропорт... |
При использовании DOS DBF файлов можно сделать небольшую программку (или процедурку), которая произведет перекодировку между форматами. что-то типа:
function update_dos(s:string):string;
var c:STRING;
I:INTEGeR;
l:byte;
dd:char;
begin
i:=1;
c:='';
while i< length(s)+1 do
begin
l:=ord(s[i]);
inc(i);
if (l>=128) and (l<=192)then l:=l+64 else
if (l>=224) and (l<240) then l:=l+16 else
if l=241 then l:=184 else
if l=240 then l:=168;
dd:=chr(l);
c:=c+dd;
end;
update_dos:=c;
end;
function update_win(s:string):string;
var c:STRING;
I:INTEGeR;
l:byte;
dd:char;
begin
i:=1;
c:='';
while i< length(s)+1 do
begin
l:=ord(s[i]);
inc(i);
if (l>=192) and (l<240)then l:=l-64 else
if (l>=240) and (l<256) then l:=l-16 else
if l=184 then l:=241 else
if l=168 then l:=240;
dd:=chr(l);
c:=c+dd;
end;
update_win:=c;
end;
|
Это и туда и обратно, у меня работает на старых DBF. Осталось только вызвать в нужный момент.
Получение переменных окружения DOS.
| Автор: Dr. Bob |
| - Служба технической подержки, здравствуйте! - У меня не запускается компьютер. Ваш высокоскоростной интернет сломал его. - Что вы имеете ввиду? - Я говорю вам, он не запускается. - Он работал, когда наш специалист был у Вас. - Да, он перестал работать после того, как я его накрыла. - Накрыли? - Я накрыла его пластиковой пленкой, чтобы туда не пропадали кошачьи волосы. У меня много кошек. Не плохая идея, на правда ли? |
Функция GetEnvironmentVariable возвращает значения переменных среды опрашиваемого процесса. Величина также возвращается в виде строки с завершающим нулем.
Получение переменных окружения DOS 2.
unit TDosEnv;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs;
type
TDosEnvironment = class(TComponent)
public
{ Public объявления класса }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
private
{ Объявления Private-полей }
FDosEnvList: TStringList;
procedure DoNothing(const Value: TStringList);
protected
{ Объявления Protected-методов }
dummy: Word;
function GetDosEnvCount: Word;
public
{ Public interface объявления }
function GetDosEnvStr(const Name: string): string;
{ Данная функция является измененной версией функции GetEnvVar,
присутствующей в поставляемом с Delphi модуле WinDos. Она
использует паскалевские строки вместо строк с терминирующим нулем.
}
published
{ Published design объявления }
property DosEnvCount: Word read GetDosEnvCount write dummy;
property DosEnvList: TStringList read FDosEnvList write DoNothing;
end;
procedure Register;
implementation
constructor TDosEnvironment.Create(AOwner: TComponent);
var
P: PChar;
i: Integer;
begin
inherited Create(AOwner);
FDosEnvList := TStringList.Create;
P := GetDosEnvironment; { Win API }
i := 0;
while P^ <> #0 do
begin
Inc(i);
FDosEnvList.Add(StrPas(P));
Inc(P, StrLen(P) + 1) { Быстрый переход к следующей переменной }
end
end {Create};
destructor TDosEnvironment.Destroy;
begin
FDosEnvList.Free;
FDosEnvList := nil;
inherited Destroy
end {Destroy};
procedure TDosEnvironment.DoNothing(const Value: TStringList);
begin
MessageDlg('TDosEnvironment.DosEnvList только для чтения!', mtInformation,
[mbOk], 0)
end {DoNothing};
function TDosEnvironment.GetDosEnvCount: Word;
{ Возвращает количество переменных окружения.
}
begin
if Assigned(FDosEnvList) then {!!}
Result := FDosEnvList.Count
else
Result := 0;
end {GetDosEnvCount};
function TDosEnvironment.GetDosEnvStr(const Name: string): string;
{ Данная функция является измененной версией функции GetEnvVar,
присутствующей в поставляемом с Delphi модуле WinDos. Она
использует паскалевские строки вместо строк с терминирующим нулем.
}
var
i: Integer;
Tmp: string;
Len: Byte absolute Name;
begin
i := 0;
Result := '';
if Assigned(FDosEnvList) then {!!}
while i < FDosEnvList.Count do
begin
Tmp := FDosEnvList[i];
Inc(i);
if Pos(Name, Tmp) = 1 then
begin
Delete(Tmp, 1, Len);
if Tmp[1] = '=' then
begin
Delete(Tmp, 1, 1);
Result := Tmp;
i := FDosEnvList.Count { конец while-цикла }
end
end
end
end {GetDosEnvStr};
procedure Register;
begin
RegisterComponents('Dr.Bob', [TDosEnvironment]);
end {Register};
end.
|
Считывать строки из текстового DOS файла.
| Автор: Dimka Maslov |
| Налоговая полиция конфисковала большую партию пиратских дисков с Windows-98. То, что программа ненастоящая, стало ясно после того, как она поднялась за считанные секунды и к тому же нормально работала на 486-м процессоре. |
procedure ReadDosTextFile(FileName: string; List: TStrings); var S: string; i: integer; begin List.BeginUpdate; try List.LoadFromFile(FileName); for i:=0 to List.Count - 1 do begin S:=List[i]; OemToChar(PChar(S), PChar(S)); List[i]:=S; end; finally List.EndUpdate; end; end; procedure WriteDosTextFile(FileName: string; List: TStrings); var S: string; I: integer; L: TStringList; begin L:=TStringList.Create; try L.Assign(List); for i:=0 to L.Count - 1 do begin S:=L[i]; CharToOem(PChar(S), PChar(S)); L[i]:=S; end; L.SaveToFile(FileName); finally L.Free; end; end; |
Получить выходные данные DOS приложения.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{----------------------------CreateDOSProcessRedirected---------------------------
Description : executes a (DOS!) app defined in the CommandLine parameter redirected
to take input from InputFile and give output to OutputFile
Result : True on success
Parameters :
CommandLine : the command line for the app, including its full path
InputFile : the ascii file where from the app takes input
OutputFile : the ascii file to which the app's output is redirected
ErrMsg : additional error message string. Can be empty
Error checking : YES
Target : Delphi 2, 3, 4
Author : Theodoros Bebekis, email bebekis@otenet.gr
Notes :
Example call : CreateDOSProcessRedirected('C:\MyDOSApp.exe',
'C:\InputPut.txt',
'C:\OutPut.txt',
'Please, record this message')
-----------------------------------------------------------------------------------}
function CreateDOSProcessRedirected(const CommandLine, InputFile, OutputFile,
ErrMsg: string): Boolean;
const
ROUTINE_ID = '[function: CreateDOSProcessRedirected ]';
var
OldCursor: TCursor;
pCommandLine: array[0..MAX_PATH] of Char;
pInputFile, pOutPutFile: array[0..MAX_PATH] of Char;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
SecAtrrs: TSecurityAttributes;
hAppProcess, hAppThread, hInputFile, hOutputFile: THandle;
begin
Result := False;
{ check for InputFile existence }
if not FileExists(InputFile) then
raise Exception.CreateFmt(ROUTINE_ID + #10 + #10 +
'Input file * %s *' + #10 +
'does not exist' + #10 + #10 +
ErrMsg, [InputFile]);
{ save the cursor }
OldCursor := Screen.Cursor;
Screen.Cursor := crHourglass;
{ copy the parameter Pascal strings to null terminated strings }
StrPCopy(pCommandLine, CommandLine);
StrPCopy(pInputFile, InputFile);
StrPCopy(pOutPutFile, OutputFile);
try
{ prepare SecAtrrs structure for the CreateFile calls
This SecAttrs structure is needed in this case because
we want the returned handle can be inherited by child process
This is true when running under WinNT.
As for Win95 the documentation is quite ambiguous }
FillChar(SecAtrrs, SizeOf(SecAtrrs), #0);
SecAtrrs.nLength := SizeOf(SecAtrrs);
SecAtrrs.lpSecurityDescriptor := nil;
SecAtrrs.bInheritHandle := True;
{ create the appropriate handle for the input file }
hInputFile := CreateFile(pInputFile,
{ pointer to name of the file }
GENERIC_READ or GENERIC_WRITE,
{ access (read-write) mode }
FILE_SHARE_READ or FILE_SHARE_WRITE,
{ share mode } @SecAtrrs, { pointer to security attributes }
OPEN_ALWAYS, { how to create }
FILE_ATTRIBUTE_TEMPORARY, { file attributes }
0); { handle to file with attributes to copy }
{ is hInputFile a valid handle? }
if hInputFile = INVALID_HANDLE_VALUE then
raise Exception.CreateFmt(ROUTINE_ID + #10 + #10 +
'WinApi function CreateFile returned an invalid handle value' +
#10 +
'for the input file * %s *' + #10 + #10 +
ErrMsg, [InputFile]);
{ create the appropriate handle for the output file }
hOutputFile := CreateFile(pOutPutFile,
{ pointer to name of the file }
GENERIC_READ or GENERIC_WRITE,
{ access (read-write) mode }
FILE_SHARE_READ or FILE_SHARE_WRITE,
{ share mode } @SecAtrrs, { pointer to security attributes }
CREATE_ALWAYS, { how to create }
FILE_ATTRIBUTE_TEMPORARY, { file attributes }
0); { handle to file with attributes to copy }
{ is hOutputFile a valid handle? }
if hOutputFile = INVALID_HANDLE_VALUE then
raise Exception.CreateFmt(ROUTINE_ID + #10 + #10 +
'WinApi function CreateFile returned an invalid handle value' +
#10 +
'for the output file * %s *' + #10 + #10 +
ErrMsg, [OutputFile]);
{ prepare StartupInfo structure }
FillChar(StartupInfo, SizeOf(StartupInfo), #0);
StartupInfo.cb := SizeOf(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
StartupInfo.wShowWindow := SW_HIDE;
StartupInfo.hStdOutput := hOutputFile;
StartupInfo.hStdInput := hInputFile;
{ create the app }
Result := CreateProcess(nil, { pointer to name of executable module }
pCommandLine,
{ pointer to command line string }
nil, { pointer to process security attributes }
nil, { pointer to thread security attributes }
True, { handle inheritance flag }
CREATE_NEW_CONSOLE or
REALTIME_PRIORITY_CLASS, { creation flags }
nil, { pointer to new environment block }
nil, { pointer to current directory name }
StartupInfo, { pointer to STARTUPINFO }
ProcessInfo); { pointer to PROCESS_INF }
{ wait for the app to finish its job and take the handles to free them later }
if Result then
begin
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
hAppProcess := ProcessInfo.hProcess;
hAppThread := ProcessInfo.hThread;
end
else
raise Exception.Create(ROUTINE_ID + #10 + #10 +
'Function failure' + #10 + #10 +
ErrMsg);
finally
{ close the handles
Kernel objects, like the process and the files we created in this case,
are maintained by a usage count.
So, for cleaning up purposes we have to close the handles
to inform the system that we don't need the objects anymore }
if hOutputFile <> 0 then CloseHandle(hOutputFile);
if hInputFile <> 0 then CloseHandle(hInputFile);
if hAppThread <> 0 then CloseHandle(hAppThread);
if hAppProcess <> 0 then CloseHandle(hAppProcess);
{ restore the old cursor }
Screen.Cursor := OldCursor;
end;
end;
Конвертирует строку из кодировки DOS в кодировку Windows и обратно.
Автор: Сергей WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Конвертирует строку из кодировки DOS в кодировку Windows и обратно
Конвертирует строку из кодировки DOS в кодировку Windows и обратно
Зависимости: ???
Автор: Сергей, nfkazak@inbox.ru, Краснодар
Copyright: VIP BANK
Дата: 11 сентября 2002 г.
***************************************************** }
function WinToDos(St: string): string;
var
Ch: PChar;
begin
Ch := StrAlloc(Length(St) + 1);
AnsiToOem(PChar(St), Ch);
Result := Ch;
StrDispose(Ch)
end;
function DosToWin(St: string): string;
var
Ch: PChar;
begin
Ch := StrAlloc(Length(St) + 1);
OemToAnsi(PChar(St), Ch);
Result := Ch;
StrDispose(Ch)
end;
Как сделать имитацию ввода с клавиатуры для программы выполняющейся в DOS-окне.
| Автор: Nomadic |
| Настоящий хакер, после того, как у него закончаться запасы еды, ещё неделю может питаться крошками из клавиатуры. |
const
ExtendedKeys: set of Byte = [ // incomplete list
VK_INSERT, VK_DELETE, VK_HOME, VK_END, VK_PRIOR, VK_NEXT,
VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN, VK_NUMLOCK
];
procedure SimulateKeyDown(Key: byte);
var
flags: DWORD;
begin
if Key in ExtendedKeys then
flags := KEYEVENTF_EXTENDEDKEY
else
flags := 0;
keybd_event(Key, MapVirtualKey(Key, 0), flags, 0);
end;
procedure SimulateKeyUp(Key: byte);
var
flags: DWORD;
begin
if Key in ExtendedKeys then
flags := KEYEVENTF_EXTENDEDKEY
else
flags := 0;
keybd_event(Key, MapVirtualKey(Key, 0), KEYEVENTF_KEYUP or flags, 0);
end;
procedure SimulateKeystroke(Key: byte);
var
flags: DWORD;
scancode: BYTE;
begin
if Key in ExtendedKeys then
flags := KEYEVENTF_EXTENDEDKEY
else
flags := 0;
scancode := MapVirtualKey(Key, 0);
keybd_event(Key,
scancode,
flags,
0);
keybd_event(Key,
scancode,
KEYEVENTF_KEYUP or flags,
0);
end;
|
Как скачать любой URL используя стандартные настройки сети.
| - Почему русские не выдвигаются в правительство Норвегии? - А вы хотите, чтобы у вас был е-майл premier-ministr@gov.no? |
Начиная с Internet Explorer 3, Microsoft поддерживает очень полезные API, Wininet. Эти функции позволяют использовать все возможности IE, такие как настройки прокси, кэширование файлов и т.д.
Ниже приведён пример использования этих функций для скачивания файла с нужного URL. Это может быть любой доступный URL, ftp://, http://, gopher://, и т.д.
Более подробную информацию об этих функция можно посмотреть в MSDN - Win32 Internet API Functions.
function DownloadFile(const Url: string): string;
var
NetHandle: HINTERNET;
UrlHandle: HINTERNET;
Buffer: array[0..1024] of char;
BytesRead: cardinal;
begin
Result := '';
NetHandle := InternetOpen('Delphi 5.x', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
if Assigned(NetHandle) then
begin
UrlHandle := InternetOpenUrl(NetHandle, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);
{ UrlHandle правильный? Начинаем загрузку }
if Assigned(UrlHandle) then
begin
FillChar(Buffer, SizeOf(Buffer), 0);
repeat
Result := Result + Buffer;
FillChar(Buffer, SizeOf(Buffer), 0);
InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer), BytesRead);
until
BytesRead = 0;
InternetCloseHandle(UrlHandle);
end
else
begin
{ UrlHandle неправильный. Генерируем исключительную ситуацию. }
raise Exception.CreateFmt('Cannot open URL %s', [Url]);
end;
InternetCloseHandle(NetHandle);
end
else
{ NetHandle недопустимый. Генерируем исключительную ситуацию }
raise Exception.Create('Unable to initialize Wininet');
end;
//-------------------------------------------------
implementation
uses
SysUtils,Windows,ShlObj;
function NetShareAdd(ServerName:PChar; //указатель на имя компьютера, например '\\Server'#0, если свой, то можно nil
Level:Word; //уровень структуры Share_info, здесь 50
PShareInfo:PChar; //указатель на структуру Share_Info
ParmErr:DWord) //указатель на ???
:dword;stdcall;external 'svrapi.dll';//svrapi для Win9X, NetApi32 для NT
function NetShareDel(ServerName:PChar;
NetName:PChar;
Reserved:DWord):dword;stdcall;external 'svrapi.dll';
type
_share_info_50 = record //структура Share уровня 50
NetName: array [1..13] of char; //Как будет называться диск в сети
SType: byte; //тип =0 (STYPE_DISKTREE) - шарить диски
Flags: word; //флаги $0191,$0192,$0193....(доступ из сети)
Remark: PChar; //указатель на комментарий, видимый из сети
Path: PChar; //указатель на имя ресурса, например 'c:\'#0
RW_Password: array [1..9] of char; //пароль для полного доступа, если не нужен =#0
RO_Password: array [1..9] of char; //пароль для доступа на чтение, если не нужен =#0
end;
//----------------------------
function SetShareOnDisk(HostName,LocalPath:string; NetName:TNetName;Remark:string;
Access:word;RO_Passw,RW_Passw:TPassw):boolean; var ShareInfo:_Share_Info_50;
begin
Result:=false;
StrPCopy(@ShareInfo.NetName,NetName);
ShareInfo.SType:=0;
ShareInfo.Flags:=Access;
ShareInfo.Remark:=PChar(Remark);
ShareInfo.Path:=PChar(LocalPath);
StrPCopy(@ShareInfo.RO_Password,RO_Passw);
StrPCopy(@ShareInfo.RW_Password,RW_Passw);
ShareResult:=NetShareAdd(PChar(HostName), 50,@ShareInfo,$0000002a); //вызываем Share
if ShareResult <> 0 then //расшарить неудалось
Exit;
SHChangeNotify(SHCNE_NETSHARE,SHCNF_PATH,PChar(LocalPath),nil); //сказать шеллу об изменениях
Result:=true;
end;
//----------------------------
function RemoveShareFromDisk(HostName, NetName, LocalPath: string): boolean;
begin
Result:=false;
ShareResult:=NetShareDel(PChar(HostName),PChar(NetName),0); //удалить шару
if ShareResult <> 0 then
Exit;
SHChangeNotify(SHCNE_NETUNSHARE, SHCNF_PATH,PChar(LocalPath),nil); //сказать шеллу об изменениях
Result:=true;
end;
end.
|
Закачать файл из Интернета.
| Сколько пользуюсь интернетом - никак не могу понять: я вхожу в интернет или я выхожу в интернет? |
uses
URLMon, ShellApi;
function DownloadFile(SourceFile, DestFile: string): Boolean;
begin
try
Result := UrlDownloadToFile(nil, PChar(SourceFile), PChar(DestFile), 0, nil) = 0;
except
Result := False;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
const
// URL Location
SourceFile = 'http://www.google.com/intl/de/images/home_title.gif';
// Where to save the file
DestFile = 'c:\temp\google-image.gif';
begin
if DownloadFile(SourceFile, DestFile) then
begin
ShowMessage('Download succesful!');
// Show downloaded image in your browser
ShellExecute(Application.Handle, PChar('open'), PChar(DestFile),
PChar(''), nil, SW_NORMAL)
end
else
ShowMessage('Error while downloading ' + SourceFile)
end;
// Minimum availability: Internet Explorer 3.0
// Minimum operating systems Windows NT 4.0, Windows 95
{********************************************************}
{2.}
uses
Wininet;
function DownloadURL(const aUrl: string): Boolean;
var
hSession: HINTERNET;
hService: HINTERNET;
lpBuffer: array[0..1024 + 1] of Char;
dwBytesRead: DWORD;
begin
Result := False;
// hSession := InternetOpen( 'MyApp', INTERNET_OPEN_TYPE_DIRECT, nil, nil, 0);
hSession := InternetOpen('MyApp', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
try
if Assigned(hSession) then
begin
hService := InternetOpenUrl(hSession, PChar(aUrl), nil, 0, 0, 0);
if Assigned(hService) then
try
while True do
begin
dwBytesRead := 1024;
InternetReadFile(hService, @lpBuffer, 1024, dwBytesRead);
if dwBytesRead = 0 then break;
lpBuffer[dwBytesRead] := #0;
Form1.Memo1.Lines.Add(lpBuffer);
end;
Result := True;
finally
InternetCloseHandle(hService);
end;
end;
finally
InternetCloseHandle(hSession);
end;
end;
|
Закачать файл из Интернета 2.
| Сколько пользуюсь интернетом - никак не могу понять: я вхожу в интернет или я выхожу в интернет? |
uses
Wininet;
function DownloadURL(const aUrl: string): Boolean;
var
hSession: HINTERNET;
hService: HINTERNET;
lpBuffer: array[0..1024 + 1] of Char;
dwBytesRead: DWORD;
begin
Result := False;
// hSession := InternetOpen( 'MyApp', INTERNET_OPEN_TYPE_DIRECT, nil, nil, 0);
hSession := InternetOpen('MyApp', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
try
if Assigned(hSession) then
begin
hService := InternetOpenUrl(hSession, PChar(aUrl), nil, 0, 0, 0);
if Assigned(hService) then
try
while True do
begin
dwBytesRead := 1024;
InternetReadFile(hService, @lpBuffer, 1024, dwBytesRead);
if dwBytesRead = 0 then break;
lpBuffer[dwBytesRead] := #0;
Form1.Memo1.Lines.Add(lpBuffer);
end;
Result := True;
finally
InternetCloseHandle(hService);
end;
end;
finally
InternetCloseHandle(hSession);
end;
end;
|
Закачать файл из Интернета 3.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{Forces a download of the requested file, object, or directory
listing from the origin server, not from the cache}
function DownloadURL_NOCache(const aUrl: string; var s: String): Boolean;
var
hSession: HINTERNET;
hService: HINTERNET;
lpBuffer: array[0..1024 + 1] of Char;
dwBytesRead: DWORD;
begin
Result := False;
s := '';
// hSession := InternetOpen( 'MyApp', INTERNET_OPEN_TYPE_DIRECT, nil, nil, 0);
hSession := InternetOpen('MyApp', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
try
if Assigned(hSession) then
begin
hService := InternetOpenUrl(hSession, PChar(aUrl), nil, 0, INTERNET_FLAG_RELOAD, 0);
if Assigned(hService) then
try
while True do
begin
dwBytesRead := 1024;
InternetReadFile(hService, @lpBuffer, 1024, dwBytesRead);
if dwBytesRead = 0 then break;
lpBuffer[dwBytesRead] := #0;
s := s + lpBuffer;
end;
Result := True;
finally
InternetCloseHandle(hService);
end;
end;
finally
InternetCloseHandle(hSession);
end;
end;
//aufrufen
var
s: String;
begin
if DownloadURL('http://www.swissdelphicenter.ch/', s) then
ShowMessage(s);
end;
Качаем с докачкой.
| В Японии скончался старейший пингвин в мире. Linux объявил 3-х дневный траур... |
// ПРЕДИСЛОВИЕ:
{
Копаясь как-то в исходниках модулей третьей Delphi, я наткнулся на файл,
который назывался WinInet.pas. Имея врожденное любопытство, я заглянул
в него и нашел там очень много интересных вещей. О некоторых из них я
попытаюсь рассказать в данной статье, в частности, как, используя этот
модуль, организовать докачку файлов при обрыве связи. В модуле WinInet.pas
содержатся описания прототипов функций и некоторых типов входящих в т.н.
Microsoft Windows Internet Extensions, описания которых я не нашел в
справочной системе (хотя может плохо искал) :-(. Поэтому пришлось идти
почти вслепую.
}
// ТЕОРИЯ:
{
Для начала рассмотрим все функции, константы и типы, которые мы будем
использовать:
}
// 1) HINTERNET, вот как он описан:
type
HINTERNET = Pointer;
PHINTERNET = ^HINTERNET;
// При детальном рассмотрении, это обычный указатель.
// 2) функции InternetOpen и InternetCloseHandle:
function InternetOpen(lpszAgent: PChar; dwAccessType: DWORD;
lpszProxy, lpszProxyBypass: PChar; dwFlags: DWORD): HINTERNET; stdcall;
{
где:
lpszAgent <-|Имя программы, с помощью которой мы соединяемся,
|может принимать любые значения
dwAccessType <-|Каким макаром соединяться с и-нетом
|принимаемые значения:
| PRE_CONFIG_INTERNET_ACCESS -как в системном реестре
| LOCAL_INTERNET_ACCESS -напрямую
| GATEWAY_INTERNET_ACCESS -через GateWay
| CERN_PROXY_INTERNET_ACCESS -через проксю
lpszProxy <-|Имя прокси сервера (ставим в nil)
lpszProxyBypass<-|Не уверен, но смахивает на имена хостов, для которых не
|использовать проксю (ставим в nil)
dwFlags <-|Принимаеемые значения:
| INTERNET_FLAG_ASYNC -этот запрос асинхронный (если есть
| поддержка), но мы поставим 0
}
// возвращает пресловутый HINTERNET, который будет требоваться при вызове
// всех остальных функций. С вызова этой функции начинается вся наша работа
// с интернетом, а с вызова второй заканчивается.
function InternetCloseHandle(hInet: HINTERNET): BOOL; stdcall;
// где: nInet ранее созданый указатель.
// 3) функция InternetOpenUrl:
function InternetOpenUrl(hInet: HINTERNET; lpszUrl: PChar;
lpszHeaders: PChar; dwHeadersLength: DWORD; dwFlags: DWORD;
dwContext: DWORD): HINTERNET; stdcall;
{
где:
hInet <-|Ранее созданый указатель
lpszUrl <-|Сам УРЛ
lpszHeaders <-|Дополнительные строки в НТТР запрос
dwHeadersLength<-|Длинна предыдущего
dwFlags <-|Принимаемые значения:
| INTERNET_FLAG_RAW_DATA -принимать как RAW данные
| INTERNET_FLAG_EXISTING_CONNECT -не создавать для
| объекта нового соединения
| (поставим в 0)
dwContext <-|пока не знаю, ставим в 0
}
// Функция возвращает HINTERNET, указывающий на конкретный файл (далее он в
// параметрах функций будет называться hFile).
// 4) функция InternetReadFile:
function InternetReadFile(hFile: HINTERNET; lpBuffer: Pointer;
dwNumberOfBytesToRead: DWORD; var lpdwNumberOfBytesRead: DWORD): BOOL; stdcall;
{
где:
hFile <-|Указатель, созданый предыдущей функцией
lpBuffer <-|Указатель на буфер куда читать
dwNumberOfBytesToRead<-|Сколько максимум читать (можно сказать размер
| буфера, хотя не факт)
lpdwNumberOfBytesRead<-|Сколько реально прочитано байт
}
// Этой функой мы будем читать файл из и-нета.
// 5) функция InternetSetFilePointer:
function InternetSetFilePointer(hFile: HINTERNET;
lDistanceToMove: Longint; pReserved: Pointer;
dwMoveMethod, dwContext: DWORD): DWORD; stdcall;
{
где:
hFile <-|Указатель созданый функцией InternetOpenUrl
lDistanceToMove<-|На сколько байт смещать указатель
pReserved <-|??
dwMoveMethod <-|Как смещать (=0)
dwContext <-|??
}
// Собственно, эта функция и поможет нам организовать докачку. Она смещает
// указатель в файле, после чего передача файла начнется с этого места.
// В принципе этих данных уже достаточно для наших целей, но есть еще одна
// полезная функция, которая пригодится нам:
function InternetQueryDataAvailable(hFile: HINTERNET; var lpdwNumberOfBytesAvailable: DWORD;
dwFlags, dwContext: DWORD): BOOL; stdcall;
{
где:
hFile <-|Указатель, созданный функцией InternetOpenUrl
lpdwNumberOfBytesAvailable<-|Сколько осталось байт
dwFlags <-|??
dwContext <-|??
}
// Как вы уже догадались, с помощью этой функции можно узнать сколько
// осталось байт скачать (или размер файла, если вызвать ее сразу после
// InternetOpenUrl).
//Ну, собственно, и все по теории.
// ПРАКТИКА:
|
Условия задачи:
Скачиваемый файл сохраняется как c:\123.tmp При очередном старте скачки идет проверка на наличие оного файла на винте, если он есть, считаем что надо докачивать. Размер этого файла является признаком того, с какого места надо качать.Требуемые материалы:
Форма (TForm)-1 шт. Кнопки (TButton)-2 шт. Строка ввода (TEdit)-1 шт. Progress bar для красоты (TProgressBar)-1 шт. Метки (TLabel)-по необходимости.Далее идет полный листинг модуля:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
wininet,
StdCtrls, ComCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit; //<-строка для УРЛа
Label1: TLabel;
Button1: TButton; //<-кнопка Start
Button2: TButton; //<-кнопка Stop
ProgressBar1: TProgressBar; //<-декорация
procedure Button1Click(Sender: TObject); //<-|процедура начала скачки
procedure Button2Click(Sender: TObject); //<-|принудительный обрыв
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
stop: boolean; //<-|вспомогательная переменная отв. за
// |остановку скачки
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
hInet, //<-переменная сод. указатель на сессию
hURL: HINTERNET; //<-указатель на URL
fSize, //<-размер файла
ReadLen, //<-количество реально прочитанных байт
RestartPos: DWORD; //<-|позиция с которой начинается
// |докачка
fBuf: array[1..1024] of byte; //<-буфер куда качаем
f: file; //<-файл куда качаем
Header: string; //<-|дополнительная переменная в HTTP
// |заголовок
begin
RestartPos := 0; //<- |инициализация
fSize := 0; //<- |переменных
Button1.Enabled := false;
Button2.Enabled := true;
//Если на винте есть файл то считаем, что нужно докачивать
if FileExists('c:\123.tmp') then
begin
AssignFile(f, 'c:\123.tmp');
Reset(f, 1);
RestartPos := FileSize(F);
Seek(F, FileSize(F));
end
else
begin
//иначе с начала
AssignFile(f, 'c:\123.tmp');
ReWrite(f, 1);
end;
//открываем сессию
hInet := InternetOpen('Mozilla',
PRE_CONFIG_INTERNET_ACCESS,
nil,
nil,
0);
//Пишем дополнительную строку для заголовка
Header := 'Accept: */*';
//открываем URL
hURL := InternetOpenURL(hInet,
PChar(Edit1.Text),
pchar(Header),
StrLen(pchar(Header)),
0,
0);
//устанавливаем позицию в файле для докачки
if RestartPos > 0 then
InternetSetFilePointer(hURL,
RestartPos,
nil,
0,
0);
//смотрим ск-ко надо скачать
InternetQueryDataAvailable(hURL, fSize, 0, 0);
if RestartPos > 0 then
begin
ProgressBar1.Min := 0;
ProgressBar1.Max := fSize + RestartPos;
ProgressBar1.Position := RestartPos;
end
else
begin
ProgressBar1.Min := 0;
ProgressBar1.Max := fSize + RestartPos;
end;
//качаем до тех пор пока реально прочитаное число байт не
//будет равно нулю или не стор
while (ReadLen <> 0) and (stop = false) do
begin
//читаем в буфер
InternetReadFile(hURL, @fBuf, SizeOf(fBuf), ReadLen);
//смотрим ск-ко осталось докачать
InternetQueryDataAvailable(hURL, fSize, 0, 0);
ProgressBar1.Position := ProgressBar1.Max - fSize;
BlockWrite(f, fBuf, ReadLen); //<-пишем в файл
Application.ProcessMessages;
end;
stop := false;
Button1.Enabled := true;
Button2.Enabled := false;
InternetCloseHandle(hURL); //<-|закрываем
InternetCloseHandle(hInet); //<-|сессии
CloseFile(f); //<-|и файл
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
stop := false; //<-прервать скачку
Button2.Enabled := false; //<-кнопка останова скачки
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
stop := true; //<-сообщаем о необходимости прерывания скачки
end;
end.
|
Как скачать файл через Proxy.
| - Сынок! Сколько раз тебе говорить, что я тебя родила, а не скачала из интернета!!! |
DownloadFile('http://some.com/some.zip', 'c:\some.zip');
function DownloadFile(const FileURL, FileName: String): Cardinal;
var
hSession, hFile: HInternet;
Buffer: array[1..1024] of Byte;
BufferLen, fSize: LongWord;
f: File;
begin
Result := 0;
hSession := InternetOpen('STEROID Download',
INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
if Assigned(hSession) then begin
hFile := InternetOpenURL(hSession, PChar(FileURL), nil, 0,
INTERNET_FLAG_RELOAD, 0);
if Assigned(hFile) then begin
AssignFile(f, FileName);
Rewrite(f,1);
fSize := 0;
repeat
InternetReadFile(hFile, @Buffer, SizeOf(Buffer), BufferLen);
BlockWrite(f, Buffer, BufferLen);
fSize := fSize + BufferLen;
until (BufferLen = 0);
CloseFile(f);
Result := fSize;
InternetCloseHandle(hFile);
end;
InternetCloseHandle(hSession);
end;
end;
|
Комментарий:
function InternetOpen(lpszAgent: PChar;
dwAccessType: DWORD;
lpszProxy, lpszProxyBypass: PChar;
dwFlags: DWORD): HINTERNET; stdcall;
|
Автоматизация создание BackUp-ов проектов.
Оформил: DeeCo
Автор: ReNoiZer.
Пробовал я много разного рода BackUp креаторов, и вот к чему я пришел: WinRAR + .BAT(CMD) - лучше нет (даже при разработке в команде)Пример использования:
Файл BackUp.CMD@echo Off echo --------------------------- echo RNZ prj. BackUp batch echo --------------------------- @echo On set tmpName=MyProject_src set backupDir=_BackUp\D6\Prj\MyProject start /w winrar a -r -y -ag_YYMMDD_HHMM -x.\Data\*.* -x@.\xlist.lst "%temp%\%tmpName%" %1 mkdir "..\_sources" mkdir C:\"%backupDir%" mkdir D:\"%backupDir%" mkdir E:\"%backupDir%" mkdir \\"MyNetDir\%backupDir%" copy /y "%temp%\%tmpName%*.rar" "..\_sources" copy /y "%temp%\%tmpName%*.rar" C:\"%backupDir%" copy /y "%temp%\%tmpName%*.rar" D:\"%backupDir%" copy /y "%temp%\%tmpName%*.rar" E:\"%backupDir%" copy /y "%temp%\%tmpName%*.rar" \\"MyNetDir\%backupDir%" del "%temp%\%tmpName%*.rar" rem start "..\_sources" rem start C:\"%backupDir%" rem start D:\"%backupDir%" rem start E:\"%backupDir%" rem start \\"MyNetDir\%backupDir%" set tmpName= set backupDir=
BackUp.CMD и xlist.lst копируем в директорию(каталог, папку) с проектом, xlist.lst содержит список исключений и может иметь примерно следующий вид:
Файл xlist.lst.
~*.* *.~* *.rar *.bak *.dcu *.res *.exe _qsq*.*Разобраться, как это работает, думаю не составит труда, даже только начавшему программировать пиплу. Вот такие дела 8)
Интерфейс присоединения Drag-and-Dock.
Эта возможность появилась в Delphi 4. Она "подсмотрена" опять-таки у разработчиков из Microsoft, внедривших плавающие панели инструментов в MS Office, Internet Explorer и другие продукты (рис. 27.2). Речь идет о том, что ряд элементов управления (а конкретно — потомки класса xwinControl) могут служить носителями (доками) для других элементов управления с возможностью их динамического перемещения из одного дока в другой при помощи мыши. Перетаскивать можно практически все — от статического текста до форм включительно. Пример использования техники Drag-and-Dock дает сама среда разработки Delphi — с ее помощью можно объединять на экране различные инструменты, такие как Инспектор объектов и Менеджер проекта. Как и в случае с технологией перетаскивания Drag-and-Drop, возможны два варианта реализации техники Drag-and-Dock: автоматический и ручной. В первом случае дело сводится к установке нужных значений для нескольких свойств, а остальную часть работы берет на себя код VCL; во втором, как следует из названия, вся работа возлагается на программиста. Итак, что же нужно сделать для внедрения Drag-and-Dock? В Инспекторе объектов необходимо изменить значение свойства DragKind на dkDock, a свойства DragMode — на dmAutomatic. Теперь этот элемент управления можно перетаскивать с одного носителя-дока на другой. Носителем других компонентов (доком) может служить потомок TwinControl. У него есть свойство Docksite, установка которого в True разрешает перенос на него других компонентов. Если при этом еще и установить свойство AutoSize в True, док будет автоматически масштабироваться в зависимости от того, что на нем находится. В принципе, этими тремя операциями исчерпывается минимальный обязательный набор.Естественно, для программиста предусмотрены возможности контроля за этим процессом. Каждый переносимый элемент управления имеет два события, возникающие в моменты начала и конца переноса:
type
TStartDockEvent = procedure(Sender: TObject;
var DragObject: TDragDockObject) of object;
TEndDragEvent = procedure(Sender, Target: TObject;
X, Y: Integer) of object;
В первом из методов sender — это переносимый объект, a DragObject — специальный объект, создаваемый на время процесса переноса и содержащий его свойства. Во втором sender — это также переносимый объект, a Target — объект-док.
Док тоже извещается о событиях во время переноса:
type
TGetSitelnfoEvent = procedure(Sender: TObject; DockClient: TControl;
var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean) of object;
TDockOverEvent = procedure(Sender: TObject; Source: TDragDockObject;
X, Y: Integer; State: TDragState; var Accept: Boolean) of object;
TDockDropEvent = procedure(Sender: TObject;
Source: TDragDockObject; X, Y: Integer) of object;
TUnDockEvent = procedure(Sender: TObject; Client: TControl;
NewTarget: TWinControl; var Allow: Boolean) of object;
Как только пользователь нажал кнопку мыши над переносимым компонентом и начал сдвигать его с места, всем потенциальным докам (компонентам, свойство которых Docksite установлено в True) рассылается событие onGetsiteinfo. С ним передаются параметры: кто хочет "приземлиться" (параметр Dockclient) и где (MousePos). В ответ док должен сообщить решение, принимает он компонент (параметр CanDock) и предоставляемый прямоугольник (infiuenceRect) или нет. При помощи этого события можно принимать только определенные элементы управления, как показано в примере:
procedure TForml.Раnе11GеtSitе1nfо(Sender: TObject; DockClient: TControl;
var InfiuenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
begin
if DockClient is TBitBtn then
CanDock := False;
end;
Два последующих события в точности соответствуют своим аналогам из механизма переноса Drag-and-Drop). Событие onDockOver происходит при перемещении перетаскиваемого компонента над доком, OnDockDrop — в момент его отпускания. Наконец, onUnDock сигнализирует об уходе компонента с дока и происходит в момент его "приземления" в другом месте.
Между доком и содержащимися на нем элементами управления есть двусторонняя связь. Все "припаркованные" элементы управления содержатся в векторном свойстве Dockclients, а их количество можно узнать из свойства.
DockClientCount: s: = ' '; for i := 0 to Panell.DockClientCount - 1 do AppendStr(s, Panell.DockClients[i].Name + #0$D#0$A); ShowMessage(s);
С другой стороны, если элемент управления находится на доке, то ссылка на док располагается в свойстве HostDocksite. С ее помощью можно установить, где находится элемент, и даже поменять свойства дока:
procedure TMyForm.ButtonlEndDock(Sender, Target: TObject; X, Y: Integer); begin (Sender as TControl).HostDockSite.SetTextBuf(pChar((Sender as TControl).Name)); end;
Компоненты можно не только переносить с одного дока на другой, но и отпускать в любом месте. Хотя сам по себе компонент TControl и его потомки не являются окнами Windows, но специально для этого случая создается окно-носитель. Свойство FloatingDockSiteClass как раз и определяет класс создаваемого окна. По умолчанию для большинства компонентов значение этого свойства равно TCustomDockForm. Это — форма, которая обладает свойствами дока и создается в момент отпускания элемента управления вне других доков. Внешне она ничем не отличается от обычной стандартной формы. Если вы хотите, чтобы ваша плавающая панель инструментов выглядела по- особенному, нужно породить потомка от класса TCustomDockForm и связать свойство FloatingDockSiteCiass с этим порожденным классом:
TMyCustomFloatingForm = class(TCustomDockForm) public constructor Create(AOwner: TComponent); override; end; constructor TMyCustomFloatingForm.Create(AOwner: TComponent}; begin inherited Create(AOwner); BorderStyle := bsNone; end; procedure TForml.FormCreate(Sender: TObject); begin ToolBarl.FioatingDockSiteCiass := TMyCustomFloatingForm; end;В этом примере решена типовая задача — сделать так, чтобы несущее окно плавающей панели инструментов не содержало заголовка. Внешний вид таких панелей приведен на рис. 27.3.
Переносить компоненты можно не только с помощью мыши, но и программно. Для этого есть пара методов ManualDock и ManualFioat. В приводимом ниже примере нажатие кнопки с именем BitBtnl переносит форму custForm на док MainForm.Paneil и размещает ее по всей доступной площади (параметр выравнивания alclient). Нажатие кнопки BitBtn2 снимает эту форму с дока и выравнивает ее по центру экрана. В свойствах UndockHeight и undockwidth хранятся высота и ширина элемента управления на момент, предшествующий помещению на док:
procedure TMainForm.BitBtnlClick(Sender: TObject);
begin
GustForm.ManualDock(MainForm.Pane11, nil, alClient);
end;
procedure TMainForm.BitBtn2Click(Sender: TObject);
begin
with CustForm do
begin
ManualFloat(Rect((Screen.Width - UndockWidth) div 2,
(Screen.Height - UndockHeight) div 2, (Screen.Width + UndockWidth) div 2,
(Screen.Height + UndockHeight) div 2));
end;
end;
Полное рассмотрение внутреннего устройства механизмов Drag-and-Dock потребовало бы расширения объема этой главы. Тем, кто хочет использовать их на все 100%, рекомендуем обратиться к свойствам useDockManager и DockManager. Последнее представляет собой СОМ-интерфейс, позволяющий расширить возможности дока, вплоть до записи его состояния в поток (класс TStream).
Примеры использования Drag and Drop для различных визуальных компонентов.
Автор: Павел Перетаскивание информации с помощью мыши стало стандартом для программ, работающих в Windows. Часто это бывает удобно и позволяет добиться более быстрой работы. В данной статье я постарался показать максимальное количество примеров использования данной технологии при разработке приложений в среде Delphi. Конечно, результат может быть достигнут различными путями, продемонстрированные приемы не являются единственными и, возможно, не всегда самые оптимальные, но вполне работоспособны, и указывают направление поиска. Надеюсь, что они побудят начинающих программистов к более широкому использованию Drag'n'Drop в своих программах, тем более что пользователи, особенно неопытные, быстро привыкают к перетаскивание и часто его применяют.Проще всего делать Drag из тех компонентов, для которых однозначно ясно, что именно перетаскивать. Для этого устанавливаем у источника DragMode = dmAutomatic, а у приемника пишем обработчики событий OnDragOver - разрешение на прием, и OnDragDrop - действия, производимые при окончании перетаскивания.
procedure TForm1.StringGrid2DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin Accept := Source = Edit1; // разрешено перетаскивание только из Edit1, // при работе программы меняется курсор end; procedure TForm1.StringGrid2DragDrop(Sender, Source: TObject; X, Y: Integer); var ACol, ARow: Integer; begin StringGrid2.MouseToCell(X, Y, ACol, ARow); // находим, над какой ячейкой произвели Drop StringGrid2.Cells[Acol, Arow] := Edit1.Text; // записываем в нее содержимое Edit1 end;
Теперь рассмотрим копирование в упорядоченный список ListBox1 из другого списка. В OnDragOver проверяем, выбран ли хоть один элемент в источнике:
Accept := (Source = ListBox2) and (ListBox2.ItemIndex >= 0);В OnDragDrop ищем отмеченные в источнике строки (установлен множественный выбор) и добавляем только те, которых еще нет в приемнике:
for i := 0 to ListBox2.Items.Count - 1 do
if (ListBox2.Selected[i]) and (ListBox1.Items.IndexOf(ListBox2.Items[i]) < 0) then
ListBox1.Items.Add(ListBox2.Items[i]);
Для ListBox2 реализуем перенос строк из ListBox1 и перестановку элементов в желаемом порядке. В OnDragOver разрешаем Drag из любого ListBox:
Accept := (Source is TListBox) and ((Source as TListBox).ItemIndex >= 0);
А OnDragDrop будет выглядеть так:
var
s: string;
begin
if Source = ListBox1 then
begin
ListBox2.Items.Add(ListBox1.Items[ListBox1.ItemIndex]);
ListBox1.Items.Delete(ListBox1.ItemIndex);
//удаляем перенесенный элемент
end
else //внутренняя перестановка
begin
s := ListBox2.Items[ListBox2.ItemIndex];
ListBox2.Items.Delete(ListBox2.ItemIndex);
ListBox2.Items.Insert(ListBox2.ItemAtPos(Point(X, Y), False), s);
//находим, в какую позицию переносить и вставляем
end;
end;
Научимся переносить текст в Memo, вставляя его в нужное место. Поскольку я выбрал в качестве источника любой из ListBox, подключим в Инспекторе Объектов для OnDragOver уже написанный ранее обработчик ListBox2DragOver, а в OnDragDrop напишем.
if not CheckBox1.Checked then // при включении добавляется в конец текста begin Memo1.SelStart := LoWord(Memo1.Perform(EM_CHARFROMPOS, 0, MakeLParam(X, Y))); // устанавливаем позицию вставки согласно координатам мыши Memo1.SelText := TListBox(Source).Items[TListBox(Source).ItemIndex]; end else memo1.lines.add(TListBox(Source).Items[TListBox(Source).ItemIndex]);
Замечу, что для RichEdit EM_CHARFROMPOS работает несколько иначе, что продемонстрировано в следующем примере. Перенос из Memo реализован с помощью правой кнопки мыши, для того, чтобы не изменять стандартное поведение Memo, и поскольку нажатие левой кнопки снимает выделение. Для Memo1 установлено DragMode = dmManual, а перетаскивание инициируется в OnMouseDown.
if (Button = mbRight) and (Memo1.SelLength > 0) then Memo1.BeginDrag(True);
Обработчик RichEdit1DragOver очевиден, а в RichEdit1DragDrop пишем.
var
p: tpoint;
begin
if not CheckBox1.Checked then
begin
p := point(x, y);
RichEdit1.SelStart := RichEdit1.Perform(EM_CHARFROMPOS, 0, Integer(@P));
RichEdit1.SelText := Memo1.SelText;
end
else
RichEdit1.Lines.Add(Memo1.SelText);
end;
Рассмотрим теперь перетаскивание в ListView1 (ViewStyle = vsReport). В OnDragOver разрешим прием из ListBox2 и из себя же:
Accept := ((Source = ListBox2) and (ListBox2.ItemIndex >= 0)) or (Source = Sender);
А вот OnDragDrop теперь будет посложнее.
var
Item, CurItem: TListItem;
begin
if Source = ListBox2 then
begin
Item := ListView1.DropTarget;
if Item <> nil then
// случай перетаскивания на Caption
if Item.SubItems.Count = 0 then
Item.SubItems.Add(ListBox2.Items[ListBox2.ItemIndex])
// добавляем SubItem, если их еще нет
else
Item.SubItems[0] := ListBox2.Items[ListBox2.ItemIndex]
// иначе заменяем имеющийся SubItem
else
begin
// при перетаскивании на пустое место создаем новый элемент
Item := ListView1.Items.Add;
Item.Caption := ListBox2.Items[ListBox2.ItemIndex];
end;
end
else // случай внутренней перестановки
begin
CurItem := ListView1.Selected;
// запомним выбранный элемент
Item := ListView1.GetItemAt(x, y);
// другой метод определения элемента на который делаем Drop
if Item <> nil then
Item := ListView1.Items.Insert(Item.Index)
// вставляем новый элемент перед найденным
else
Item := ListView1.Items.Add;
// или добавляем новый элемент в конец
Item.Assign(CurItem);
// копируем исходный в новый
CurItem.Free;
// уничтожаем исходный
end;
end;
Для ListView2 установим ViewStyle = vsSmallIcon и покажем, как вручную расставлять значки. В OnDragOver зададим условие.
Accept := (Sender = Source) and ([htOnLabel, htOnItem, htOnIcon] * ListView2.GetHitTestInfoAt(x, y) = []); // пересечение множеств должно быть пустым - запрещаем накладывать элементы
А код в OnDragDrop очень простой:
ListView2.Selected.SetPosition(Point(X, Y));
Перетаскивание в TreeView - довольно любопытная тема, здесь порой приходится разрабатывать алгоритмы обхода ветвей для достижения желаемого поведения. Для TreeView1 разрешим перестановку своих узлов в другое положение. В OnDragOver проверим, не происходит ли перетаскивание узла на свой же дочерний во избежание бесконечной рекурсии:
var
Node, SelNode: TTreeNode;
begin
Node := TreeView1.GetNodeAt(x, y);
// находим узел-приемник
Accept := (Sender = Source) and (Node <> nil);
if not Accept then
Exit;
SelNode := Treeview1.Selected;
while (Node.Parent <> nil) and (Node <> SelNode) do
begin
Node := Node.Parent;
if Node = SelNode then
Accept := False;
end;
Код OnDragDrop выглядит так:
var
Node, SelNode: TTreeNode;
begin
Node := TreeView1.GetNodeAt(X, Y);
if Node = nil then
Exit;
SelNode := TreeView1.Selected;
SelNode.MoveTo(Node, naAddChild);
// все уже встроено в TreeView
end;
Теперь разрешим перенос в TreeView2 из TreeView1.
Accept := (Source = TreeView1) and (TreeView2.GetNodeAt(x, y) <> nil);
И в OnDragDrop копируем выбранную в TreeView1 ветвь во всеми подветвями, для чего придется сделать рекурсивный обход:
var
Node: TTreeNode;
procedure CopyNode(FromNode, ToNode: TTreeNode);
var
TempNode: TTreeNode;
i: integer;
begin
TempNode := TreeView2.Items.AddChild(ToNode, '');
TempNode.Assign(FromNode);
for i := 0 to FromNode.Count - 1 do
CopyNode(FromNode.Item[i], TempNode);
end;
begin
Node := TreeView2.GetNodeAt(X, Y);
if Node = nil then
Exit;
CopyNode(TreeView1.Selected, Node);
end;
Рассмотрим теперь перенос ячеек в StringGrid1. Поскольку, как и в случае с Memo, простое нажатие левой кнопки занято под другие действия, установим DragMode = dmManual и будем запускать Drag при нажатии левой кнопки, удерживая клавиши Alt или Ctrl. Запишем в OnMouseDown:
var
Acol, ARow: Integer;
begin
with StringGrid1 do
if (ssAlt in Shift) or (ssCtrl in Shift) then
begin
MouseToCell(X, Y, Acol, Arow);
if (Acol >= FixedCols) and (Arow >= FixedRows) then
// не будем перетаскивать из фиксированных ячеек
begin
if ssAlt in Shift then
Tag := 1
else if ssCtrl in Shift then
Tag := 2;
// запомним что нажато - Alt или Ctrl - в Tag StringGrid1
BeginDrag(True)
end
else
Tag := 0;
end;
end;
Код OnDragOver учитывает также возможность перетаскивания из StringGrid2 (описание ниже).
var
Acol, ARow: Integer;
begin
with StringGrid1 do
begin
MouseToCell(X, Y, Acol, Arow);
Accept := (Acol >= FixedCols) and (Arow >= FixedRows)
and (((Source = StringGrid1) and (Tag > 0))
or (Source = StringGrid2));
end;
Часть OnDragDrop, относящаяся к внутреннему переносу:
var
ACol, ARow, c, r: Integer;
GR: TGridRect;
begin
StringGrid1.MouseToCell(X, Y, ACol, ARow);
if Source = StringGrid1 then
with StringGrid1 do
begin
Cells[Acol, Arow] := Cells[Col, Row];
//копируем ячейку-источник в приемник
if Tag = 1 then
Cells[Col, Row] := '';
// очищаем источник, если было нажато Alt
Tag := 0;
end;
А вот из StringGrid2 сделаем перенос выбранного диапазона ячеек с помощью правой кнопки, для этого в OnMouseDown.
if Button = mbRight then StringGrid2.BeginDrag(True);
И теперь часть StringGrid1DragDrop, относящаяся к переносу из StringGrid2:
if Source = StringGrid2 then
begin
GR := StringGrid2.Selection;
// Selection - выделенные в StringGrid2 ячейки
for r := 0 to GR.Bottom - GR.Top do
for c := 0 to GR.Right - GR.Left do
if (ACol + c < StringGrid1.ColCount) and
(ARow + r < StringGrid1.RowCount) then
// застра***мся от записи вне StringGrid1
StringGrid1.Cells[ACol + c, ARow + r] :=
StringGrid2.Cells[c + GR.Left, r + GR.Top];
end;
Теперь покажем, как этот диапазон ячеек из StringGrid2 перенести в Memo2. Для этого в OnDragOver Memo2 пишем:
Accept := (Source = StringGrid2) or (Source = DBGrid1);
И в OnDragDrop Memo2:
var
c, r: integer;
s: string;
begin
Memo2.Clear;
if Source = StringGrid2 then
with StringGrid2 do
for r := Selection.Top to Selection.Bottom do
begin
s := '';
for c := Selection.Left to Selection.Right do
s := s + Cells[c, r] + #9;
// разделим ячейки табуляцией
memo2.lines.add(s);
end
Кроме того, в Memo2 можно переносить выбранную запись из DBGrid1, у которого установлено в Options dgRowSelect = True. В сетке отображается таблица из стандартной поставки Delphi DBDEMOS - Animals.dbf. Перетаскивание осуществляется аналогично StringGrid2, правой кнопкой мыши, только по событию OnMouseMove.
if ssRight in Shift then DBGrid1.BeginDrag(true);
Код в Memo2DragDrop, относящийся к переносу из DBGrid1:
else
with DBGrid1.DataSource.DataSet do
begin
s := '';
for c := 0 to FieldCount - 1 do
s := s + Fields[c].AsString + ' | ';
memo2.lines.add(s);
end;
// в случае dgRowSelect = False для переноса одного поля достаточно сделать
// memo2.lines.add(DbGrid1.SelectedField.AsString);
Drag из DBGrid1 принимается также на Panel3, условие приема очевидно, а OnDragDrop выглядит так:
Panel3.Height := 300; // раскрываем панель
Image1.visible := True;
OleContainer1.Visible := false;
Image1.Picture.Assign(DBGrid1.DataSource.DataSet.FieldByName('BMP'));
// показываем графическое поле текущей записи таблицы
Теперь покажу, как можно передвигать мышью визуальные компоненты в Run-Time. Для Panel1 установим DragMode = dmAutomatic, в OnDragOver формы пишем:
var Ct: TControl; begin Ct := ControlAtPos(Point(X + Panel1.Width, Y + Panel1.Height), True, True); // для упрощения проверяем перекрытие с другими контролами только правого нижнего угла Accept := (Source = Panel1) and ((Ct = nil) or (Ct = Panel1));
И в OnDragDrop формы очень просто.
Panel1.Left := X; Panel1.Top := Y;
Другой метод перетаскивания можно встретить в каждом FAQ по Delphi:
procedure TForm1.Panel2MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); const SC_DragMove = $F012; begin ReleaseCapture; Panel2.Perform(WM_SysCommand, SC_DragMove, 0); end;
И в завершение реализация популярной задачи перетаскивания значков файлов на форму из Проводника. Для этого следует описать обработчик сообщения WM_DROPFILES.
private procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;В OnCreate формы разрешить прием файлов
DragAcceptFiles(Handle, true);
И в OnDestroy отключить его.
DragAcceptFiles(Handle, False);
Процедура обработки приема файлов может выглядеть так:
procedure TForm1.WMDropFiles(var Msg: TWMDropFiles);
const
maxlen = 254;
var
h: THandle;
//i,num:integer;
pchr: array[0..maxlen] of char;
fname: string;
begin
h := Msg.Drop;
// дана реализация для одного файла, а
//если предполагается принимать группу файлов, то можно добавить:
//num:=DragQueryFile(h,Dword(-1),nil,0);
//for i:=0 to num-1 do begin
// DragQueryFile(h,i,pchr,maxlen);
//...обработка каждого
//end;
DragQueryFile(h, 0, pchr, maxlen);
fname := string(pchr);
if lowercase(extractfileext(fname)) = '.bmp' then
begin
Image1.visible := True;
OleContainer1.Visible := false;
image1.Picture.LoadFromFile(fname);
Panel3.Height := 300;
end
else if lowercase(extractfileext(fname)) = '.doc' then
begin
Image1.visible := False;
OleContainer1.Visible := True;
OleContainer1.CreateObjectFromFile(fname, false);
Panel3.Height := 300;
end
else if lowercase(extractfileext(fname)) = '.htm' then
ShellExecute(0, nil, pchr, nil, nil, 0)
else if lowercase(extractfileext(fname)) = '.txt' then
Memo2.Lines.LoadFromFile(fname)
else
Memo2.Lines.Add(fname);
DragFinish(h);
end;
При перетаскивании на форму файла с расширением Bmp он отображается в Image1, находящемся на Panel3, Doc загружается в OleContainer, для Htm запускается Internet Explorer или другой браузер по умолчанию, Txt отображается в Memo2, а для остальных файлов в Memo2 будет просто показано имя.
Полагаю, на основе содержащихся в статье приемов будет нетрудно организовать перетаскивание и для других, не описанных здесь, визуальных компонентов.
Drag and Drop из TScrollBox.
Вы можете написать общую функцию для отдельного TImage, и назначать этот метод для каждого динамически создаваемого TImage, примерно так:
procedure TForm1.GenericMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
TImage(Sender).BeginDrag(False);
{что-то другое, что вы хотели бы сделать}
end;
{....}
UmpteenthDynImage := TImage.Create(dummyImage);
UmpteenthDynImage.MouseDown := TForm1.GenericMouseDown;
|
Это должно быть синтаксически закрытым. Вы можете просто назначать каждый динамический объект методу GenericMouseDown, и они все им будут пользоваться. Предок dummyImage позволяет легко разрушать все динамические объекты обычным деструктором dummyImage.
Интерфейс переноса Drag-and-Drop.
Интерфейс переноса и приема компонентов появился достаточно давно. Он обеспечивает взаимодействие двух элементов управления во время выполнения приложения. При этом могут выполняться любые необходимые операции. Несмотря на простоту реализации и давность разработки, многие программисты (особенно новички) считают этот механизм малопонятным и экзотическим. Тем не менее использование Drag-and-Drop может оказаться очень полезным и простым в реализации. Сейчас мы в этом убедимся. Для того чтобы механизм заработал, требуется настроить соответствующим образом два элемента управления. Один должен быть источником (Source), второй — приемником (Target). При этом источник никуда не перемещается, а только регистрируется в качестве такового в механизме. Примечание: Один элемент управления может быть одновременно источником и приемником. Пользователь помещает указатель мыши на нужный элемент управления, нажимает левую кнопку мыши и, не отпуская ее, начинает перемещать курсор ко второму элементу. При достижении этого элемента пользователь отпускает кнопку мыши. В этот момент выполняются предусмотренные разработчиком действия. При этом первый элемент управления является источником, а второй — приемником. После выполнения настройки механизм включается и реагирует на перетаскивание мышью компонента-источника в приемник. Группа методов-обработчиков обеспечивает контроль всего процесса и служит для хранения исходного кода, который разработчик сочтет нужным связать с перетаскиванием. Это может быть передача текста, значений свойств (из одного редактора в другой можно передать настройки интерфейса, шрифта и сам текст); перенос файлов и изображений; простое перемещение элемента управления с места на место и т. д. Пример реализации Drag-and-Drop в Windows — возможность переноса файлов и папок между дисками и папками. Как видите, можно придумать множество областей применения механизма Drag-and-Drop. Его универсальность объясняется тем, что это всего лишь средство связывания двух компонентов при помощи указателя мыши. А конкретное наполнение зависит только от фантазии программиста и поставленных задач. Весь механизм Drag-and-Drop реализован в базовом классе TControl, который является предком всех элементов управления. Рассмотрим суть механизма.Любой элемент управления из Палитры компонентов Delphi является источником в механизме Drag-and-Drop. Его поведение на начальном этапе переноса зависит от значения свойства.
type TDragMode = (dmManual, dmAutomatic); property DragMode: TDragMode;Значение dmAutomatic обеспечивает автоматическую реакцию компонента на нажатие левой кнопки мыши и начало перетаскивания — при этом механизм включается самостоятельно.
Значение dmManual (установлено по умолчанию) требует от разработчика обеспечить включение механизма вручную. Этот режим используется в том случае, если компонент должен реагировать на нажатие левой кнопки мыши как-то иначе. Для инициализации переноса используется метод.
procedure BeginDrag(Immediate: Boolean; Threshold: Integer = -1);Параметр immediate = True обеспечивает немедленный старт механизма. При значении False механизм включается только при перемещении курсора на расстояние, определенное параметром Threshold.
О включении механизма сигнализирует указатель мыши — он изменяется на курсор, определенный в свойстве.
property DragCursor: TCursor;Еще раз напомним, что источник при перемещении курсора не изменяет собственного положения, и только в случае успешного завершения переноса сможет взаимодействовать с приемником.
Приемником может стать любой компонент, в котором создан метод-обработчик.
procedure DragOver(Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);Он вызывается при перемещении курсора в режиме Drag-and-Drop над этим компонентом. В методе-обработчике можно предусмотреть селекцию источников переноса по нужным атрибутам.
Если параметр Accept получает значение True, то данный компонент становится приемником. Источник переноса определяется параметром source. Через этот параметр разработчик получает доступ к свойствам и методам источника. Текущее положение курсора задают параметры X и Y. Параметр state возвращает информацию о характере движения мыши:
type TDragState = (dsDragEnter, dsDragLeave, dsDragMove);dsDragEnter — указатель появился над компонентом; dsDragLeave — указатель покинул компонент; dsDragMove — указатель перемещается по компоненту.
Приемник должен предусматривать выполнение некоторых действий в случае, если источник завершит перенос именно на нем. Для этого используется метод-обработчик.
type TDragDropEvent = procedure(Sender, Source: TObject;
X, Y: Integer) of object;
property OnDragDrop: TDragDropEvent;
который вызывается при отпускании левой кнопки мыши на компоненте-приемнике. Доступ к источнику и приемнику обеспечивают параметры Source и Sender соответственно. Координаты мыши возвращают параметры X и Y.
При завершении переноса элемент управления — источник — получает соответствующее сообщение, которое обрабатывается методом.
type TEndDragEvent = procedure(Sender, Target: TObject;
X, Y: Integer) of object;
property OnEndDrag: TEndDragEvent;
Источник и приемник определяются параметрами Sender и Target соответственно. Координаты мыши определяются параметрами X и Y.
Для программной остановки переноса можно использовать метод EndDrag источника (при обычном завершении операции пользователем он не используется):
procedure EndDrag(Drop: Boolean);Параметр Drop = True завершает перенос. Значение False прерывает перенос. Теперь настало время закрепить полученные знания на практике. Рассмотрим небольшой пример. В проекте DemoDragDrop на основе механизма Drag-and-Drop реализована передача текста между текстовыми редакторами и перемещение панелей по форме (рис. 27.1).
Листинг 27.1. Секция implementation модуля главной формы проекта DemoDragDrop.
implementation
{$R *.DFM}
procedure TMainForm.EditlMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, У: Integer);
begin
if Button = mbLeft then
TEdit(Sender).BeginDrag(True);
end;
procedure TMainForm.Edit2DragOver(Sender, Source: TObject;
X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
if Source is TEdit then
Accept := True
else
Accept := False;
end;
procedure TMainForm.Edit2DragDrop(Sender, Source: TObject;
X, Y: Integer);
begin
TEdit(Sender).Text := TEdit(Source).Text;
TEdit(Sender).SetFocus;
TEdit(Sender).SelectAll;
end;
procedure TMainForm.EditlEndDrag(Sender, Target: TObject;
X, Y: Integer);
begin
if Assigned(Target) then
TEdit(Sender).Text := 'Текст перенесен в ' + TEdit(Target).Name;
end;
procedure TMainForm.FormDragOver(Sender, Source: TObject;
X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
if Source.ClassName = 'TPanel' then
Accept := True
else
Accept := False;
end;
procedure TMainForm.FormDragDrop(Sender, Source: TObject;
X, Y: Integer);
begin
TPanel(Source).Left := X;
TPanel(Source).Top := Y;
end;
end.
Для однострочного редактора Edit1 определены методы-обработчики источника. В методе EditiMouseDown обрабатывается нажатие левой кнопки мыши
и включается механизм переноса. Так как свойство DragMode для Edit1 имеет значение dmManual, то компонент без проблем обеспечивает получение фокуса и редактирование текста.
Метод EditiEndDrag обеспечивает отображение информации о выполнении переноса в источнике.
Для компонента Edit2 определены методы-обработчики приемника. Метод Edit2DragOver проверяет класс источника и разрешает или запрещает прием.
Метод Edit2DragDrop осуществляет перенос текста из источника в приемник.
Примечание
Обратите внимание, что оба компонента TEdit одновременно являются источниками и приемниками. Для этого каждый из них использует методы-обработчики другого. А исходный код методов настроен на обработку владельца как экземпляра класса TEdit.
Форма, как приемник Drag-and-Drop, обеспечивает перемещение панели Panel2, которая выступает в роли источника. Метод FormDragOver запрещает прием любых компонентов, кроме панелей. Метод FormDragDrop осуществляет перемещение компонента.
Панель не имеет своих методов-обработчиков, т. к. работает в режиме dmAutomatic и не нуждается в дополнительной обработке завершения переноса.
Drag and Drop для TListBox на примере двойного списка.
Автор: Александр Малыгин Специально для Королевства Delphi Типичная задача перетаскивания мышью объектов из одного контрола в другой просто решается обработкой событий OnDragOver и OnDragDrop, при установленных свойствах DragMode := dmAutomatic и DragKind := dkDrag у всех участвующих компонентов. Первый обработчик предназначен для принятия решения - допускается ли сбросить объект в контрол, над которым находится мышь (параметр Sender), и выставить соответствующий курсор. Для этого передается параметр Source:TObject, представляющий собой тот компонент, с которого начали перетаскивание (источник), координаты курсора X,Y:integer, состояние процесса перетаскивания State:TDragState, и результат, который надо вернуть var Accept:boolean. Второй обработчик вызывается, когда пользователь отпускает кнопку мыши, и контрол Sender:TObject должен отработать прием переданной ему "посылки" Source:TObject, по координатам X,Y : integer. Предлагаемый пример DualListFrame реализует операции drag'n'drop над двумя списками TListBox, организованными в один фрейм с органами управления. Прототипом послужил стандартный шаблон-форма "Dual list box" из репозитория Delphi и аналогичный компонент-диалог из библиотеки RxLib. Первый не снабжен функциями drag'n'drop, а второй хоть и умеет это, но все равно является отдельным диалогом, что не всегда удобно. Данный пример благодаря технологии фреймов позволяет встроить двойной список "со всеми причиндалами" в любую форму проекта. Чтобы им воспользоваться, достаточно подключить модуль DualListFrame.pas к проекту. Для удобства при многократном использовании (в разных проектах) можно добавить его в репозиторий, скопировав предварительно в общую для шаблонов директорию (например, "Delphi5\ObjRepos"). В архиве вместе с кодом и ресурсом фрейма имеется пиктограмма для репозитория и вырезка из файла "Delphi5\Bin\delphi32.dro".Ниже приведены фрагменты исходного текста, ключевые для реализации технологии "тащи и бросай".
procedure TfrDualList.ListDragOver(Sender, Source: TObject; X,
Y: Integer; State: TDragState; var Accept: Boolean);
var
DragIndex: integer;
begin
// из другого листбокса принимаем всегда
if Source <> Sender then
Accept := true
// а если это мы сами - надо проверить возможность изменения порядка
// в списке и позицию сбрасывания
else
with (Sender as TListBox) do
begin
Accept := False;
if not Sorted and ((SelCount = 1) or (not MultiSelect)) then
begin
DragIndex := ItemAtPos(Point(X, Y), True);
if (DragIndex >= 0) and (DragIndex <> ItemIndex) then
Accept := True; // попали внутрь видимого списка
end;
end;
// установка изображения курсора
if State = dsDragLeave then
(Source as TListBox).DragCursor := crDrag;
if (State = dsDragEnter) and ((Source as TListBox).SelCount > 1) then
(Source as TListBox).DragCursor := crMultiDrag;
end;
procedure TfrDualList.ListDragDrop(Sender, Source: TObject; X, Y: Integer);
begin
if Source <> Sender then // перемещаем элементы из другого листбокса
MoveItems(TListBox(Source), TListBox(Sender))
else
BoxMoveSel(TListBox(Sender), // перемещаем элемент внутри списка
TListBox(Sender).ItemAtPos(Point(X, Y), True));
end;
|
Drag and Drop из RichEdit.
| Разговор двух программистов: - Ты слышал, Костя умер. - А он записался?!! |
var
Form1: TForm1;
richcopy: string;
transfering: boolean;
implementation
{$R *.DFM\}
procedure TForm1.RichEdit1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if length(richedit1.seltext)>0 then begin
richcopy:=richedit1.seltext;
transfering:=true;
end; //seltext
end;
procedure TForm1.ListBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if transfering then begin
transfering:=false;
listbox1.items.add(richcopy);
end; //transfering
end;
|
Drag and Drop TImage.
Вот рабочий пример. Расположите на форме панель побольше, скопируйте и измените приведенный код так, чтобы изображение загружалось из ВАШЕГО каталога Delphi.
procedure TForm1.Panel1DragDrop(Sender, Source: TObject; X, Y: Integer);
begin
with Source as TImage do
begin
Left := X;
Top := Y;
end;
end;
procedure TForm1.Panel1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := Source is TImage;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
with TImage.Create(Self) do
begin
Parent := Panel1;
AutoSize := True;
Picture.LoadFromFile('D:\DELPHI\IMAGES\CHIP.BMP');
DragMode := dmAutomatic;
OnDragOver := Panel1DragOver;
OnDragDrop := Panel1DragDrop;
end;
end;
|
Drag and Drop несколько элементов в TListView.
{ ListView1.DragMode := dmAutomatic }
procedure TForm1.ListView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
DragItem, DropItem, CurrentItem, NextItem: TListItem;
begin
if Sender = Source then
with TListView(Sender) do
begin
DropItem := GetItemAt(X, Y);
CurrentItem := Selected;
while CurrentItem <> nil do
begin
NextItem := GetNextItem(CurrentItem, SdAll, [IsSelected]);
if DropItem = nil then DragItem := Items.Add
else
DragItem := Items.Insert(DropItem.Index);
DragItem.Assign(CurrentItem);
CurrentItem.Free;
CurrentItem := NextItem;
end;
end;
end;
procedure TForm1.ListView1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState;
var Accept: Boolean);
begin
Accept := Sender = ListView1;
end;
|
Пример Drag and Drop между двумя DBGrid.
Данный пример компонента и демонстрационный проект показывают простой путь осуществления операции "drag and drop" (перетащи и брось) между двумя полями различных табличных сеток. Запустите Delphi 3 (с незначительными изменениями данный код может работать и в Delphi 1-2). Активизируйте File|New|Unit. Скопируйте приведенный ниже модуль MyDBGrid во вновь созданный модуль. Сделайте File|Save As. Сохраните модуль как MyDBGrid.pas. Выберите пункт меню Component|Install Component. Переключитесь на страницу Info New Package. Поместите MyDBGrid.pas в поле редактирования "Unit file name" (имя файла модуля). Назовите модуль MyPackage.dpk. Ответьте Yes на вопрос Delphi 3 о необходимости сборки и установки пакета. Нажмите OK на сообщение Delphi 3 о необходимости включения VCL30.DPL. После этого пакет будет собран и установлен. Теперь компонент TMyDBGrid будет отображен в Палитре Компонентов в группе "Samples". Закройте редактор пакетов и сохраните пакет. Выберите пункт меню File|New Application. Щелкните правой кнопкой мыши на форме (Form1) и выберите View As Text. Скопируйте приведенный ниже исходный код формы GridU1 в Form1. Щелкните правой кнопкой мыши на форме и выберите View As Form. Убедитесь в активности ваших таблиц. Скопируйте расположенный ниже модуль GridU1 в ваш модуль Unit1. Выберите пункт меню File|Save Project As. Сохраните модуль как GridU1.pas. Сохраните проект как GridProj.dpr. Теперь запустите проект и наслаждайтесь функцией Drag and Drop между двумя табличными сетками.
// Модуль MyDBGrid
unit MyDBGrid;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids;
type
TMyDBGrid = class(TDBGrid)
private
{ Private declarations }
FOnMouseDown: TMouseEvent;
protected
{ Protected declarations }
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
published
{ Published declarations }
property Row;
property OnMouseDown read FOnMouseDown write FOnMouseDown;
end;
procedure Register;
implementation
procedure TMyDBGrid.MouseDown(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Assigned(FOnMouseDown) then
FOnMouseDown(Self, Button, Shift, X, Y);
inherited MouseDown(Button, Shift, X, Y);
end;
procedure Register;
begin
RegisterComponents('Samples', [TMyDBGrid]);
end;
end.
// Модуль GridU1
unit GridU1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, Db, DBTables, Grids, DBGrids, MyDBGrid, StdCtrls;
type
TForm1 = class(TForm)
MyDBGrid1: TMyDBGrid;
Table1: TTable;
DataSource1: TDataSource;
Table2: TTable;
DataSource2: TDataSource;
MyDBGrid2: TMyDBGrid;
procedure MyDBGrid1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure MyDBGrid1DragOver(Sender, Source: TObject;
X, Y: Integer; State: TDragState; var Accept: Boolean);
procedure MyDBGrid1DragDrop(Sender, Source: TObject;
X, Y: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
var
SGC: TGridCoord;
procedure TForm1.MyDBGrid1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
DG: TMyDBGrid;
begin
DG := Sender as TMyDBGrid;
SGC := DG.MouseCoord(X, Y);
if (SGC.X > 0) and (SGC.Y > 0) then
(Sender as TMyDBGrid).BeginDrag(False);
end;
procedure TForm1.MyDBGrid1DragOver(Sender, Source: TObject;
X, Y: Integer; State: TDragState; var Accept: Boolean);
var
GC: TGridCoord;
begin
GC := (Sender as TMyDBGrid).MouseCoord(X, Y);
Accept := Source is TMyDBGrid and (GC.X > 0) and (GC.Y > 0);
end;
procedure TForm1.MyDBGrid1DragDrop(Sender, Source: TObject;
X, Y: Integer);
var
DG: TMyDBGrid;
GC: TGridCoord;
CurRow: Integer;
begin
DG := Sender as TMyDBGrid;
GC := DG.MouseCoord(X, Y);
with DG.DataSource.DataSet do
begin
with (Source as TMyDBGrid).DataSource.DataSet do
Caption := 'Вы перетащили "' + Fields[SGC.X - 1].AsString + '"';
DisableControls;
CurRow := DG.Row;
MoveBy(GC.Y - CurRow);
Caption := Caption + ' в "' + Fields[GC.X - 1].AsString + '"';
MoveBy(CurRow - GC.Y);
EnableControls;
end;
end;
end.
// Форма GridU1
object Form1: TForm1
Left = 200
Top = 108
Width = 544
Height = 437
Caption = 'Form1'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
PixelsPerInch = 96
TextHeight = 13
object MyDBGrid1: TMyDBGrid
Left = 8
Top = 8
Width = 521
Height = 193
DataSource = DataSource1
Row = 1
TabOrder = 0
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'MS Sans Serif'
TitleFont.Style = []
OnDragDrop = MyDBGrid1DragDrop
OnDragOver = MyDBGrid1DragOver
OnMouseDown = MyDBGrid1MouseDown
end
object MyDBGrid2: TMyDBGrid
Left = 7
Top = 208
Width = 521
Height = 193
DataSource = DataSource2
Row = 1
TabOrder = 1
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'MS Sans Serif'
TitleFont.Style = []
OnDragDrop = MyDBGrid1DragDrop
OnDragOver = MyDBGrid1DragOver
OnMouseDown = MyDBGrid1MouseDown
end
object Table1: TTable
Active = True
DatabaseName = 'DBDEMOS'
TableName = 'ORDERS'
Left = 104
Top = 48
end
object DataSource1: TDataSource
DataSet = Table1
Left = 136
Top = 48
end
object Table2: TTable
Active = True
DatabaseName = 'DBDEMOS'
TableName = 'CUSTOMER'
Left = 104
Top = 240
end
object DataSource2: TDataSource
DataSet = Table2
Left = 136
Top = 240
end
end
|
Drag and Drop - как использовать ItemAtPos для получения элемента DirListBox.
Просто сохраните результат функции ItematPos в переменной формы, и затем используйте эту переменную в обработчике ListBoxDragDrop. Пример:
FDragItem := ItematPos(X, Y, True);
if FDragItem >= 0 then
BeginDrag(false);
...
procedure TForm1.ListBoxDragDrop(Sender, Source: TObject; X, Y: Integer);
begin
if Source is TDirectoryListBox then
ListBox.Items.Add(TDirectoryListBox(Source).GetItemPath(FDragItem));
end;
|
Drag and Drop со списками.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
This example shows how to drag&drop within a TListBox.
The Demo Program also shows how to implement an autoscroll-feature.
Dieses Beispiel zeigt, wie man drag & drop in einer TListBox realisiert.
Im Demo-Programm wird auch eine Autoscroll-Funktion gezeigt.
}
procedure TForm1.ListBox1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := Sender is TListBox;
end;
procedure TForm1.ListBox1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
iTemp: Integer;
ptTemp: TPoint;
szTemp: string;
begin
{ change the x,y coordinates into a TPoint record }
ptTemp.x := x;
ptTemp.y := y;
{ Use a while loop instead of a for loop due to items possible being removed
from listboxes this prevents an out of bounds exception }
iTemp := 0;
while iTemp <= TListBox(Source).Items.Count-1 do
begin
{ look for the selected items as these are the ones we wish to move }
if TListBox(Source).selected[iTemp] then
begin
{ use a with as to make code easier to read }
with Sender as TListBox do
begin
{ need to use a temporary variable as when the item is deleted the
indexing will change }
szTemp := TListBox(Source).Items[iTemp];
{ delete the item that is being dragged }
TListBox(Source).Items.Delete(iTemp);
{ insert the item into the correct position in the listbox that it
was dropped on }
Items.Insert(itemAtPos(ptTemp, True), szTemp);
end;
end;
Inc(iTemp);
end;
end;
Как получить список файлов из Проводника.
Автор: NomadicРазвлекался когда-то - вот, осталось:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ShellAPI, Grids, StdCtrls;
type
TForm1 = class(TForm)
lb: TListBox;
Memo1: TMemo;
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
procedure WMDropFiles(var M: TMessage); message WM_DROPFILES;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
var
CountFiles: integer;
SizeName: integer;
cch: integer;
var
hDrop: integer;
Point: TPoint;
lpszFile: PChar;
{$R *.DFM}
procedure TForm1.WMDropFiles(var M: TMessage);
var
i: integer;
begin
hDrop := M.WParam;
DragQueryPoint(hDrop, Point);
CountFiles := DragQueryFile(hDrop, $FFFFFFFF, nil, cch);
for i := 0 to CountFiles - 1 do
begin
SizeName := DragQueryFile(hDrop, i, nil, cch);
GetMem(lpszFile, SizeName + 1);
DragQueryFile(hDrop, i, lpszFile, SizeName + 1);
lb.Items.Add(lpszFile);
FreeMem(lpszFile, SizeName + 1);
end;
DragFinish(hDrop);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DragAcceptFiles(Handle, True);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
lb.Items.Clear;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ShellAbout(Handle, 'Anton Saburov', 'APSystems', 0);
end;
end.
|
Перемещение из DbGrid.
| Что говорят мыши: Мышь квакера: «Слушай, кореш, давай ещё разок колесо, я кайф недоловила» Мышь фотошопера: “Так, вот в эту точку? Сейчас, блин, с тобой косоглазой станешь, да, вижу, вижу, что опять не то выделила? Да иди ты на хр@н, это у тебя руки дрожат!” Мышь програмиста: “Я, это, посплю децл, ок?” Мышь любителя DОSа: “It’s no fate” |
Скопируйте код из данного "Совета", сохраните его с именем DBGrid.pas и установите компонент в палитру. У Вас появится новый компонент EDBGrid с двумя новыми событиями: OnMouseDown и OnMouseUp. Я не считаю эту информацию конфиденциальной: это ошибка разработчиков Delphi! На самом деле эти два события должны быть частью компонента DBGrid.
unit Dbgrid;
interface
uses
DBGrids, Controls, Classes;
type
TEDBGrid = class(TDBGrid)
private
FOnMouseDown: TMouseEvent;
FOnMouseUp: TMouseEvent;
protected
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y:
Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y:
Integer); override;
published
property OnMouseDown: TMouseEvent read FOnMouseDown write
FOnMouseDown;
property OnMouseUp: TMouseEvent read FOnMouseUp write FOnMouseUp;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Data Controls', [TEDBGrid]);
end;
procedure TEDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
if Assigned(FOnMouseDown) then
FOnMouseDown(Self, Button, Shift, X, Y);
inherited MouseDown(Button, Shift, X, Y);
end;
procedure TEDBGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
if Assigned(FOnMouseUp) then
FOnMouseUp(Self, Button, Shift, X, Y);
inherited MouseUp(Button, Shift, X, Y);
end;
end.
|
Drag and Drop c Win95 Explorer.
| Крошка сын к отцу пришел И сказала кроха: Navigator - хорошо, а Explorer - плохо! |
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
ComCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure FileIsDropped(var Msg: TMessage); message WM_DropFiles;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
shellapi;
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
DragAcceptFiles(Handle, True);
end;
procedure TForm1.FileIsDropped(var Msg: TMessage);
var
hDrop: THandle;
fName: array[0..254] of CHAR;
NumberOfFiles: INTEGER;
fCounter: INTEGER;
Names: string;
begin
hDrop := Msg.WParam;
NumberOfFiles := DragQueryFile(hDrop, -1, fName, 254);
Names := '';
for fCounter := 1 to NumberOfFiles do
begin
DragQueryFile(hDrop, fCounter, fName, 254);
// Здесь вы получаете один к одному имя вашего файла
Names := Names + #13#10 + fName;
end;
ShowMessage('Бросаем ' + IntToStr(NumberOfFiles) + ' файла(ов) : ' + Names);
DragFinish(hDrop);
end;
end.
|
Drag and Drop c Win95 Explorer 2.
Автор: Aleksey.
{Так можно заставить окно принимать файлы, перетаскиваемые из проводника}
{ОБЯЗАТЕЛЬНО ПОМЕСТИТЕ В СЕКЦИЮ PRIVATE СТРОКИ
procedure CreateParams(var Params: TCreateParams); override;
procedure WMDropFiles(var Message: TWMDropFiles); message WM_DROPFILES;
и не забудьте - ShellAPI поместить в uses}
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited
CreateParams(Params);
{сделаем окно способным принимать файлы}
Params.ExStyle := Params.ExStyle or WS_EX_ACCEPTFILES;
end;
procedure TForm1.WMDropFiles(var Message: TWMDropFiles);
var
aFile: array[0..255] of Char;
//FilesCount : Integer;
begin
inherited;
{так можно узнать сколько файлов перетягивается}
// FilesCount := DragQueryFile(Message.drop, $FFFFFFFF, nil, 0);
begin
{здесь можно поставить цикл открытия всех перетаскиваемых файлов
for N := 0 to FilesCount - 1 do DragQueryFile(Message.drop, N, aFile, 256);
а в данном случае открывается только первый файл в списке}
DragQueryFile(Message.drop, 0, aFile, 256);
Memo1.Lines.LoadFromFile(aFile);
end;
DragFinish(Message.Drop);
end;
procedure TForm1.FormCreate(Sender: TObject); {Form1.OnCreate}
begin
{сделаем окно неравнодушным к пролетающим над ним файлам}
DragAcceptFiles(Handle, True);
end;
|
Как перетаскивать (Drag and Drop) выделенный текст между компонентами Memo.
Данный способ позволяет не погружаясь глубоко в создание компонент осуществить операцию "drag and drop" выделенного текста.
Создайте новый компонент (TMyMemo), наследовав его от TMemo. И объявите его следующим образом:
type
TMyMemo = class(TMemo)
private
FLastSelStart : Integer;
FLastSelLength : Integer;
procedure WMLButtonDown(var message: TWMLButtonDown); message WM_LBUTTONDOWN;
published
property LastSelStart : Integer read FLastSelStart write FLastSelStart;
property LastSelLength : Integer read FLastSelLength write FLastSelLength;
end;
|
Добавьте обработчик WMLButtonDown:
procedure TMyMemo.WMLButtonDown(var message: TWMLButtonDown);
var
Ch: Integer;
begin
if SelLength > 0 then
begin
Ch := LoWord(Perform(EM_CHARFROMPOS, 0,
MakeLParam(message.XPos, message.YPos)));
LastSelStart := SelStart;
LastSelLength := SelLength;
if (Ch >= SelStart) and (Ch then
BeginDrag(True)
else
inherited;
end
else
inherited;
end;
|
Теперь установите этот компонент в package, создайте новый проект в Delphi и поместите на форму два TMyMemo. Для обоих компонент необходимо создать обработчики событий OnDragOver, которые должны выглядеть следующим образом:
procedure TForm1.MyMemo1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin Accept := Source is TMyMemo; end; |
Так же для них необходимо сделать обработчики событий OnDragDrop:
procedure TForm1.MyMemo1DragDrop(Sender, Source: TObject;
X, Y: Integer);
var
Dst, Src : TMyMemo;
Ch : Integer;
Temp : string;
begin
Dst := Sender as TMyMemo;
Src := Source as TMyMemo;
Ch := LoWord(Dst.Perform(EM_CHARFROMPOS,0,MakeLParam(X,Y)));
if (Src = Dst) and (Ch >= Src.LastSelStart) and
(Ch then
Exit;
Dst.Text := Copy(Dst.Text,1,Ch)+Src.SelText+
Copy(Dst.Text,Ch+1,Length(Dst.Text)-Ch);
Temp := Src.Text;
Delete(Temp,Src.LastSelStart+1,Src.LastSelLength);
Src.Text := Temp;
end;
|
Запустите приложение, поместите в поля memo какой-нибудь текст, и посмотрите что произойдёт, если перетащить текст между полями.
Drag and Drop между двумя компонентами ListBox.

Вот пересмотренный OnDragDrop, использующий source (источник) и sender (передатчик) вместо DstList и SrcList. Теперь, если вы установили SrcList и DstList для использования тех же методов OnDragOver и OnDragDrop и создали обработчик события OnDragDrop, то для операции Drag and Drop вы можете использовать оба решения.
procedure TDualListDlg.DstListDragDrop(Sender, Source: TObject; X,
Y: Integer);
var
droppedOnIndex: integer;
anItem: integer;
numberOfItems: integer;
begin
if (Sender is TListbox) and (Source is TListBox) then
begin
droppedOnIndex := TListBox(Sender).ItemAtPos(Point(X, Y), false);
numberOfItems := TListBox(Source).SelCount;
anItem := 0;
while numberOfItems > 0 do
begin
if TListBox(Source).Selected[anItem] = true then
begin
TListBox(Sender).Items.Insert(droppedOnIndex,
TListBox(Source).Items[anItem]);
TListBox(Source).Items.Delete(anItem);
TListBox(Source).Update;
TListBox(Sender).Update;
numberOfItems := numberOfItems - 1;
end
else
anItem := anItem + 1;
end;
end;
end;
|
Для того, чтобы предотвратить операцию Drag and Drop с одним и тем же компонентом, используйте следующий код в обработчике события OnDragOver:
if (Sender is TListBox) and (Source is TListBox) then
begin
if TListBox(Sender).Name = TListBox(Source).Name then
Accept := False
else
Accept := true;
end;
|
Перетаскивание элементов управления c рамкой контура.
Автор: NEil ...как перетаскивать элементы управления с контурной рамкой по их форме, "приклеенной" к курсору? Решение, найденное вами, работать не будет, поскольку таскаемая рамка не обязательно может находиться в пределах области компонента (а вы отрисовываете ее только на компоненте). В общих чертах, вы должны рисовать на всей поверхности формы и даже рабочего стола, для чего необходимо сделать растровую КОПИЮ окна или десктопа и рисовать на ней. Вот что нам нужно.Начните со свеженькой формы. Бросьте на нее компонент Notebook и установите его свойство Align в alClient. Разработайте форму на первой странице компонента Notebook. Создайте вторую страницу в Notebook, поместите туда Paintbox и установите его свойство Align в alClient. Далее добавьте нижеследующие строчки в секцию Private вашей формы:
Img : TBitmap; DragX, DragY, DragW, DragH, XOff, YOff : Integer; |
В обработчике формы OnCreate:
Img := TBitmap.Create; |
В общем, для всех перетаскиваемых компонентов, обработчике события OnMouseDown:
IF NOT (ssShift IN Shift) THEN Exit; Img := GetFormImage; Notebook1.PageIndex := 1; WITH Sender AS TControl DO BEGIN DragW := Width; DragH := Height; XOff:= X; YOff := Y; BeginDrag(True); END; |
В общем, для всех перетаскиваемых компонентов, обработчике события EndDrag:
Notebook1.PageIndex := 0; WITH Sender AS Tcontrol DO BEGIN Left := X-Xoff; Top := Y-YOff; END; |
Поместите следующую строку в обработчик события OnPaint компонента PaintBox:
PaintBox1.Canvas.Draw(0, 0, Img); |
И наконец, если вам еще это не надоело, поместите следующую строчку в обработчик OnDragOver компонента PaintBox:
IF (X=DragX) AND (Y=DragY) THEN Exit; WITH PaintBox1.Canvas DO BEGIN DrawFocusRect(Bounds(DragX-XOff, DragY-YOff, DragW, DragH); DragX := X; DragY := Y; DrawFocusRect(Bounds(DragX-XOff, DragY-YOff, DragW, DragH); END; |
Начнем с самого начала. Причина того, что вы получаете курсор "crNoDrop" в том, что под курсором элемент управления не готов принять перетаскиваемый компонент. Чтобы исправить эту ситуацию, дважды щелкните (в Инспекторе Объектов) на событии формы или компонента OnDragOver и установите параметр Accept в, например так:
procedure TForm1.FormDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin Accept := true ; end; |
Вы можете получить координаты в методе OnDragOver при сравнении BoundsRect с областью компонентов. Например, вы не хотите принимать перетаскиваемый компонент кнопкой, перекрывающей любую другую имеющуюся кнопку. В обработчике OnDragOver напишите примерно следующее:
FOR N := 0 TO ComponentCount-1 DO
IF COmponents[N] IS TButton THEN
IF IntersectRect(DummyRect, TControl(Components[N]).BoundsRect,
(Bounds(X-XOff, Y-YOff, DragW, DragH)) >0 THEN
Accept := False;
|
В этом случае курсор будет изменяться на перечеркнутый кружок при пересечении перетаскиваемым элементом границы интересующей вас кнопки. Вы должны сделать аналогичную логику или в обработчике EndDrag или OnDragDrop компонента PainBox.
Drag and Drop с минимизированным приложением.
Автор: Neil J. Rubenking В ситуации, когда ваше приложение минимизировано, необходимо понимать, что окно главной формы НЕ работает. Фактически, если вы проверяете окно главной формы, и обнаруживаете, что оно имеет прежний размер, не удивляйтесь, оно просто невидимо. Иконка минимизированного Delphi-приложения принадлежит объекту Application, чей дескриптор окна - Application.Handle.Вот некоторый код из моей программы, который с помощью компонента CheckBox проверяет возможность принятия перетаскиваемых файлов минимизированным приложением:
procedure TForm1.WMDropFiles(var Msg: TWMDropFiles);
{Вызывается только если TApplication НЕ получает drag/drop}
begin
RecordDragDrop(Msg.Drop, False); {внутренняя функция}
Msg.Result := 0;
end;
procedure TForm1.AppOnMessage(var Msg: TMsg; var Handled: Boolean);
{когда активно, получаем сообщения WM_DROPFILES, посылаемые
форме ИЛИ минимизированному приложению}
begin
if Msg.message = WM_DROPFILES then
begin
RecordDragDrop(Msg.wParam, Msg.hWnd = Application.Handle);
Handled := True;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DragAcceptFiles(Handle, True);
DragAcceptFiles(Application.Handle, False);
Application.OnMessage := nil;
end;
|
OK? Первоначально вызов DragAcceptFiles работает с дескриптором главной формы...
Рисование кривых по заданным точкам.
Автор: AndrusЗдесь я использую процедуру рисования кривой Безье между двумя точками. Можно задать кривизну кривой(20-35 лучше всего). Можно задать число отрезков между соседними точками, а если в процедуре DrawSlice убрать коментарий со строки.
// num_slices:=trunc(sqrt(sqr(p1.x-p2.x)+sqr(p1.y-p2.y)));
То число отрезков между соседними точками будет расчитываться автоматически, исходя из растояния между ними. Если потребуются дополнительные коментарии, пишите по адресу andrus78@mail.ru.
unit u_bezier;
interface
uses Windows, Graphics, SysUtils;
type TArrayPoint = array of TPoint; //массив точек
const num_slices: integer = 20; //число отрезков между двумя точками
krivizna: integer = 30; //кривизна кривой (длина плеча направляющей)
procedure DrawBezier(acanv: TCanvas; var ArrPoint: TArrayPoint);
/////////////////////////////////////////////////////////////////////
implementation
uses unit1;
type
TBezierPoint = record //точка Безье
x, y: integer; //основной узел
xl, yl, //левая контрольная точка
xr, yr: single; //правая контрольная точка
end;
TArrayBezierPoint = array of TBezierPoint; //массив точек Безье
const grad_to_rad = pi / 180; //перевод градусов в радианы
rad_to_grad = 180 / pi; //перевод радиан в градусы
rad_90 = 90 * grad_to_rad; //90 градусов в радианах
rad_180 = 180 * grad_to_rad; //180 градусов в радианах
rad_270 = 270 * grad_to_rad; //270 градусов в радианах
rad_360 = 360 * grad_to_rad; //360 градусов в радианах
var Canvas: TCanvas; //рабочий холст, на котором происходит рисование
//определить угол в радианах между точкой и положительным направлением оси х
function GetAngle(dx, dy: single): single;
begin
if dx = 0 then begin
if dy = 0 then Result := 0
else if dy < 0 then Result := rad_270
else Result := rad_90;
exit
end;
Result := arctan(abs(dy) / abs(dx));
if dy < 0 then
if dx < 0 then Result := rad_180 + Result
else Result := rad_360 - Result
else
if dx < 0 then Result := rad_180 - Result
end;
//определить направляющие линии к точке p
procedure GetCooPerpendikular(a, o, b: TPoint; var p: TBezierPoint);
var alfa, beta, gamma, dx, dy, angle_napr: single;
l1, l2: single;
begin
dx := a.x - o.x; dy := a.y - o.y;
alfa := GetAngle(dx, dy);
l1 := sqrt(dx * dx + dy * dy) * (krivizna / 100); //растояние oa
dx := b.x - o.x; dy := b.y - o.y;
beta := GetAngle(dx, dy);
l2 := sqrt(dx * dx + dy * dy) * (krivizna / 100); //растояние ob
gamma := (alfa + beta) / 2; //биссектриса угла aob
if alfa > beta then angle_napr := gamma + rad_90
else angle_napr := gamma - rad_90;
p.xl := o.x + l1 * cos(angle_napr);
p.yl := o.y + l1 * sin(angle_napr);
p.xr := o.x + l2 * cos(angle_napr + rad_180);
p.yr := o.y + l2 * sin(angle_napr + rad_180)
end;
//вычислить координаты точки, лежащей на участке кривой между
//двумя точками Безье в пределах от 0 до 1
procedure BezierValue(P1, P2: TBezierPoint; t: single; var X, Y: integer);
var t_sq, t_cb, r1, r2, r3, r4: single;
begin
t_sq := t * t;
t_cb := t * t_sq;
r1 := (1 - 3 * t + 3 * t_sq - t_cb);
r2 := (3 * t - 6 * t_sq + 3 * t_cb);
r3 := (3 * t_sq - 3 * t_cb);
r4 := (t_cb);
X := round(r1 * p1.x + r2 * p1.xr + r3 * p2.xl + r4 * p2.x);
Y := round(r1 * p1.y + r2 * p1.yr + r3 * p2.yl + r4 * p2.y)
end;
//рисуй участок кривой между двумя точками Безье
procedure DrawSlice(p1, p2: TBezierPoint);
var i: integer;
x, y: integer;
r1, r2: TRect;
begin
// если убрать комментарий, то количество отрезков между соседними
// точками будет расчитываться исходя из растояния между ними
// num_slices:=trunc(sqrt(sqr(p1.x-p2.x)+sqr(p1.y-p2.y)));
Canvas.MoveTo(p1.x, p1.y);
for i := 1 to num_slices - 1 do begin
BezierValue(p1, p2, i / num_slices, x, y);
Canvas.LineTo(x, y)
end;
Canvas.LineTo(p2.x, p2.y)
end;
//рисуй кривую на холсте acanv по точкам массива ArrPoint
procedure DrawBezier(acanv: TCanvas; var ArrPoint: TArrayPoint);
var ArrBezPoint: TArrayBezierPoint;
i, num_point: integer;
a, o, b: TPoint;
begin
Canvas := acanv;
num_point := high(ArrPoint) + 1;
SetLength(ArrBezPoint, num_point);
for i := 0 to num_point - 1 do begin
ArrBezPoint[i].x := ArrPoint[i].x;
ArrBezPoint[i].y := ArrPoint[i].y;
end;
ArrBezPoint[0].xr := ArrPoint[0].x;
ArrBezPoint[0].yr := ArrPoint[0].y;
ArrBezPoint[0].xl := ArrPoint[0].x;
ArrBezPoint[0].yl := ArrPoint[0].y;
for i := 1 to num_point - 2 do begin
a := ArrPoint[i - 1];
o := ArrPoint[i];
b := ArrPoint[i + 1];
GetCooPerpendikular(a, o, b, ArrBezPoint[i])
end;
ArrBezPoint[num_point - 1].xr := ArrPoint[num_point - 1].x;
ArrBezPoint[num_point - 1].yr := ArrPoint[num_point - 1].y;
ArrBezPoint[num_point - 1].xl := ArrPoint[num_point - 1].x;
ArrBezPoint[num_point - 1].yl := ArrPoint[num_point - 1].y;
for i := 1 to num_point - 1 do
DrawSlice(ArrBezPoint[i - 1], ArrBezPoint[i])
end;
end.
// *********************************** //
// использовать этот модуль можно так: //
// *********************************** //
procedure TForm1.Button2Click(Sender: TObject);
var ArrPoint: TArrayPoint;
begin
SetLength(ArrPoint, 5);
ArrPoint[0].x := random(200); ArrPoint[0].y := random(200);
ArrPoint[1].x := random(200); ArrPoint[1].y := random(200);
ArrPoint[2].x := random(200); ArrPoint[2].y := random(200);
ArrPoint[3].x := random(200); ArrPoint[3].y := random(200);
ArrPoint[4].x := random(200); ArrPoint[4].y := random(200);
num_slices := 10;
krivizna := 30;
DrawBezier(Form1.Canvas, ArrPoint)
end;
// нужно не забыть включить модуль в список используемых:
// implementation
// uses u_bezier;
Рисование без мерцания.
Автор: Mike Scott ...вот я и удивляюсь - почему я получаю мерцание, если я вызываю Repaint или Refresh, а не метод OnPaint напрямую? Или это просто "вариация на тему"? Имеются две фазы обновления окна. В первой фазе, при выводе окна, Windows посылает ему сообщение WM_ERASEBKGND, сообщающее о необходимости стирания фона перед процедурой рисования. Затем посылается сообщение WM_PAINT, служащее сигналом для закрашивания "переднего плана".Тем не менее, вы можете пропустить первую фазу, которая вызывает мерцание, одним из двух способов: первый способ заключается в том, что вы форсируете обновление сами, с помощью вызова функции Windows API InvalidateRect. На входе он получает дескриптор окна, указатель на закрашиваемую область - передаем NIL, если вы хотите отрисовать всю область окна - и третий параметр, сообщающий о необходимости очистки фона. Вот как раз последний параметр и должен содержать значение FALSE, если вы сами будете в методе Paint полностью отрисовывать всю область:
InvalidateRect( Handle, NIL, FALSE ) ; |
Описав первый способ, я скажу, что существует другое подходящее решение - использовать функциональность VCL. Вы можете указать VCL не стирать фон, добавляя [ csOpaque ] к значению свойства ControlStyle, как показано ниже:
ControlStyle := ControlStyle + [ csOpaque ] ; |
Если вы хотите углубиться в тему дальше, то я отошлю вас к моей статье "Optimizing Display Updates in Delphi" (Оптимизация обновления экрана в Delphi), опубликованной в первом выпуске журнала "Delphi magazine".
Как вывести на Canvas надпись под углом.
{Create a rotated font based on the font object F}
function CreateRotatedFont(F : TFont; Angle : Integer) : hFont;
var
LF: TLogFont;
begin
FillChar(LF, SizeOf(LF), #0);
with LF do
begin
lfHeight := F.Height;
lfWidth := 0;
lfEscapement := Angle*10;
lfOrientation := 0;
if fsBold in F.Style then
lfWeight := FW_BOLD
else
lfWeight := FW_NORMAL;
lfItalic := Byte(fsItalic in F.Style);
lfUnderline := Byte(fsUnderline in F.Style);
lfStrikeOut := Byte(fsStrikeOut in F.Style);
lfCharSet := DEFAULT_CHARSET;
StrPCopy(lfFaceName, F.name);
lfQuality := DEFAULT_QUALITY;
{everything else as default}
lfOutPrecision := OUT_DEFAULT_PRECIS;
lfClipPrecision := CLIP_DEFAULT_PRECIS;
case F.Pitch of
fpVariable: lfPitchAndFamily := VARIABLE_PITCH;
fpFixed: lfPitchAndFamily := FIXED_PITCH;
else
lfPitchAndFamily := DEFAULT_PITCH;
end;
end;
Result := CreateFontIndirect(LF);
end;
...
{create the rotated font}
if FontAngle <> 0 then
Canvas.Font.Handle := CreateRotatedFont(Font, FontAngle);
...
|
Вращаются только векторные шрифты.
Отрисовка стрелки с заданными параметрами.
Автор: Александр WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Отрисовка стрелки с заданными параметрами
// рисует стрелку с заданными параметрами
// X_Line, Y_Line - координаты "неподвижного" конца линии стрелки;
// Length_Line, Length_Arrow - длина линии и бокового ребра стрелки;
// Angle_Line, Angle_Arrow - углы между линией стрелки и горизонтальной
// осью и между линией стрелки и боковым ребром стрелки;
// IsAllTriangle - если True, то "подвижные" концы боковых рёбер стрелки
// соединяются отрезком прямой(и не рисуется высота равнобедренного треугольника стрелки);
// DrawSurface - поверхность на которой будет рисоваться стрелка
Зависимости: ничего неожиданного
Автор: Александр
Copyright: default
Дата: 14 февраля 2004 г.
***************************************************** }
procedure DrawArrow(X_Line, Y_Line, Length_Line, Length_Arrow: Integer;
Angle_Line, Angle_Arrow: Extended;
IsAllTriangle: Boolean; DrawSurface: TCanvas);
var
XB, YB, XE, YE, XM, YM: Integer;
begin
XB := Round(X_Line + Cos(Angle_Line) * Length_Line);
YB := Round(Y_Line - Sin(Angle_Line) * Length_Line);
XM := Round(XB - Cos(Angle_Arrow - Angle_Line) * Length_Arrow);
YM := Round(YB - Sin(Angle_Arrow - Angle_Line) * Length_Arrow);
XE := Round(XB + Sin(Angle_Arrow + Angle_Line - Pi / 2) * Length_Arrow);
YE := Round(YB + Cos(Angle_Arrow + Angle_Line - Pi / 2) * Length_Arrow);
DrawSurface.MoveTo(X_Line, Y_Line);
if IsAllTriangle then
begin
DrawSurface.LineTo(Round((XM + XE) / 2), Round((YM + YE) / 2));
DrawSurface.MoveTo(XB, YB);
end
else
DrawSurface.LineTo(XB, YB);
DrawSurface.LineTo(XM, YM);
if IsAllTriangle then
DrawSurface.LineTo(XE, YE)
else
DrawSurface.MoveTo(XE, YE);
DrawSurface.LineTo(XB, YB)
end;
Как нарисовать кривую Безье.
Автор: Даниил Карапетян WEB сайт: http://program.dax.ruКак нарисовать кривую Безье. Именно она применяется для построения гладких кривых во всех графических программах - от PaintBrush до CorelDraw и PhotoShop. Для задания кривой Безье n-ной степени (чем больше степень, тем более кривой может быть линия; кривая первой степени - отрезок) нужно указать n+1 точку. Первая и последняя точки будут началом и концом кривой, а остальные точки задаю ее поведение на других участках. В частности, первая и n-ая точки задают касательные и кривизну кривой на ее концах. В большинстве программ используются кривые Безье третьего порядка. Начиная с Delphi5 такую кривую можно нарисовать при помощи функции PolyBezier. Кривая Безье задается параметрически (x=x(t), y=y(t)). Это позволяет ей вести себя абсолютно произвольно. Если бы она задавалась, как y(x), она не смогла бы даже сделать поворот на 180 градусов. Функции x(t) и y(t) выглядят так: x(t)= Cn0 * t0 * (1-t)n * x0 + Cn1 * t1 * (1-t)n-1 * x1 + Cn2 * t2 * (1-t)n-2 * x2 + ... + Cnn * tn * (1-t)0 * xn y(t)= Cn0 * t0 * (1-t)n * y0 + Cn1 * t1 * (1-t)n-1 * y1 + Cn2 * t2 * (1-t)n-2 * y2 + ... + Cnn * tn * (1-t)0 * yn где n - порядок кривой, Cni - коэффициенты в разложении бинома Ньютона, t - параметр, меняющийся от 0 до 1, xi, yi - координаты опорных точек. Эта программа строит кривую Безье n-ного порядка. n задается в SpinEdit1. Все узлы можно перемещать по полю мышью. Для создания нового узла нужно нажать мышью на пустое место на поле или увеличить порядок кривой. Скачать необходимые для компиляции файлы проекта можно на http://program.dax.ru
uses Math;
const
RectSize = 5;
MaxN = 128;
var
n: integer = -1;
pt: array[0..MaxN] of TPoint;
C: array[0..MaxN] of single;
bm: TBitMap;
function GetBinomialCoefficient(m, i: integer): single;
function Factorial(x: integer): double;
var
i: integer;
begin
result := 1;
for i := 2 to x do
result := result * i;
end;
begin
result := Factorial(m) / (Factorial(i) * Factorial(m - i));
end;
procedure DrawBezier(Canvas: TCanvas; Count: integer);
type
TPointArray = array[word] of TPoint;
PPointArray = ^TPointArray;
var
p: PPointArray;
Step, qx, qy, t, q: single;
i, j: integer;
begin
GetMem(p, sizeof(TPoint) * (Count + 1));
Step := 1.0 / Count;
for i := 0 to Count do
begin
t := i * Step;
qx := 0;
qy := 0;
for j := 0 to n do
begin
q := C[j] * IntPower(1 - t, j) * IntPower(t, n - j);
qx := qx + q * pt[j].x;
qy := qy + q * pt[j].y;
end;
p[i] := Point(round(qx), round(qy));
end;
Canvas.Polyline(Slice(p^, Count + 1));
FreeMem(p);
end;
procedure DrawLines(canvas: TCanvas; const pt: array of TPoint);
var
i: integer;
begin
Canvas.Pen.Color := clGreen;
Canvas.Pen.Width := 1;
Canvas.MoveTo(pt[0].x, pt[0].y);
for i := 0 to n do
begin
Canvas.Rectangle(Bounds(pt[i].x - RectSize, pt[i].y - RectSize,
2 * RectSize, 2 * RectSize));
Canvas.LineTo(pt[i].x, pt[i].y);
end;
end;
procedure Redraw;
begin
with Form1.PaintBox1 do
begin
bm.Canvas.FillRect(Bounds(0, 0, Width, Height));
if Form1.CheckBox1.Checked then
DrawLines(bm.Canvas, pt);
bm.Canvas.PolyBezier(pt);
bm.Canvas.Pen.Color := clRed;
bm.Canvas.pen.Width := Form1.SpinEdit3.Value;
DrawBezier(bm.Canvas, Form1.SpinEdit2.Value);
Canvas.Draw(0, 0, bm);
end;
end;
var
moving: integer = -1;
oldr: TRect;
procedure FillRandom(NewN: integer);
var
i: integer;
begin
randomize;
for i := n + 1 to NewN do
pt[i] := Point(random(Form1.PaintBox1.Width - 20) + 10,
random(Form1.PaintBox1.Height - 20) + 10);
n := NewN;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
bm := TBitmap.Create;
bm.Width := PaintBox1.Width;
bm.Height := PaintBox1.height;
SpinEdit1.MinValue := 1;
SpinEdit1.MaxValue := MaxN;
SpinEdit1.Value := 3;
SpinEdit2.MinValue := 6;
SpinEdit2.MaxValue := MaxN * 4;
SpinEdit2.Value := 50;
SpinEdit2.OnChange := PaintBox1.OnPaint;
SpinEdit3.MinValue := 1;
SpinEdit3.MaxValue := 8;
SpinEdit3.Value := 3;
SpinEdit3.OnChange := PaintBox1.OnPaint;
CheckBox1.Checked := true;
CheckBox1.OnClick := PaintBox1.OnPaint;
end;
procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
i: integer;
r: TRect;
begin
if Button <> mbLeft then
Exit;
for i := 0 to n do
if (abs(X - pt[i].x) <= RectSize) and (abs(Y - pt[i].y) <= RectSize) then
begin
moving := i;
r.TopLeft := Form1.ClientToScreen(PaintBox1.BoundsRect.TopLeft);
r.BottomRight := Form1.ClientToScreen(PaintBox1.BoundsRect.BottomRight);
GetClipCursor(oldr);
ClipCursor(@r);
Exit;
end;
if moving < 0 then
begin
SpinEdit1.Value := SpinEdit1.Value + 1;
pt[n] := Point(X, Y);
Redraw;
end;
end;
procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X, Y:
Integer);
begin
if moving < 0 then
Exit;
pt[moving] := Point(X, Y);
Redraw;
end;
procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if (Button = mbLeft) and (moving >= 0) then
begin
moving := -1;
ClipCursor(@oldr);
end;
end;
procedure TForm1.SpinEdit1Change(Sender: TObject);
var
i: integer;
begin
FillRandom(SpinEdit1.Value);
SpinEdit2.MinValue := n * 2;
for i := 0 to n do
C[i] := GetBinomialCoefficient(n, i);
Redraw;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
Redraw;
end;
Отрисовка битового образца.
unit aplanes_;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
TForm1 = class(TForm)
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
sky, aplane: TBitMap; // битовые образы: небо и самолет
implementation
{$R *.DFM}
procedure TForm1.FormPaint(Sender: TObject);
begin
// создать битовые образы
sky := TBitMap.Create;
aplane := TBitMap.Create;
// загрузить картинки
sky.LoadFromFile('sky.bmp');
aplane.LoadFromFile('aplane.bmp');
Form1.Canvas.Draw(0, 0, sky); // отрисовка фона
Form1.Canvas.Draw(20, 20, aplane); // отрисовка левого самолета
aplane.Transparent := True;
// теперь элементы рисунка, цвет которых совпадает с цветом
// левой нижней точки битового образа, не отрисовываются
Form1.Canvas.Draw(120, 20, aplane); // отрисовка правого самолета
// освободить память
sky.free;
aplane.free;
end;
end.
Скачать весь проект
Программа рисует на форме календарь на 2002 год.

| Автор: Даниил Карапетян WEB сайт: http://program.dax.ru |
| Девушка флиртует с программистом (сисадмином, электронщиком). Задает вопрос: - О чем думает компьютер? Получает ответ: - Он думает о том дебиле, который написал для него программу о том что нужно думать. |
Скачать необходимые для компиляции файлы проекта можно на program.dax.ru
const year = 2002; // Год календаря
var months: array [1..12] of byte;
procedure TForm1.FormCreate(Sender: TObject);
begin
Form1.Caption := 'Календарь на ' + IntToStr(year) + ' год';
Form1.Color := clWhite;
// Длины месяцев:
months[1] := 31;
months[2] := 28 + ord(IsLeapYear(year));
months[3] := 31;
months[4] := 30;
months[5] := 31;
months[6] := 30;
months[7] := 31;
months[8] := 31;
months[9] := 30;
months[10] := 31;
months[11] := 30;
months[12] := 31;
end;
procedure TForm1.FormPaint(Sender: TObject);
const // Настройки размеров календаря:
MonthDX = 150;
MonthDY = 135;
DayDX = 20;
DayDY = 15;
MonthH = 20;
var
month, i: integer;
day: integer;
s: string[2];
begin
with Form1.Canvas do for month := 1 to 12 do begin
// Вывод названия месяца:
Font.Name := 'Times';
Font.Size := 13;
TextOut((month - 1) mod 3 * MonthDX, (month - 1) div 3 * MonthDY,
LongMonthNames[month]);
Font.Name := 'Courier';
Font.Size := 8;
// Вывод названий дней недели:
for day := 1 to 7 do
TextOut((month - 1) mod 3 * MonthDX,
day mod 7 * DayDY + (month - 1) div 3 * MonthDY + MonthH,
ShortDayNames[(day + 1) mod 7 + 1]);
// Определение дня недели первого числа месяца:
day := DayOfWeek(EncodeDate(year, month, 1)) - 2;
if day < 0 then inc(day, 7);
// Вывод чисел:
for i := 1 to months[month] do begin
str(i: 2, s);
TextOut(day div 7 * DayDX + (month - 1) mod 3 * MonthDX + DayDX,
day mod 7 * DayDY + (month - 1) div 3 * MonthDY + MonthH, s);
inc(day);
end;
end;
end;
|
Выводим цветной текст на форме под любым углом.
Пример демонстрирует вывод теста случайным образом на форме под определённым углом. Добавляем в форму компонент TButton и в событие OnClick следующий код:
procedure TForm1.Button1Click(Sender: TObject);
var
logfont: TLogFont;
font: Thandle;
count: integer;
begin
LogFont.lfheight := 20;
logfont.lfwidth := 20;
logfont.lfweight := 750;
LogFont.lfEscapement := -200;
logfont.lfcharset := 1;
logfont.lfoutprecision := out_tt_precis;
logfont.lfquality := draft_quality;
logfont.lfpitchandfamily := FF_Modern;
font := createfontindirect(logfont);
SelectObject(Form1.canvas.handle, font);
SetTextColor(Form1.canvas.handle, rgb(0, 0, 200));
SetBKmode(Form1.canvas.handle, transparent);
for count := 1 to 10 do
begin
Canvas.TextOut(Random(form1.width), Random(form1.height), 'Delphi World');
SetTextColor(form1.canvas.handle, rgb(Random(255), Random(255), Random(255)));
end;
DeleteObject(font);
end;
|
Как вывести цветную надпись на StatusBare.
Статусбар, это стандартный элемент управления Windows и как все отображает шрифт, заданный в параметре clBtnText, который устанавливается через Панель управления. Поумолчанию этот цвет чёрный, но он может менятся в зависимоти пользовательской темы. StatusBar и связанные с ним панели имеют возможность самостоятельной перерисовки (owner-draw), позволяющей рисовать текст различными цветами. Для этого необходимо в TStatusBar.Panels установить свойство Style в OwnerDraw.
procedure TForm1.StatusBar1DrawPanel(StatusBar: TStatusBar;
Panel: TStatusPanel; const Rect: TRect);
begin
if Panel = StatusBar.Panels[0] then
begin
StatusBar.Canvas.Font.Color := clRed;
StatusBar.Canvas.TextOut(Rect.Left, Rect.Top, 'Panel - 0')
end
else
begin
StatusBar.Canvas.Font.Color := clGreen;
StatusBar.Canvas.TextOut(Rect.Left, Rect.Top, 'Panel - 1');
end;
end;
|
Нарисовать подсвеченный прямоугольник вокруг контрола под мышкой.

{
This tip might be useful if you want to program
a screen capture tool and show a bounding box
around a control or for a spy tool like winsight to
highlight a object on the screen.
}
var
hOldWnd :HWND;
procedure FrameWindow(Wnd: HWnd);
var
Rect: TRect;
DC: hDC;
OldPen, Pen: hPen;
OldBrush, Brush: hBrush;
X2, Y2: Integer;
begin
{ Get the target window's rect and DC }
GetWindowRect(Wnd, Rect);
DC := GetWindowDC(Wnd);
{ Set ROP appropriately for highlighting }
SetROP2(DC, R2_NOT);
{ Select brush and pen }
Pen := CreatePen(PS_InsideFrame, 4, 0);
OldPen := SelectObject(DC, Pen);
Brush := GetStockObject(Null_Brush);
OldBrush := SelectObject(DC, Brush);
{ Set dimensions of highlight }
X2 := Rect.Right - Rect.Left;
Y2 := Rect.Bottom - Rect.Top;
{ Draw highlight box }
Rectangle(DC, 0, 0, X2, Y2);
{ Clean up }
SelectObject(DC, OldBrush);
SelectObject(DC, OldPen);
ReleaseDC(Wnd, DC);
{ Do NOT delete the brush, because it was a stock object }
DeleteObject(Pen);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
hNewWnd: HWnd;
begin
hNewWnd := WindowFromPoint(Mouse.CursorPos);
{ To avoid flickering, remove the old frame ONLY if moved to new window }
if hNewWnd <> hOldWnd then
begin
if hOldWnd <> 0 then
FrameWindow(hOldWnd);
if hNewWnd <> 0 then
FrameWindow(hNewWnd);
hOldWnd := hNewWnd;
end;
end;
|
Рисование КРИВЫХ в Delphi.
Автор: Dmitry Streblechenko В: У кого-нибудь есть исходный код или какая-либо информация для рисования кривых Безье? Я должен использовать их в своем компоненте. Пожалуйста используйте для ответа мой адрес электронной почты. О: Я делал это недавно; мне было лениво разбираться с тем, как рисовать кривые Безье с помощью Win API, поэтому я использовал функцию Polyline().Примечание: для координатных точек я использовал реальные величины типа floating (я применял некоторый тип виртуального экрана), округляя их до целого.
PBezierPoint = ^TBezierPoint; TBezierPoint = record X, Y: double; // основной узел Xl, Yl: double; // левая контрольная точка Xr, Yr: double; // правая контрольная точка end; // P1 и P2 - две точки TBezierPoint, расположенные между 0 и 1: // когда t=0 X=P1.X, Y=P1.Y; когда t=1 X=P2.X, Y=P2.Y; procedure BezierValue(P1, P2: TBezierPoint; t: double; var X, Y: double); var t_sq, t_cb, r1, r2, r3, r4: double; begin t_sq := t * t; t_cb := t * t_sq; r1 := (1 - 3 * t + 3 * t_sq - t_cb) * P1.X; r2 := (3 * t - 6 * t_sq + 3 * t_cb) * P1.Xr; r3 := (3 * t_sq - 3 * t_cb) * P2.Xl; r4 := (t_cb) * P2.X; X := r1 + r2 + r3 + r4; r1 := (1 - 3 * t + 3 * t_sq - t_cb) * P1.Y; r2 := (3 * t - 6 * t_sq + 3 * t_cb) * P1.Yr; r3 := (3 * t_sq - 3 * t_cb) * P2.Yl; r4 := (t_cb) * P2.Y; Y := r1 + r2 + r3 + r4; end; |
Для рисования кривой Безье разделяем интервал между P1 и P2 на несколько отрезков (их количество влияет на точность воспроизведения кривой, 3 - 4 точки вполне достаточно), затем в цикле создаем массив точек, используем описанную выше процедуру с параметром t от 0 до 1 и рисуем данный массив точек, используя функцию polyline().
Рисование КРИВЫХ в Delphi 2.
В: У кого-нибудь есть исходный код или какая-либо информация для рисования кривых Безье? Я должен использовать их в своем компоненте. Пожалуйста используйте для ответа мой адрес электронной почты. Я решил ответить на этот крик души - причина?: 1. Не первый раз вижу подобный вопрос, 2. Задача настолько избита, что я без труда нашел ответ в своем архиве. (BTW: У меня есть более старые решения, чем это ;-P)Тем не менее эта технология жива до сих пор и приносит свои плоды:
(********************************************************************)
(* GRAPHIX TOOLBOX 4.0 *)
(* Copyright (c) 1985, 87 by Borland International, Inc. *)
(********************************************************************)
unit GShell;
interface
{------------------------------ вырезано --------------------------}
procedure Bezier(A: PlotArray; MaxContrPoints: integer;
var B: PlotArray; MaxIntPoints: integer);
implementation
{------------------------------ вырезано --------------------------}
procedure Bezier {(A : PlotArray; MaxContrPoints : integer;
var B : PlotArray; MaxIntPoints : integer)};
const
MaxControlPoints = 25;
type
CombiArray = array[0..MaxControlPoints] of Float;
var
N: integer;
ContrPoint, IntPoint: integer;
T, SumX, SumY, Prod, DeltaT, Quot: Float;
Combi: CombiArray;
begin
MaxContrPoints := MaxContrPoints - 1;
DeltaT := 1.0 / (MaxIntPoints - 1);
Combi[0] := 1;
Combi[MaxContrPoints] := 1;
for N := 0 to MaxContrPoints - 2 do
Combi[N + 1] := Combi[N] * (MaxContrPoints - N) / (N + 1);
for IntPoint := 1 to MaxIntPoints do
begin
T := (IntPoint - 1) * DeltaT;
if T <= 0.5 then
begin
Prod := 1.0 - T;
Quot := Prod;
for N := 1 to MaxContrPoints - 1 do
Prod := Prod * Quot;
Quot := T / Quot;
SumX := A[MaxContrPoints + 1, 1];
SumY := A[MaxContrPoints + 1, 2];
for N := MaxContrPoints downto 1 do
begin
SumX := Combi[N - 1] * A[N, 1] + Quot * SumX;
SumY := Combi[N - 1] * A[N, 2] + Quot * SumY;
end;
end
else
begin
Prod := T;
Quot := Prod;
for N := 1 to MaxContrPoints - 1 do
Prod := Prod * Quot;
Quot := (1 - T) / Quot;
SumX := A[1, 1];
SumY := A[1, 2];
for N := 1 to MaxContrPoints do
begin
SumX := Combi[N] * A[N + 1, 1] + Quot * SumX;
SumY := Combi[N] * A[N + 1, 2] + Quot * SumY;
end;
end;
B[IntPoint, 1] := SumX * Prod;
B[IntPoint, 2] := SumY * Prod;
end;
end; { Bezier }
end. { GShell }
|
Как вывести текст с красивым обрезанием если не помещается.

Используй вызов DrawTextEx, установив в параметре dwDTFormat значение DT_PATH_ELLIPSIS.
procedure TForm1.FormPaint(Sender: TObject); var r: TRect; begin r := Rect(20, 20, 110, 70); // DT_PATH_ELLIPSIS or DT_WORD_ELLIPSIS or DT_END_ELLIPSIS DrawTextEx(Form1.Canvas.Handle, 'Delphi World - это круто!!!', 25, r, DT_WORD_ELLIPSIS, nil); end; |
Рисовать неактивный текст.
function DrawDisabledText (Canvas : tCanvas; Str: PChar; Count: Integer; var Rect: TRect; Format: Word): Integer; begin SetBkMode(Canvas.Handle, TRANSPARENT); OffsetRect(Rect, 1, 1); Canvas.Font.color := ClbtnHighlight; DrawText(Canvas.Handle, Str, Count, Rect, Format); Canvas.Font.Color := ClbtnShadow; OffsetRect(Rect, -1, -1); DrawText(Canvas.Handle, Str, Count, Rect, Format); end; |
Хочу прорисовать границу ячейки в Excel.
Cells[i,j].Borders.LineStyle := xlContinuous; |
Вывод шрифтов в списке в виде самих шрифтов.
| - Что общего между Биллом Гейтсом и Россией? - Одинаковый валютный запас |
unit Fontlist;
interface
uses
Windows, Classes, Graphics, Forms, Controls, StdCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Label1: TLabel;
FontLabel: TLabel;
procedure FormCreate(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure DrawItem(Control: TWinControl; index: Integer; Rect: TRect;
State: TOwnerDrawState);
procedure ListBox1MeasureItem(Control: TWinControl; index: Integer;
var Height: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
Listbox1.Items := Screen.Fonts;
end;
procedure TForm1.ListBox1Click(Sender: TObject);
begin
FontLabel.Caption := ListBox1.Items[ListBox1.ItemIndex];
end;
procedure TForm1.DrawItem(Control: TWinControl; index: Integer;
Rect: TRect; State: TOwnerDrawState);
begin
with ListBox1.Canvas do
begin
FillRect(Rect);
Font.name := ListBox1.Items[index];
Font.Size := 0; // use font's preferred size
TextOut(Rect.Left+1, Rect.Top+1, ListBox1.Items[index]);
end;
end;
procedure TForm1.ListBox1MeasureItem(Control: TWinControl; index: Integer;
var Height: Integer);
begin
with ListBox1.Canvas do
begin
Font.name := Listbox1.Items[index];
Font.Size := 0; // use font's preferred size
Height := TextHeight('Wg') + 2; // measure ascenders and descenders
end;
end;
end.
|
Как нарисовать фрактал.
Автор: http://www.swissdelphicenter.ch
procedure DrawMandelbrot(ACanvas: TCanvas; X, Y, au, bu: Double; X2, Y2:
Integer);
var
c1, c2, z1, z2, tmp: Double;
i, j, Count: Integer;
begin
c2 := bu;
for i := 10 to X2 do
begin
c1 := au;
for j := 0 to Y2 do
begin
z1 := 0;
z2 := 0;
Count := 0;
{count is deep of iteration of the mandelbrot set
if |z| >=2 then z is not a member of a mandelset}
while (((z1 * z1 + z2 * z2 < 4) and (Count <= 90))) do
begin
tmp := z1;
z1 := z1 * z1 - z2 * z2 + c1;
z2 := 2 * tmp * z2 + c2;
Inc(Count);
end;
//the color-palette depends on TColor(n*count mod t)
{$IFDEF LINUX}
ACanvas.Pen.Color := (16 * Count mod 255);
ACanvas.DrawPoint(j, i);
{$ELSE}
ACanvas.Pixels[j, i] := (16 * Count mod 255);
{$ENDIF}
c1 := c1 + X;
end;
c2 := c2 + Y;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
R: TRect;
au, ao: Integer;
dX, dY, bo, bu: Double;
begin
// Initialize Mandelbrot
R.Left := 0;
R.Right := 200;
R.Top := 0;
R.Bottom := 205;
ao := 1;
au := -2;
bo := 1.5;
bu := -1.5;
//direct scaling cause of speed
dX := (ao - au) / (R.Right - R.Left);
dY := (bo - bu) / (R.Bottom - R.Top);
DrawMandelbrot(Self.Canvas, dX, dY, au, bu, R.Right, R.Bottom);
end;
Как нарисовать график функции.
procedure TForm1.Button3Click(Sender: TObject);
var
x, y: array[1..50] of double;
i: integer;
scalex, scaley, ymin, ymax, xmin, xmax: double;
begin
for i := 1 to 50 do
begin
y[i] := sin(i * 0.5);
x[i] := i;
end;
xmin := x[1];
xmax := x[1];
ymin := y[1];
ymax := y[1];
for i := 2 to 50 do
begin // или используйте ymin:=MinValue(y); и т.д.
if y[i] < ymin then
ymin := y[i];
if y[i] > ymax then
ymax := y[i];
if x[i] < xmin then
xmin := x[i];
if x[i] > xmax then
xmax := x[i];
end;
scalex := paintbox1.Width / (xmax - xmin);
scaley := paintbox1.Height / (ymax - ymin);
with paintbox1.canvas do
begin
moveto(trunc(scalex * (x[1] - xmin)), paintbox1.height - trunc(scaley * (y[1]
- ymin)));
for i := 2 to 50 do
Lineto(trunc(scalex * (x[i] - xmin)), paintbox1.height - trunc(scaley *
(y[i] - ymin)));
end;
end;
Как нарисовать график функции 2.
Автор: Baa WEB сайт: http://forum.vingrad.ruЗабавная штука синусы:
for i := 1 to 500 do
paintbox1.Canvas.Pixels[round(sin(i * 5) * 10 + 50), round(sin(i * 10) * 10 +
50)] := RGB(0, 0, 0);
Рисование графов.
...вы могли бы использовать объект TCanvas, чем рисовать самому. В вашем случае сгодится компонент TImage, он имеет bitmap и свойство canvas, на котором очень удобно рисовать.Пример: (Создайте новую форму, добавьте к ней Image и Button. Добавьте следующий код к обработчику события нажатия кнопки).
var
x, l: Integer;
y, a: Double;
begin
Image1.Picture.Bitmap := TBitmap.Create;
Image1.Picture.Bitmap.Width := Image1.Width;
Image1.Picture.Bitmap.Height := Image1.Height; {Эти три строчки могут быть
размещены в обработчике Form1.Create}
l := Image1.Picture.Bitmap.Width;
for x := 0 to l do
begin
a := (x / l) * 2 * Pi; {Преобразуем позицию по оси X к углу между 0 & 2Pi}
y := Sin(a); {Ваша функция должна находиться здесь}
y := y * (Image1.Picture.Bitmap.Height / 2); {Масштабируем по оси Y}
y := y * -1; {Инвертируем Y, верх экрана это 0 !}
y := y + (Image1.Picture.Bitmap.Height / 2);
{Добавляем компенсацию для среднего 0}
Image1.Picture.Bitmap.Canvas.Pixels[Trunc(x), Trunc(y)] := clBlack;
end;
end;
|
Я обнаружил, что лучшим решением будет рисование на холсте. Предпочтительно делать это в отдельной процедуре, которая принимает в качестве параметров TCanvas и TRect. Таким способом мы может передать в качестве параметров холст вашего окна и клиентскую область для рисования на экране, и холст принтера и область клиента для ее позиционирования и печати. Чтобы посмотреть доступные для рисования подпрограммы, взгляните на методы холста.
Вывести полупрозрачный текст.
procedure TForm1.FormPaint(Sender: TObject);
var
x, y: integer;
bm: TBitMap;
begin
Form1.ClientWidth := 200;
Form1.ClientHeight := 100;
randomize;
for x := 0 to 199 do
for y := 0 to 99 do
if random(3) = 1 then
Form1.Canvas.Pixels[x,y] := clGreen
else
Form1.Canvas.Pixels[x,y] := clLime;
bm := TBitMap.Create;
bm.Width := 200;
bm.Height := 100;
with bm.Canvas do
begin
Brush.Color := clGreen;
FillRect(ClipRect);
Font.name := 'Arial';
Font.Size := 50;
Font.Color := clGray;
Font.Style := [fsBold];
TextOut((bm.Width - TextWidth('Text')) div 2,
(bm.Height - TextHeight('Text')) div 2, 'Text');
end;
Form1.Canvas.CopyMode := cmSrcPaint;
Form1.Canvas.CopyRect(bm.Canvas.ClipRect, bm.Canvas,
bm.Canvas.ClipRect);
bm.Destroy;
end;
|
Рисование иконки в TImage.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
DrawIcon(Image1.Canvas.Handle, 0, 0, LoadCursor(0, IDC_ARROW));
Как рисовать картинки в пунктах меню (через OwnerDraw).
unit DN_Win;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, Menus, StdCtrls,
type
TDNForm = class(TForm)
MainMenu1: TMainMenu;
cm_MainExit: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure cm_MainExitClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
BM: TBitmap;
procedure WMDrawItem(var Msg:TWMDrawItem); message wm_DrawItem;
procedure WMMeasureItem(var Msg:TWMMeasureItem); message wm_MeasureItem;
end;
var
DNForm: TDNForm;
implementation
{$R *.DFM}
var
Comm, yMenu: word;
procedure TDNForm.FormCreate(Sender: TObject);
begin
yMenu:=GetSystemMetrics(SM_CYMENU);
comm:=cm_MainExit.Command;
ModifyMenu(MainMenu1.Handle,0,mf_ByPosition or mf_OwnerDraw,comm,'Go');
end;
procedure TDNForm.cm_MainExitClick(Sender: TObject);
begin
DNForm.Close;
end;
procedure TDNForm.WMMeasureItem(var Msg:TWMMeasureItem);
begin
with Msg.MeasureItemStruct^ do
if ItemID=comm then
begin
ItemWidth:=yMenu;
Itemheight:=yMenu;
end;
end;
procedure TDNForm.WMDrawItem(var Msg: TWMDrawItem);
var
MemDC:hDC;
BM:hBitMap;
mtd:longint;
begin
with Msg.DrawItemStruct^ do
begin
if ItemID=comm then
begin
BM:=LoadBitMap(hInstance,'dver');
MemDC:=CreateCompatibleDC(hDC);
SelectObject(MemDC,BM);
if ItemState=ods_Selected then
mtd:=NotSrcCopy
else
mtd:=SrcCopy;
StretchBlt(hDC,rcItem.left, rcItem.top, yMenu, yMenu, MemDC,
0, 0, 24, 23, mtd);
DeleteDC(MemDC);
DeleteObject(BM);
end;
end;
end;
end.
|
Компонент для отрисовки линий.

Вот компонент, инкапсулирующий функции рисования линий. Он может рисовать горизонтальные, вертикальные и диагональные линии. Вы можете добавить необходимые вам события в секцию *published*.
unit Lines; {от Bill Murto, CIS 73730,2505}
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms;
type
TLineOrigin = (loTopLeft, loTopRight);
TLine = class(TGraphicControl)
private
{ Private declarations }
fOrigin: TLineOrigin;
fPen: TPen;
procedure SetOrigin(Value: TLineOrigin);
procedure SetPen(Value: TPen);
protected
{ Protected declarations }
procedure Paint; override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
property Origin: TLineOrigin read fOrigin write SetOrigin default
loTopLeft;
property Pen: TPen read fPen write SetPen;
property Height default 33;
property Width default 33;
procedure StyleChanged(Sender: TObject);
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TLine]);
end;
procedure TLine.SetOrigin(Value: TLineOrigin);
begin
if fOrigin <> Value then
begin
fOrigin := Value;
Invalidate;
end;
end;
procedure TLine.SetPen(Value: TPen);
begin
fPen.Assign(Value);
end;
procedure TLine.StyleChanged(Sender: TObject);
begin
Invalidate;
end;
constructor TLine.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Height := 33;
Width := 33;
fPen := TPen.Create;
fPen.OnChange := StyleChanged;
if csOpaque in ControlStyle then
ControlStyle := ControlStyle - [csOpaque];
end;
procedure TLine.Paint;
begin
with Canvas do
begin
Pen := fPen;
if (Width <= Pen.Width) or (Height <= Pen.Width) then
begin
if Width <= Pen.Width then
begin
MoveTo(0, 0);
LineTo(0, Height);
end;
if Height <= Pen.Width then
begin
MoveTo(0, 0);
LineTo(Width, 0);
end;
end
else
case fOrigin of
loTopLeft:
begin
MoveTo(0, 0);
LineTo(Width, Height);
end;
loTopRight:
begin
MoveTo(Width, 0);
LineTo(0, Height);
end;
end;
end;
end;
destructor TLine.Destroy;
begin
fPen.Free;
inherited Destroy;
end;
end.
|
Нарисовать линию, не используя функции LineTo.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Enables you do draw a line if for some reason you
cannot use the delphi LineTo procedure.
For example, for drawing higher resolution lines
or drawing lines in 2D arrays.
}
procedure DrawLine(APoint1, APoint2: TPoint; ACanvas: TCanvas);
var
Lpixel, LMaxAxisLength: integer;
LRatio: Real;
begin
LMaxAxisLength := Max(abs(APoint1.X - APoint2.X), abs(APoint1.Y - APoint2.Y));
for Lpixel := 0 to LMaxAxisLength do
begin
LRatio := Lpixel / LMaxAxisLength;
ACanvas.Pixels[APoint1.X + Round((APoint2.X - APoint1.X) * LRatio),
APoint1.Y + Round((APoint2.Y - APoint1.Y) * LRatio)] :=
ACanvas.Pen.Color;
end;
end;
// Draw a double resolution line
procedure DrawLineDouble(APoint1, APoint2: TPoint; ACanvas: TCanvas);
var
Lpixel, LMaxAxisLength: integer;
LRatio: Real;
LPoint: TPoint;
begin
LMaxAxisLength := max(abs(APoint1.X - APoint2.X), abs(APoint1.Y - APoint2.Y));
for Lpixel := 0 to LMaxAxisLength do
begin
LRatio := Lpixel / LMaxAxisLength;
LPoint.X := APoint1.X + Round((APoint2.X - APoint1.X) * LRatio);
LPoint.Y := APoint1.Y + Round((APoint2.Y - APoint1.Y) * LRatio);
with ACAnvas do
begin
Pixels[LPoint.X * 2, LPoint.Y * 2] := clBlack;
Pixels[(LPoint.X * 2) + 1, LPoint.Y * 2] := clBlack;
Pixels[LPoint.X * 2, (LPoint.Y * 2) + 1] := clBlack;
Pixels[(LPoint.X * 2) + 1, (LPoint.Y * 2) + 1] := clBlack;
end;
end;
end;
Как элемент списка нарисовать своим цветом.

Не забудь установить свойство Style у твоего ListBox в lbOwnerDrawFixed или в lbOwnerDrawVariable.
procedure TForm1.ListBox1DrawItem(Control: TWinControl; index: Integer;
Rect: TRect; State: TOwnerDrawState);
begin
with ListBox1 do
begin
if odSelected in State then
Canvas.Brush.Color:=clTeal { твой цвет }
else
Canvas.Brush.Color:=clWindow;
Canvas.FillRect(Rect);
Canvas.TextOut(Rect.Left+2,Rect.Top,Items[index]);
end;
end;
|
Рисование квадрата мышкой.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
private
{ Private declarations }
AnchorX, AnchorY,
CurX, CurY: Integer;
Bounding: Boolean;
end;
implementation
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
AnchorX := X;
CurX := X;
AnchorY := Y;
CurY := Y;
Bounding := True;
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if Bounding then
begin
Canvas.Pen.Mode := pmNot;
Canvas.Pen.Width := 2;
Canvas.Brush.Style := bsClear;
Canvas.Rectangle(AnchorX, AnchorY, CurX, CurY);
CurX := X;
CurY := Y;
Canvas.Rectangle(AnchorX, AnchorY, CurX, CurY);
end;
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Bounding then
begin
Bounding := False;
Canvas.Pen.Mode := pmNot;
Canvas.Brush.Style := bsClear;
Canvas.Rectangle(AnchorX, AnchorY, CurX, CurY);
end;
end;
Как рисовать линии (или ещё что-нибудь) на экране (TDesktopCanvas).

// Пример рисует две горизонтальные линии на экране используя TDesktopCanvas.
program TrinitronTraining;
uses
Messages, Windows, Graphics, Forms;
type
TDesktopCanvas = class(TCanvas)
private
DC : hDC;
function GetWidth:Integer;
function GetHeight:Integer;
public
constructor Create;
destructor Destroy; override;
published
property Width: Integer read GetWidth;
property Height: Integer read GetHeight;
end;
{ Объект TDesktopCanvas }
function TDesktopCanvas.GetWidth:Integer;
begin
Result:=GetDeviceCaps(Handle,HORZRES);
end;
function TDesktopCanvas.GetHeight:Integer;
begin
Result:=GetDeviceCaps(Handle,VERTRES);
end;
constructor TDesktopCanvas.Create;
begin
inherited Create;
DC := GetDC(0);
Handle := DC;
end;
destructor TDesktopCanvas.Destroy;
begin
Handle := 0;
ReleaseDC(0, DC);
inherited Destroy;
end;
const
YCount = 2;
var
desktop : TDesktopCanvas;
dx,dy : Integer;
i : Integer;
F : array[1..YCount] of TForm;
function CreateLine(Y : Integer) : TForm;
begin
Result := TForm.Create(Application);
with Result do begin
Left := 0;
Top := y;
Width := dx;
Height := 1;
BorderStyle := bsNone;
FormStyle := fsStayOnTop;
Visible := True;
end;
end;
procedure ProcessMessage;
var
Msg : TMsg;
begin
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
if Msg.message = WM_QUIT then
Application.Terminate;
end;
begin
desktop := TDesktopCanvas.Create;
try
dx := desktop.Width;
dy := desktop.Height div (YCount+1);
finally
desktop.free;
end;
for i:=1 to YCount do
F[i]:=CreateLine(i*dy);
Application.NormalizeTopMosts;
ShowWindow(Application.Handle, SW_Hide);
for i:=1 to YCount do
SetWindowPos(F[i].Handle, HWND_TOPMOST, 0,0,0,0, SWP_NOACTIVATE+SWP_NOMOVE+SWP_NOSIZE);
{
следующие строки используются для того, чтобы не останавливаться
repeat
ProcessMessage;
until false;
}
Sleep(15000);
for i:=1 to YCount do
F[i].Free;
end.
|
Можно ли рисовать на рамке формы.
Обрабатывайте событие WM_NCPAINT. В примере рамка обводится красной линией толщиной в 1 пиксель.
В частных объявлениях объявляем процедуру обработки сообщение WMNCPaint:
private
{ Private declarations }
procedure WMNCPaint(var Msg : TWMNCPaint); message WM_NCPAINT;
|
В области реализации [после слова implemantation] пишем:
procedure TForm1.WMNCPaint(var Msg: TWMNCPaint); var dc: hDc; Pen: hPen; OldPen: hPen; OldBrush: hBrush; begin inherited; dc := GetWindowDC(Handle); msg.Result := 1; Pen := CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); OldPen := SelectObject(dc, Pen); OldBrush := SelectObject(dc, GetStockObject(NULL_BRUSH)); Rectangle(dc, 0,0, Form1.Width, Form1.Height); SelectObject(dc, OldBrush); SelectObject(dc, OldPen); DeleteObject(Pen); ReleaseDC(Handle, Canvas.Handle); end; |
Рисование на минимизированной иконке.
Автор: Nick Hodges (Monterey, CA) Есть ли у кого пример рисования на иконке минимизированного приложения с помощью Delphi?Когда Delphi-приложение минимизировано, иконка, которая вы видите - реальное главное окно, объект TApplication, поэтому вам необходимо использовать переменную Application. Таким образом, чтобы удостовериться что приложение минимизировано, вызовите IsIconic(Application.Handle). Если функция возвратит True, значит так оно и есть. Для рисования на иконке создайте обработчик события Application.OnMessage. Здесь вы можете проверять наличие сообщения WM_Paint и при его нахождении отрисовывать иконку. Это должно выглядеть приблизительно так:
...
{ private declarations }
procedure AppOnMessage(var Msg: TMsg; var Handled: Boolean);
...
procedure TForm1.AppOnMessage(var Msg: TMsg; var Handled: Boolean);
var
DC: hDC;
PS: TPaintStuff;
begin
if (Msg.Message = WM_PAINT) and IsIconic(Application.Handle) then
begin
DC := BeginPaint(Application.Handle, PS);
...осуществляем отрисовку с помощью вызовов Windows GDI...
EndPaint(Application.Handle, PS);
Handled := True;
end;
end;
procedure TForm1.OnCreate(Sender: TObject);
begin
Application.OnMessage := AppOnMessage;
end;
|
Код создан на основе алгоритма Neil Rubenking.
Можно ли из Delphi рисовать в любой части экрана или в чужом окне.
Автор: Зайцев О.В. WEB-сайт: http://www.sources.ruДля этого надо воспользоваться функциями API. Получить контекст чужого окна, либо всего экрана:
function GetDC(Wnd: HWnd): HDC;
Где Wnd - указатель на нужное окно, или 0 для получения контекста всего экрана. И далее, пользуясь функциями API, нарисовать все что надо.
procedure DrawOnScreen;
var
ScreenDC: hDC;
begin
ScreenDC := GetDC(0); {получить контекст экрана}
Ellipse(ScreenDC, 0, 0, 200, 200); {нарисовать}
ReleaseDC(0, ScreenDC); {освободить контекст}
end;
Не забывайте после своих манипуляций посылать пострадавшим (или всем) окнам сообщение о необходимости перерисовки, для восстановления их первоначального вида.
Как рисовать на органе управления, например, на TPanel.
У всех компонентов, порожденных от TCustomControl, имеется свойство Canvas типа TCanvas. Грубо говоря, это аналог TDC из OWL. Те операции, которые нельзя выполнить с помощью методов TCanvas, можно выполнить с помощью WinAPI. Для этого у обьектов класса TCanvas имеется свойство Handle - это и есть Хэндл Дисплейного Контекста ОС Windows (HDC), который необходим графическим функциям WinAPI. Если свойство Canvas недоступно, Вы можете достучаться до него созданием потомка и переносом этого свойства в раздел Public.
{ Example. We recommend You to create this component
through Component Wizard. In Delphi 1 it can be found
as 'File|New Component...', and can be found as
'Component|New Component...' in Delphi 2 or above. }
type
TcPanel = class(TPanel)
public
property Canvas;
end;
|
А если у объекта нет свойства Canvas (у TDBEdit, вроде-бы нет), по крайней мере в D3 можно использовать класс TControlCanvas. Примерное использование:
var cc: TControlCanvas; ... cc := TControlCanvas.Create; cc.Control := youControl; ... |
И далее как обычно можно использовать методы Canvas.
Вывод текста на родительском элементе управления.
Свойство Canvas в TCustomControl существует, но оно защищено. Поскольку свойство canvas инкапсулирует windows HDC (Canvas.Handle), вы можете создавать объект TCanvas и назначать через свойство Handle контекст устройства элементу управления, на котором вы хотите рисовать.Для примера:
procedure AControl.DrawLabel(ACaption: TCaption);
var
ACanvas: TCanvas;
DC: HDC;
begin
ACanvas := TCanvas.Create;
try
WindowHandle := parent.Handle;
DC := GetDeviceContext(WindowHandle);
ACanvas.Handle := DC;
with ACanvas do
begin
end;
ACanvas.Handle := 0;
ReleaseDC(WindowHandle, DC);
finally
ACanvas.free;
end;
end;
|
Как рисовать на экране.
Обладая такими способностями, вы сможете, например, разлиновать поверхность экрана как в тетради в клеточку, выводить пугающие пользователя надписи и даже создать эффект окаменение экрана, если, конечно, разработаете алгоритм выполнения данной задачи.
Я покажу как рисовать на экране на примере разлиновки:
Сначала объявите глобальную переменную.
Scr: TCanvas; |
Затем по событию OnCreate() для формы напишите такой код:
Scr := TCanvas.Create; Scr.Handle := GetDC(HWND_DESKTOP); |
По событию OnDestroy() такой:
Scr.Free; |
Обработчик события по нажатию на кнопку пусть выглядит так:
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
begin
i := 0;
while i do
begin
with Scr do
begin
MoveTo(i, 0);
LineTo(i, 768);
i := i + 10;
end;
end;
i := 0;
while i do
begin
with Scr do
begin
MoveTo(0, i);
LineTo(1024, i);
i := i + 10;
end;
end;
Button1.Refresh;
end;
|
Как нарисовать что-нибудь на TMemo.

| Опять-таки... составляю тут резюме... первым делом, есессно, натыкаюсь на пункт "ФИО" и пытаюсь ввести свой ник... затем все же заставляю себя заполнить данный пункт, несколько ошалело глядя на собственные _реальные_ фамилию и имя... настолько привыкнув за время пребывания в Сети к осторожности скрытности, что в первый момент жутко ругаю себя, что что ж это я так уже себя не контролирую... При достижении пунта "e-mail" и не менее послушном заполнении ужас просто-таки начинает раздирать - как - я - оставляю свое мыло... недремлющий спаммер... через некоторое время все-таки приходит осознание ситуации и понимание необходимости ввести свой пол и возраст, а также телефон :))), ...который чудом не был заменен номером Аськи... |
Для рисования на поверхности TMemo необходимо создать создать собственный компонент, наследованный от TMemo и переопределить в нём рисование. Примерно так:
type
TMyMemo = class(TMemo)
protected
procedure WMPaint(var message: TWMPaint); message WM_PAINT;
end;
|
А теперь добавьте реализацию этой процедуры:
procedure TMyMemo.WMPaint(var message: TWMPaint);
var
MCanvas: TControlCanvas;
DrawBounds : TRect;
begin
inherited;
MCanvas:=TControlCanvas.Create;
// Работаем с временной записью TRect.
DrawBounds := ClientRect;
try
MCanvas.Control:=Self;
with MCanvas do
begin
Brush.Color := clBtnFace;
FrameRect( DrawBounds );
InflateRect( DrawBounds, -1, -1);
FrameRect( DrawBounds );
FillRect ( DrawBounds );
MoveTo ( 33, 0 );
Brush.Color := clWhite;
LineTo ( 33, ClientHeight );
PaintImages;
end;
finally
MCanvas.Free;
end;
end;
|
Процедура PaintImages рисует картинки на канвасе Memo.
procedure TMyMemo.PaintImages;
var
MCanvas: TControlCanvas;
DrawBounds : TRect;
i, j : Integer;
OriginalRegion : HRGN;
ControlDC : HDC;
begin
MCanvas:=TControlCanvas.Create;
// Работаем с временной записью TRect.
DrawBounds := ClientRect;
try
MCanvas.Control:=Self;
ControlDC := GetDC(Handle);
MCanvas.Draw(0, 1, Application.Icon);
finally
MCanvas.Free;
end;
end;
|
Теперь мы имеем собственноручно нарисованный memo.
Рисование прямоугольника на изображении.

| Билл Гейтс помер и попал на небеса, а там вместо апостола Петра машину с Windows поставили. И, значит, в окне на мониторе надпись: "Уважаемый Билл, за Ваши заслуги перед человечеством мы разрешаем Вам самому выбрать, куда Вы попадете - в ад или в рай Выбирайте - в ад или в рай?". И ниже три кнопки с вариантами ответов: "Yes, No,Cancel"... |
Создайте логическое поле ImageMouse, для рисования прямоугольника ImageRect используйте метод холста drawfocusrect. Следующий код работает одинаково для левой и правой кнопок мыши:
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
ImageMouse := True;
ImageRect.Left := X;
ImageRect.Top := Y;
ImageRect.Right := X;
ImageRect.Bottom := Y;
Image1.Canvas.DrawFocusRect(ImageRect);
end;
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y:
Integer);
var
NewRect: tRect;
begin
if ImageMouse then
if (X > ImageRect.Left) and (Y > ImageRect.Top) then
begin
{Восстанавливаем фон}
Image1.Canvas.DrawFocusRect(ImageRect);
{Меняем прямоугольник}
ImageRect.Right := X;
ImageRect.Bottom := Y;
{Рисуем прямоугольник фокуса}
Image1.Canvas.DrawFocusRect(ImageRect);
end;
end;
procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
{Восстанавливаем фон}
if ImageMouse then
begin
ImageRect.Right := X;
ImageRect.Bottom := Y;
Image1.Canvas.DrawFocusRect(ImageRect);
ImageMouse := False;
Image1.Canvas.CopyRect(Image1.Canvas.ClipRect,
Image1.Canvas, ImageRect)
end;
end;
|
Как отобразить выбранную строку DBGrid различными цветами.
| Разговаривают два программиста: - А что он собой представляет? - Очень общительный и доброжелательный, даже не скажешь, что программист. |
Если Вы хотите раскрасить выбранную строку DBGrid, но не хотите использовать опцию dgRowSelect, так как хотели бы редактировать данные, то можно воспользоваться следующей технологией в событии DBGrid.OnDrawColumnCell:
type
TCustomDBGridCracker = class(TCustomDBGrid);
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
begin
with Cracker(Sender) do
if DataLink.ActiveRecord = Row - 1 then
Canvas.Brush.Color := clRed
else
Canvas.Brush.Color := clWhite;
DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;
|
Как быстро нарисовать тень в заданном регионе.
procedure TForm2.DrawShadows(WDepth, HDepth : Integer); var Dst, RgnBox: TRect; hOldDC: HDC; OffScreen: TBitmap; Pattern: TBitmap; Bits: array [0..7] of WORD; begin Bits[0]:=$0055; Bits[1]:=$00aa; Bits[2]:=$0055; Bits[3]:=$00aa; Bits[4]:=$0055; Bits[5]:=$00aa; Bits[6]:=$0055; Bits[7]:=$00aa; hOldDC:=Canvas.Handle; Canvas.Handle:=GetWindowDC(Form1.Handle); OffsetRgn(ShadeRgn, WDepth, HDepth); GetRgnBox(ShadeRgn, RgnBox); Pattern:=TBitmap.Create; Pattern.ReleaseHandle; Pattern.Handle:=CreateBitmap(8, 8, 1, 1, @(Bits[0])); Canvas.Brush.Bitmap:=Pattern; OffScreen:=TBitmap.Create; OffScreen.Width:=RgnBox.Right-RgnBox.Left; OffScreen.Height:=RgnBox.Bottom-RgnBox.Top; Dst:=Rect(0, 0, OffScreen.Width, OffScreen.Height); OffsetRgn(ShadeRgn, 0, -RgnBox.Top); FillRgn(OffScreen.Canvas.Handle, ShadeRgn, Canvas.Brush.Handle); OffsetRgn(ShadeRgn, 0, RgnBox.Top); // BitBlt работает быстрее CopyRect BitBlt(OffScreen.Canvas.Handle, 0, 0, OffScreen.Width, OffScreen.Height, Canvas.Handle, RgnBox.Left, RgnBox.Top, SRCAND); Canvas.Brush.Color:=clBlack; FillRgn(Canvas.Handle, ShadeRgn, Canvas.Brush.Handle); BitBlt(Canvas.Handle, RgnBox.Left, RgnBox.Top, OffScreen.Width, OffScreen.Height, OffScreen.Canvas.Handle, 0, 0, SRCPAINT); OffScreen.Free; Pattern.Free; OffsetRgn(ShadeRgn, -WDepth, -HDepth); ReleaseDC(Form1.Handle, Canvas.Handle); Canvas.Handle:=hOldDC; end; |
Комментарии: Функция рисует тень сложной формы на форме Form2 (извиняюсь за стиль). Для определения формы тени используется регион ShadeRgn, который был создан где-то раньше (например в OnCreate). Относительно регионов см. Win32 API.
Изобразить эффект разбрызгивания.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure Spray(Canvas: TCanvas; x, y, r: Integer; Color: TColor);
var
rad, a: Single;
i: Integer;
begin
for i := 0 to 100 do
begin
a := Random * 2 * pi;
rad := Random * r;
Canvas.Pixels[x + Round(rad * Cos(a)), y + Round(rad * Sin(a))] := Color;
end;
end;
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if ssLeft in Shift then Spray(Image1.Canvas, x, y, 40, clRed);
end;
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if ssLeft in Shift then Spray(Image1.Canvas, x, y, 40, clRed);
end;
Отображение текста с тегами форматирования.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Отображение текста с тегами форматирования
Рисует строку текста, содержащую теги форматирования, походие на теги HTML.
Поддерживаются следующие теги:
<b>..</b> Полужирный
<i>..</i> Наклонный
<u>..</u> Подчёркнутый
<s>..</s> Перечёркнутый
<big[ n]>..</big> Увеличить ращмер шрифта на n единиц (по умолчанию 1)
<small[ n]>..</small> Уменьшить шрифт на n единиц (по умолчанию 1)
<sub>..</sub> Нижний индекс
<sup>..</sup> Верхний индекс
Для правильного отображения внутри тегов <sub> и <sup> не должно располагаться других тегов
<font name="Имя шрифта" size="Размер" color="Цвет" charset="Кодовая страница">..</font>
Установка параметров шрифта.
Размер шрифта указывается как для свойства TFont.Size, а не как в HTML.
В качестве цвета можно указывать либо константы clXXXX либо числа в формате #RRGGBB,
где RR GG и ВВ соответственно шестнадцатеричные значения 00..FF красной,
зелёной и синей составляющей, HTML цвета не поддерживаются. В параметре charset
указываются константы XXXX_CHARSET, например RUSSIAN_CHARSET или ANSI_CHARSET
Зависимости: Windows, SysUtils, Classes, Graphics, Dim
Автор: Dimka Maslov, mainbox@endimus.com, ICQ:148442121, Санкт-Петербург
Copyright: Dimka Maslov
Дата: 17 марта 2004 г.
***************************************************** }
unit HtDraw;
interface
uses Windows, SysUtils, Classes, Graphics, Dim;
type
TTag = class;
THtDraw = class(TObject)
private
FTag: TTag;
FText: TString;
procedure SetText(Value: TString);
public
function Draw(Canvas: TCanvas; X, Y: Integer): Integer;
// Canvas - устройство для отображения текста.
// в свойстве Canvas.Font задаются начальные параметры шрифта
// X, Y - начальные координаты
// Функция возвращает сумму X и ширины выведенного текста
property Text: TString read FText write SetText;
// Задаёт текст для отображения. В отличие от свойств компонентов,
// присвоение значения этого свойства не приводит к автоматической
// перерисовке. Необходимо вызывать метод Draw
constructor Create(const AText: TString);
destructor Destroy; override;
end;
Полный текст модулей располагается по адресам:
HtDraw: http://downloads.endimus.com/htdraw.zip
Dim : http://downloads.endimus.com/dimpas.zip
Пример использования:
var
HtDraw: THtDraw;
begin
HtDraw :=
THtDraw.Create('<b>T</b><i>e</i><u>s</u><s>t</s>');
try
HtDraw.Draw(Canvas, 10, 10);
finally
HtDraw.Free;
end;
end;
Ввыводим текст и графику, используя DelphiX.
| Встречаются два квакера. - Ну как жизнь? - Да так, процентов на тридцать. |
Итак, цель нашего первого урока - вывести текст и графику. Сначала определимся, куда же пихать эти самые текст и графику. Сначала поставьте на форму DXDraw, при этом в свойствах, в разделе "Options" измените "doFullScreen" и "doFlip" на "True", чтобы при запуске программа заполняла весь экран. Запустите и проверьте. Если вы видите черный экран, все нормально. Выходить придется Ctrl-Alt-Dеl'ом, так как процедуры выхода из программы мы еще не написали. "Рабочий стол" готов.
Теперь нарисуйте какую-нибудь картинку, по размерам экрана. Я возьму "Космос"( черный экран и белые точки, рисуется распылителем в Paint'e), сохраняем, как "cosmos.bmp". Вроде этого:
Теперь нарисуем то, что должно будет двинаться по "космосу" в следующих уроках, а пока это самое будет просто отображаться. Я опять же нарисую в Раint'у летающую тарелку(жирный овал,"tarelka.bmp"), вроде этого:
Копируем эти рисунки, в папку с Дельфи-проектом. Ставим на форму DXSpriteEngine и DXImageList. Выбираем в их свойстве "DXDraw" "DXDraw1", уже сконфигурированный нами в предыдущем абзаце. В свойствах Имаг-листа(DXImageList) выбираем "Items". В раскрывшемся окне создаем два объекта, назовем их "cosmos" и "tarelka", в свойстве "Picture" укажем путь к "cosmos.bmp" и "tarelka.bmp" соответственно.
Теперь самое сложное: поскольку ДельфИкс создавал какой-то, хоть и очень умный, но один человек, а не корпорация, нам придется ВРУЧНУЮ прописывать класс спрайтов(рисунков). Открываем редактор кода, в части "Implementation", после "Var" пишем "type" и с новой строчки:
TCosmos = class(TImageSprite) public constructor Create(AParent: TSprite); override; end; TTarelka = class(TImageSprite) public constructor Create(AParent: TSprite); override; end; |
Разбираемся: "Tcosmos" - имя будущего класса, "Class(TImageSprite)" - указывает, что будущий класс относится к ImаgеSрritе'ам, "Public" - указывает, что следующий раздел глобальный, "Constructor" - указывает, что следующая процедура относится к подклассу конструкторов, "Create(AParent: TSprite); Override;" - надо заучить как молитву, т.к. эта процедурка будет создавать ВСЕ спрайты в ваших программах, "End;" обозначает, что описание класса завершено.
Но процедуры мало Описать, их надо ещё и ПРОписать. В этом же разделе, после описания всех классов, пишем:
constructor Tcosmos.Create(AParent: TSprite);
begin
inherited Create(AParent);
Image := Form1.ImageList1.Items.Find('cosmos');
X := x;
Y := x;
Z := z;
Width := Image.Width;
Height := Image.Height;
PixelCheck := True;
end;
|
Учить точно так же, как и описание этого самого конструктора. Изменятся будут только подчеркнутые элементы. "Form1.ImageList1.Items.Find('cosmos')" - путь к спрайту "cosmos", меняться будут толко имена спрайтов. "x" - координата по горизонтали, у всех фоновых рисунков равна единице. "y" - координата по вертикали, у всех фоновых рисунков также равна единице. "z" - глубина рисунка, при наслоении двух спрайтов отображается тот, у которого "z" больше, у всех фоновых рисунков равна минус единице. Также пишем и тарелку:
constructor Ttarelka.Create(AParent: TSprite);
begin
inherited Create(AParent);
Image := Form1.ImageList1.Items.Find('tarelka');
X := 50;
Y := 50;
Z := 1;
Width := Image.Width;
Height := Image.Height;
PixelCheck := True;
end;
|
Как видите, изменилась только подчеркнутая часть. Итак, описания закончены.
Теперь перейдем непосредственно к выводу на экран. Создаем DXTimer, в событие onTimer пишем:
if not DXDraw.CanDraw then exit; DXDraw.Surface.Fill(0); SpriteEngine.Move(1000 div 10); SpriteEngine.Dead; SpriteEngine.Draw; with DXDraw.Surface.Canvas do begin Brush.Style := bsClear; Font.Color := clWhite; Font.Size := 8; Textout(20, 3, 'Так выводится текст под ДельфИкс); Release; End; DXDraw.Flip; |
Разбираем: а разбирать тут и нечего, учить надо, это процедура инициализации DirectX! Второй абзац - процедура вывода текста, последняя строка отрисовывает все объекты. Теперь смотрив события формы и создаем обработчик события onCreate. Пишем:
Ttarelka.create(SpriteEngine.Engine); Tcosmos.create(SpriteEngine.Engine); |
Запускаем, и видим тарелку на звездном фоне!
Если не видим, то тут три возможности:
обратиться к офтальмологу включить монитор проверить формат рисунков: ДельфИкс поддерживает только .bmp и .dib, если у вас рисунок другого формата, нужно пойти на хитрость: в Picture Editor'e надо сначала нажать "copy", а потом "paste", тогда оба рисунка сконвертируются в формат .bmpГотовую программу можно скачать здесь или на сайте автора http://instrumentari.narod.ru и свериться, если что-то не работает.
Рисование текста в DBGrid.
| Решение всех жизненных проблем находится на интернете. Надо только уметь хорошо искать. |
Следующий метод может быть использован в качестве обработчика события TDBGrid.OnDrawDataCell. Он демонстрирует способ рисования текста в колонке цветом, отличным от цвета текста в остальной части табличной сетки.
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect:
TRect; Field: TField; State: TGridDrawState);
{ ПРИМЕЧАНИЕ: Свойство DefaultDrawing компонента
Grid должно быть установлено в False }
begin
{ если имя поля - "NAME" }
if Field.FieldName = 'NAME' then
{ изменяем цвет шрифта на красный }
(Sender as TDBGrid).Canvas.Font.Color := clRed;
{ выводим текст в табличной сетке }
(Sender as TDBGrid).Canvas.TextRect(Rect, Rect.Left + 2,
Rect.Top + 2, Field.AsString);
end;
|
Рисование текста в DBGrid 2.
Автор: Eryk Для отображения таблицы я использую DBGrid. Для некоторых полей я хочу применить другой шрифт, размер, цвет...Вам необходимо обработать событие OnDrawDataCell, например так:
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const
Rect: TRect; Field: TField; State: TGridDrawState);
begin
if Field.FieldName = 'SERIAL' then
if (Field as TStringField).Value = 'НЕИЗВЕСТНО' then
with (Sender as TDBGrid).Canvas do
begin
Brush.Color := clRed;
Font.Style := [fsItalic];
Font.Color := clAqua;
FillRect(Rect);
TextOut(Rect.Left, Rect.Top, Field.AsString);
end;
end;
|
....BTW, выключите DefaultDrawing.
Вывод текста на канве картинки.
При использовании BitBtn Caption(текст) и картинка(bitmap) из файла не видны одновременно. Почему?
Это может происходить если картинка слишком велика. Класс TBitBtn сначала рисует картинку, а затем выводит текст над, под, слева или справа от картинки (в завивимости от свойства Layout). Если размер картинки такой же как у всей кнопки для вывода текста просто не остается места. Если Вам нужно получить кнопку такого же размера как Ваша картинка и видеть при этом надпись на кнопке Вам придется выводить текст надписи непосредственно на канву картинки.
var bm : TBitmap; OldBkMode : integer; begin bm := TBitmap.Create; bm.Width := BitBtn1.Glyph.Width; bm.Height := BitBtn1.Glyph.Height; bm.Canvas.Draw(0, 0, BitBtn1.Glyph); OldBkMode := SetBkMode(bm.Canvas.Handle, Transparent); bm.Canvas.TextOut(0, 0, 'The Caption'); SetBkMode(bm.Canvas.Handle, OldBkMode); BitBtn1.Glyph.Assign(bm); end; |
Как нарисовать Bitmap с прозрачностью.
procedure DrawTransparentBmp(Cnv: TCanvas; x,y: Integer; Bmp: TBitmap; clTransparent: TColor);
var
bmpXOR, bmpAND, bmpINVAND, bmpTarget: TBitmap;
oldcol: Longint;
begin
try
bmpAND := TBitmap.Create;
bmpAND.Width := Bmp.Width;
bmpAND.Height := Bmp.Height;
bmpAND.Monochrome := True;
oldcol := SetBkColor(Bmp.Canvas.Handle, ColorToRGB(clTransparent));
BitBlt(bmpAND.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, Bmp.Canvas.Handle, 0, 0, SRCCOPY);
SetBkColor(Bmp.Canvas.Handle, oldcol);
bmpINVAND := TBitmap.Create;
bmpINVAND.Width := Bmp.Width;
bmpINVAND.Height := Bmp.Height;
bmpINVAND.Monochrome := True;
BitBlt(bmpINVAND.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, bmpAND.Canvas.Handle, 0, 0, NOTSRCCOPY);
bmpXOR := TBitmap.Create;
bmpXOR.Width := Bmp.Width;
bmpXOR.Height := Bmp.Height;
BitBlt(bmpXOR.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, Bmp.Canvas.Handle, 0, 0, SRCCOPY);
BitBlt(bmpXOR.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, bmpINVAND.Canvas.Handle, 0, 0, SRCAND);
bmpTarget := TBitmap.Create;
bmpTarget.Width := Bmp.Width;
bmpTarget.Height := Bmp.Height;
BitBlt(bmpTarget.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, Cnv.Handle, x, y, SRCCOPY);
BitBlt(bmpTarget.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, bmpAND.Canvas.Handle, 0, 0, SRCAND);
BitBlt(bmpTarget.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, bmpXOR.Canvas.Handle, 0, 0, SRCINVERT);
BitBlt(Cnv.Handle, x, y, Bmp.Width, Bmp.Height, bmpTarget.Canvas.Handle, 0, 0, SRCCOPY);
finally
bmpXOR.Free;
bmpAND.Free;
bmpINVAND.Free;
bmpTarget.Free;
end;
end;
|
Как с помощью функции Textout вывести на канве прозрачный текст.
Вот небольшой участок кода из купленного мною CD-ROM "How To Book". Файл с именем "HowUtils.Pas" содержит реализацию алгоритма "потухания" текста и обратного ему эффекта на холсте, откуда вы можете почерпнуть необходимую вам информацию.
function TFadeEffect.FadeInText(Target: TCanvas; X, Y: integer; FText: string): TRect; var Pic: TBitmap; W, H: integer; PicRect, TarRect: TRect; begin Pic := TBitmap.Create; Pic.Canvas.Font := Target.Font; W := Pic.Canvas.TextWidth(FText); H := Pic.Canvas.TextHeight(FText); Pic.Width := W; Pic.Height := H; PicRect := Rect(0, 0, W, H); TarRect := Rect(X, Y, X + W, Y + H); Pic.Canvas.CopyRect(PicRect, Target, TarRect); SetBkMode(Pic.Canvas.Handle, Transparent); Pic.Canvas.TextOut(0, 0, FText); FadeInto(Target, X, Y, Pic); Pic.Free; FadeInText := TarRect; end; procedure TFadeEffect.FadeOutText(Target: TCanvas; TarRect: TRect; Orig: TBitmap); var Pic: TBitmap; PicRect: TRect; begin Pic := TBitmap.Create; Pic.Width := TarRect.Right - TarRect.Left; Pic.Height := TarRect.Bottom - TarRect.Top; PicRect := Rect(0, 0, Pic.Width, Pic.Height); Pic.Canvas.CopyRect(PicRect, Orig.Canvas, TarRect); FadeInto(Target, TarRect.Left, TarRect.Top, Pic); Pic.Free; end; |
Драйвера на Delphi.
Автор: Павел Компиляция данного примера возможна только с Delphi 3. Delphi 2 не был опробован в связи с его отсутствием, объектные фалы созданные Delphi 4 отвергаются Microsoft® Linker 5.12.8181 как файлы неизвестного формата. При написании данного материала были использованы Microsoft® Macro Assembler ver. 6.11d и Microsoft® Incremental Linker ver. 5.12.8181 из поставки Windows 98DDK Введение VxD драйвера делятся два типа: Статические Загружаются в память при загрузке операционной системы и присутствуют в памяти постоянно Динамические Загружаются в память при первом обращении к драйверу и могут быть выгружены при закрытии последнего дескриптора VxD. Практически возможно создание драйвера поддерживающего оба типа загрузки. Нам необходим динамически загружаемый VxD драйвер (далее "VxD") т.к. такой драйвер можно будет без перезагрузки Windows загружать из Win32® приложений используя процедуру CreateFile(). Когда Win32 приложение открывает дескриптор для VxD, виртуальное устройство VWIN32 используя сервис LoadDevice загружает VxD-драйвер в память и посылает информационное сообщение W32_DEVICEIOCONTROL загруженному VxD. Таким образом для построения VxD необходимо обрабатывать как минимум три системных сообщения: SYS_DYNAMIC_DEVICE_INIT SYS_DYNAMIC_DEVICE_EXIT W32_DEVICEIOCONTROL. Собщение SYS_DYNAMIC_DEVICE_INIT посылается при попытке динамической загрузки VxD, SYS_DYNAMIC_DEVICE_EXIT посылается при попытке динамической выгрузке. Из обработчиков сообщений для подтверждения успеха необходимо вернуть VXD_SUCCESS в регистре AX Сообщение W32_DEVICEIOCONTROL имеет следующие значения для параметра dwService DIOC_OPEN - посылается посылается при открытии дескриптора VxD функцией CreateFile() только после SYS_DYNAMIC_DEVICE_INIT. В случае успеха необходимо вернуть NO_ERROR (0); DIOC_CLOSEHANDLE - посылается при закрытии дескриптора VxD функцией API CloseHandle() и только перед SYS_DYNAMIC_DEVICE_EXIT (Значение > 0) - Номер функции, заданный в параметре dwIoControlCode при обращении к VxD функцией API DeviceIoControl Загрузочный модуль (vxdmain.asm)При обращении к процедурам находящимся в модулях Delphi надо учесть для fastcall-процедур к имени добавляется в начале символ "@".
... extrn SysDynamicDeviceInit :PROC extrn SysDynamicDeviceExit :PROC extrn W32DeviceIoControl :PROC ... Control_0 proc cmp eax, SYS_DYNAMIC_DEVICE_INIT jnz short chkSysDynExit call SysDynamicDeviceInit cmp eax, 1 retn ;------------- chkSysDynExit: cmp eax, SYS_DYNAMIC_DEVICE_EXIT jnz short chkDevIOCtl call SysDynamicDeviceExit cmp eax, 1 retn ;------------- chkDevIOCtl: cmp eax, W32_DEVICEIOCONTROL jnz short loc_ret push esi push edx push ebx push ecx call W32DeviceIoControl cmp eax, 1 retn ;------------- loc_ret: clc retn Control_0 endp ...
Delphi создает код для инициализации/деинициализации модулей, обращаясь к внешним процедурам HandleFinaly и initialization даже если блоки initilization и finalization в модуле отсутствуют. Создадим пустую "заглушку" для этих процедур и объявим их доступными для внешних модулей.
...
Public @@HandleFinally
Public @initialization
...
@@HandleFinally:
@initialization:
ret
...
end.
Процедурный модуль (vxdProcs.pas)
procedure ShellMessage(Handle, Flags: integer; const Message, Caption: PChar;
Callback, ReferenceData: pointer); stdcall; assembler;
asm
mov ebx, Handle // virtual machine handle
mov eax, Flags // message box flags
mov ecx, Message // address of message text
mov edi, Caption // address of caption text
mov esi, Callback // address of callback
mov edx, ReferenceData // reference data for callback
int 20H // VxDCall
dd 170004h // Shell_Message
end;
const
Copyright: PChar = '(c) 1999 Emil Biserov, ' +
'fatty777@mail.ru, http://dinfo.da.ru';
function SysDynamicDeviceInit: INTEGER;
begin
ShellMessage(0, $10, Copyright, 'SysDynInit: Hello from Delphi VxD !!!', nil,
nil);
Result := VXD_SUCCESS;
end;
function SysDynamicDeviceExit: INTEGER;
begin
ShellMessage(0, $10, Copyright, 'SysDynDevExit: Bye from Delphi VxD !!!', nil,
nil);
Result := VXD_SUCCESS;
end;
function W32DeviceIoControl(dwService: INTEGER;
dwDDB: INTEGER;
hDevice: INTEGER;
lpDIOCParms: pointer): INTEGER;
begin
ShellMessage(0, $10, Copyright, 'W32DevIOCtl', nil, nil);
if (dwService = DIOC_OPEN) then
begin
Result := NO_ERROR;
end
else if (dwService = DIOC_CLOSEHANDLE) then
begin
Result := VXD_SUCCESS;
end
else if (dwService > MAX_PASVXD_W32_API) then
begin
Result := ERROR_NOT_SUPPORTED;
end
else
begin
Result := VXD_SUCCESS;
end;
end;
Инструмент для загрузки/выгрузки VxD
Представляет из себя простую форму с двумя кнопками. Приведу лишь методы для открытия и закрытия VxD драйвера.
const
VxDName = '\\.\DELPHIIO.VXD';
...
function TVxDTestForm.OpenVxDDriver: boolean;
begin
HVxDHandle := CreateFile(VxDName, 0, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0);
Result := HVxDHandle <> INVALID_HANDLE_VALUE;
end;
procedure TVxDTestForm.CloseVxDDriver;
begin
if HVxDHandle <> INVALID_HANDLE_VALUE then
begin
CloseHandle(HVxDHandle);
HVxDHandle := INVALID_HANDLE_VALUE;
end;
end;
Выгрузку неиспользуемого модуля можно производить автоматически, указав в параметрах CreateFile(,,,,,FILE_FLAG_DELETE_ON_CLOSE,). В данном случае система при каждом открытии дескриптора к VxD будет увеличивать внутренний счетчик использования на 1 и вычитать 1 при закрытии дескриптора. При значении счетчика равном нулю VxD будет автоматически выгружен.
Выводы
Драйвера на Delphi писать можно. Но вот только стоит ли?
Использование VxD имеет смысл только для использования Windows 3.1 и Windows 95. Да и доступ к аппаратным ресурсам в них гораздо прозрачнее чем Windows NT/2000. Windows 98, Windows NT и Windows2000 поддерживают новую модель драйверов WDM 1.0/2.0, использующую формат PE (Portable Executable) и (предположительно) проблем будет меньше, чем с VxD и его LE (Linear Executable) форматом драйверов.
Получить список дисков и сведения о выбранном диске.
Автор: Xavier Pacheco
unit MainFrm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, FileCtrl;
type
TMainForm = class(TForm)
btnGetDriveTypes: TButton;
lbDrives: TListBox;
lblSectPerClust2: TLabel;
lblBytesPerSector2: TLabel;
lblNumFreeClusters2: TLabel;
lblTotalClusters2: TLabel;
lblSectPerCluster: TLabel;
lblBytesPerSector: TLabel;
lblNumFreeClust: TLabel;
lblTotalClusters: TLabel;
lblFreeSpace2: TLabel;
lblTotalDiskSpace2: TLabel;
lblFreeSpace: TLabel;
lblTotalDiskSpace: TLabel;
procedure btnGetDriveTypesClick(Sender: TObject);
procedure lbDrivesClick(Sender: TObject);
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TMainForm.btnGetDriveTypesClick(Sender: TObject);
var
i: Integer;
C: string;
DType: Integer;
DriveString: string;
begin
{ Loop from A..Z to determine available drives }
for i := 65 to 90 do
begin
// Format a string to represent the root directory.
C := chr(i) + ':\';
{ Call the GetDriveType() function which returns an integer
value representing one of the types shown in the case statement
below }
DType := GetDriveType(PChar(C));
{ Based on the drive type returned, format a string to add to
the listbox displaying the various drive types. }
case DType of
0: DriveString := C + ' The drive type cannot be determined.';
1: DriveString := C + ' The root directory does not exist.';
DRIVE_REMOVABLE: DriveString :=
C + ' The drive can be removed from the drive.';
DRIVE_FIXED: DriveString :=
C + ' The disk cannot be removed from the drive.';
DRIVE_REMOTE: DriveString :=
C + ' The drive is a remote (network) drive.';
DRIVE_CDROM: DriveString := C + ' The drive is a CD-ROM drive.';
DRIVE_RAMDISK: DriveString := C + ' The drive is a RAM disk.';
end;
// Only add drive types that can be determined.
if not ((DType = 0) or (DType = 1)) then
lbDrives.Items.AddObject(DriveString, Pointer(i));
end;
end;
procedure TMainForm.lbDrivesClick(Sender: TObject);
var
RootPath: string; // Holds the drive root path
SectorsPerCluster: DWord; // Sectors per cluster
BytesPerSector: DWord; // Bytes per sector
NumFreeClusters: DWord; // Number of free clusters
TotalClusters: DWord; // Total clusters
DriveByte: Byte; // Drive byte value
FreeSpace: Int64; // Free space on drive
TotalSpace: Int64; // Total drive space.
begin
with lbDrives do
begin
{ Convert the ascii value for the drive letter to a valid drive number:
1 = A, 2 = B, etc. by subtracting 64 from the ascii value. }
DriveByte := Integer(Items.Objects[ItemIndex]) - 64;
{ First create the root path string }
RootPath := chr(Integer(Items.Objects[ItemIndex])) + ':\';
{ Call GetDiskFreeSpace to obtain the drive information }
if GetDiskFreeSpace(PChar(RootPath), SectorsPerCluster,
BytesPerSector, NumFreeClusters, TotalClusters) then
begin
{ If this function is successful, then update the labels to
display the disk information. }
lblSectPerCluster.Caption := Format('%.0n', [SectorsPerCluster * 1.0]);
lblBytesPerSector.Caption := Format('%.0n', [BytesPerSector * 1.0]);
lblNumFreeClust.Caption := Format('%.0n', [NumFreeClusters * 1.0]);
lblTotalClusters.Caption := Format('%.0n', [TotalClusters * 1.0]);
// Obtain the available disk space
FreeSpace := DiskFree(DriveByte);
TotalSpace := DiskSize(DriveByte);
lblFreeSpace.Caption := Format('%.0n', [FreeSpace * 1.0]);
{ Calculate the total disk space }
lblTotalDiskSpace.Caption := Format('%.0n', [TotalSpace * 1.0]);
end
else
begin
{ Set labels to display nothing }
lblSectPerCluster.Caption := 'X';
lblBytesPerSector.Caption := 'X';
lblNumFreeClust.Caption := 'X';
lblTotalClusters.Caption := 'X';
lblFreeSpace.Caption := 'X';
lblTotalDiskSpace.Caption := 'X';
ShowMessage('Cannot get disk info');
end;
end;
end;
end.
Свой формат для TDateTimePicker.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
ComCtrls;
const
DTM_SETFORMAT = $1005;
procedure TForm1.Button1Click(Sender: TObject);
var
sFormat: string;
begin
DateTimePicker1.kind := dtkTime;
// Don't show the seconds, Sekunden nicht anzeigen
SendMessage(DateTimePicker1.Handle, DTM_SETFORMAT, 0, Longint(PChar('hh:mm')));
// To show AM/PM
SendMessage(DateTimePicker1.Handle, DTM_SETFORMAT, 0, Longint(PChar('hh:mm tt')));
// 24-hour clock: Be sure to set Kind to dtkTime
DateTime_SetFormat(DateTimePickerTest.Handle, pChar('H:mm:ss'));
// To show Date and Time, Datum und Zeit anzeigen
// Note that you can only edit the date or the time depending
// on the Kind (dtkTime or dtkDate).
SendMessage(DateTimePicker1.Handle, DTM_SETFORMAT, 0, Longint(PChar('dd/MM/yyyy hh:ss')));
// You could also use the DateTime_SetFormat macro:
DateTime_SetFormat(DateTimePicker1.Handle, PChar('M/d/yyy'));
// Instead of using the DateTime_SetFormat function, you can
// send a message to the control directly:
sFormat := 'dd-MMMM-yyyy';
DateTimePicker1.Perform(DTM_SETFORMAT, 0, DWORD(sFormat));
end;
// Show the Week Number in a TDateTimePicker
procedure DateToWeek(dtDate: TDateTime; var AWeek, AYear: Word);
const
FIRST_WEEKDAY: Integer = 2;
FIRST_WEEKDATE: Integer = 4;
var
wMonth, wDay: Word;
begin
dtDate := dtDate - ((DayOfWeek(dtDate) - FIRST_WEEKDAY + 7) mod 7) + 7 - FIRST_WEEKDATE;
DecodeDate(dtDate, AYear, wMonth, wDay);
AWeek := (Trunc(dtDate - EncodeDate(AYear, 1, 1)) div 7) + 1;
end;
procedure TForm1.DateTimePicker1Change(Sender: TObject);
var
sFormat: string;
wWeek, wYear: Word;
begin
DateToWeek(DateTimePicker1.date, wWeek, wYear);
sFormat := 'dd/MM/yy Week:' + IntToStr(wWeek) + '';
DateTimePicker1.Perform(DTM_SETFORMAT, 0, DWORD(sFormat));
end;
procedure TForm1.FormShow(Sender: TObject);
begin
DateTimePicker1Change(Self);
end;
Автоматически открыть TDateTimePicker.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
There's no possibility to automatically
open a TDateTimePicker than to send a F4 to it.}
{
Es gibt't keine Moglichkeit, einen TDateTimePicker automatisch zu offnen,
als F4 ihm zu senden
}
procedure TForm1.Button1Click(Sender: TObject);
begin
DateTimePicker1.Perform(WM_KEYDOWN, VK_F4, 0);
DateTimePicker1.Perform(WM_KEYUP, VK_F4, 0);
end;
Очистить дату в TDateTimePicker.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Use the DateTime_SetFormat API macro (put CommCtrl in the uses clause) to set
the date to blank or a message.
Then in the DateTimePicker OnCloseUp event handler set the format to what you
want when a date has been selected, and the DTP will show the selected date.
Attach an OnClick handler if you want it to open when you
click on the edit control.
}
{
Verwende das DateTime_SetFormat API Makro von der CommCtrl Unit, um den TDateTimePicker
anfangs leer anzuzeigen oder um einen beliebigen String zu setzen.
Wenn das OnCloseUp Ereignis vom TDateTimePicker aufgerufen wird, kannst du ein
beliebiges Datums-Format setzen und der DateTimePicker zeigt es dann an.
Damit man mit einem Click auf das Edit-Control den TDateTimePicker цffnеn kann, kann man
zиsдtz1iсh einen OnClick Handler hinzиfьgеn, wo man dann den TDateTimePicker цffnеt.
}
uses
CommCtrl;
var
FDTMDateEmpty: Boolean;
procedure TForm1.FormCreate(Sender: TObject);
begin
DateTime_SetFormat(DateTimePicker1.Handle, '''Choose a date''');
FDTMDateEmpty := True;
end;
procedure TForm1.DateTimePicker1CloseUp(Sender: TObject);
begin
DateTime_SetFormat(DateTimePicker1.Handle, PChar('dd.MM.yy'));
end;
procedure TForm1.DateTimePicker1Click(Sender: TObject);
begin
if FDTMDateEmpty then
begin
DateTimePicker1.Perform(WM_KEYDOWN, VK_F4, 0);
DateTimePicker1.Perform(WM_KEYUP, VK_F4, 0);
end;
end;
procedure TForm1.DateTimePicker1Change(Sender: TObject);
begin
FDTMDateEmpty := False;
end;
Удалить отображение текущей даты из TDateTimePicker.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
CommCtrl;
procedure TForm1.DateTimePicker1DropDown(Sender: TObject);
var
wnd: HWND;
Style: Integer;
begin
wnd := DateTime_GetMonthCal(DateTimePicker1.Handle);
if wnd <> 0 then
begin
Style := GetWindowLong(wnd, GWL_STYLE);
SetWindowLong(wnd, GWL_STYLE, Style or MCS_NOTODAY or MCS_NOTODAYCIRCLE);
end;
end;
{
The calendar will still highlite the current day but the circle and the
today display at the bottom are gone.
}
Показывать номера недели в TDateTimePicker.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
CommCtrl;
type
THackCommonCalendar = class(TCommonCalendar);
{...}
procedure TForm1.DateTimePicker1DropDown(Sender: TObject);
var
Style: Integer;
ReqRect: TRect;
MaxTodayWidth: Integer;
begin
with THackCommonCalendar(Sender as TDateTimePicker) do
begin
// set style to include week numbers
Style := GetWindowLong(CalendarHandle, GWL_STYLE);
SetWindowLong(CalendarHandle, GWL_STYLE, Style or MCS_WEEKNUMBERS);
FillChar(ReqRect, SizeOf(TRect), 0);
// get required rect
Win32Check(MonthCal_GetMinReqRect(CalendarHandle, ReqRect));
// get max today string width
MaxTodayWidth := MonthCal_GetMaxTodayWidth(CalendarHandle);
// adjust rect width to fit today string
if MaxTodayWidth > ReqRect.Right then
ReqRect.Right := MaxTodayWidth;
// set new height & width
SetWindowPos(CalendarHandle, 0, 0, 0, ReqRect.Right, ReqRect.Bottom,
SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOZORDER);
end;
end;
Получить или установить позицию пиктограмм рабочего стола.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// For Win9x:
//-------------------------------------------
uses
CommCtrl,
IPCThrd; (from your Delphi\Demos\Ipcdemos directory)
function GetDesktopListViewHandle: THandle;
var
S: String;
begin
Result := FindWindow('ProgMan', nil);
Result := GetWindow(Result, GW_CHILD);
Result := GetWindow(Result, GW_CHILD);
SetLength(S, 40);
GetClassName(Result, PChar(S), 39);
if PChar(S) <> 'SysListView32' then Result := 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
type
PInfo = ^TInfo;
TInfo = packed record
infoPoint: TPoint;
infoText: array[0..255] of Char;
infoItem: TLVItem;
infoFindInfo: TLVFindInfo;
end;
var
r : TRect;
hWnd : THandle;
i, iCount : Integer;
Info: PInfo;
SharedMem: TSharedMem;
begin
hWnd := GetDesktopWindow();
GetWindowRect(hWnd,r);
Memo.Lines.Add('Bottom: ' + IntToStr(r.Bottom));
Memo.Lines.Add('Right: ' + IntToStr(r.Right));
hWnd := GetDesktopListViewHandle;
iCount := ListView_GetItemCount(hWnd);
Memo.Lines.Add('# Icons: ' + IntToStr(iCount));
SharedMem := TSharedMem.Create('', SizeOf(TInfo));
Info := SharedMem.Buffer;
with Info^ do
try
infoItem.pszText := infoText;
infoItem.cchTextMax := 255;
infoItem.mask := LVIF_TEXT;
try
begin
for i := 0 to iCount - 1 do
begin
infoItem.iItem := i;
try
ListView_GetItem(hWnd, infoItem);
ListView_GetItemPosition(hWnd, I, infoPoint);
Memo.Lines.Add('Icon: ' + infoText);
Memo.Lines.Add(' X: ' + IntToStr(infoPoint.X));
Memo.Lines.Add(' Y: ' + IntToStr(infoPoint.Y));
except
end;
end;
end;
finally
end;
finally
SharedMem.Free;
end;
end;
// For NT, Win2k, XP:
//-------------------------------------------
// Unit to save/restore the positions of desktop icons to/from the registry)
unit dipsdef;
interface
uses
Windows, CommCtrl;
const
RegSubKeyName = 'Software\LVT\Desktop Item Position Saver';
procedure RestoreDesktopItemPositions;
procedure SaveDesktopItemPositions;
implementation
uses
uvirtalloc, registry;
procedure SaveListItemPosition(LVH : THandle; RemoteAddr : Pointer);
var
lvi : TLVITEM;
lenlvi : integer;
nb : integer;
buffer : array [0..MAX_PATH] of char;
Base : Pointer;
Base2 : PByte;
i, ItemsCount : integer;
Apoint : TPoint;
key : HKEY;
Dummy : integer;
begin
ItemsCount := SendMessage(LVH, LVM_GETITEMCOUNT, 0, 0);
Base := RemoteAddr;
lenlvi := SizeOf(lvi);
FillChar(lvi, lenlvi, 0);
lvi.cchTextMax := 255;
lvi.pszText := Base;
inc(lvi.pszText, lenlvi);
WriteToRemoteBuffer(@lvi, Base, 255);
Base2 := Base;
inc(Base2, Lenlvi);
RegDeleteKey(HKEY_CURRENT_USER, RegSubKeyName);
RegCreateKeyEx(HKEY_CURRENT_USER,
PChar(RegSUbKeyName),
0,
nil,
REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE,
nil,
key,
nil);
for i := 0 to ItemsCount - 1 do
begin
nb := SendMessage(LVH, LVM_GETITEMTEXT, i, LParam(Base));
ReadRemoteBuffer(Base2, @buffer, nb + 1);
FillChar(Apoint, SizeOf(Apoint), 0);
WriteToRemoteBuffer(@APoint, Base2, SizeOf(Apoint));
SendMessage(LVH, LVM_GETITEMPOSITION, i, LParam(Base) + lenlvi);
ReadRemoteBuffer(Base2, @Apoint, SizeOf(Apoint));
RegSetValueEx(key, @buffer, 0, REG_BINARY, @Apoint, SizeOf(APoint));
end;
RegCloseKey(key);
end;
procedure RestoreListItemPosition(LVH : THandle; RemoteAddr : Pointer);
type
TInfo = packed record
lvfi : TLVFindInfo;
Name : array [0..MAX_PATH] of char;
end;
var
SaveStyle : Dword;
Base : Pointer;
Apoint : TPoint;
key : HKey;
idx : DWord;
info : TInfo;
atype : Dword;
cbname, cbData : Dword;
itemidx : DWord;
begin
SaveStyle := GetWindowLong(LVH, GWL_STYLE);
if (SaveStyle and LVS_AUTOARRANGE) = LVS_AUTOARRANGE then
SetWindowLong(LVH, GWL_STYLE, SaveStyle xor LVS_AUTOARRANGE);
RegOpenKeyEx(HKEY_CURRENT_USER, RegSubKeyName, 0, KEY_QUERY_VALUE, key);
FillChar(info, SizeOf(info), 0);
Base := RemoteAddr;
idx := 0;
cbname := MAX_PATH;
cbdata := SizeOf(APoint);
while (RegEnumValue(key, idx, info.Name, cbname, nil, @atype, @Apoint, @cbData) <>
ERROR_NO_MORE_ITEMS) do
begin
if (atype = REG_BINARY) and (cbData = SizeOf(Apoint)) then
begin
info.lvfi.flags := LVFI_STRING;
info.lvfi.psz := Base;
inc(info.lvfi.psz, SizeOf(info.lvfi));
WriteToRemoteBuffer(@info, Base, SizeOf(info.lvfi) + cbname + 1);
itemidx := SendMessage(LVH, LVM_FINDITEM, - 1, LParam(Base));
if itemidx > -1 then
SendMessage(LVH, LVM_SETITEMPOSITION, itemidx, MakeLong(Apoint.x, Apoint.y));
end;
inc(idx);
cbname := MAX_PATH;
cbdata := SizeOf(APoint);
end;
RegCloseKey(key);
SetWindowLong(LVH, GWL_STYLE, SaveStyle);
end;
function GetSysListView32: THandle;
begin
Result := FindWindow('Progman', nil);
Result := FindWindowEx(Result, 0, nil, nil);
Result := FindWindowEx(Result, 0, nil, nil);
end;
procedure SaveDesktopItemPositions;
var
pid : integer;
rembuffer : PByte;
hTarget : THandle;
begin
hTarget := GetSysListView32;
GetWindowThreadProcessId(hTarget, @pid);
if (hTarget = 0) or (pid = 0) then
Exit;
rembuffer := CreateRemoteBuffer(pid, $FFF);
if Assigned(rembuffer) then
begin
SaveListItemPosition(hTarget, rembuffer);
DestroyRemoteBuffer;
end;
end;
procedure RestoreDesktopItemPositions;
var
hTarget : THandle;
pid : DWord;
rembuffer : PByte;
begin
hTarget := GetSysListView32;
GetWindowThreadProcessId(hTarget, @pid);
if (hTarget = 0) or (pid = 0) then
Exit;
rembuffer := CreateRemoteBuffer(pid, $FFF);
if Assigned(rembuffer) then
begin
RestoreListItemPosition(hTarget, rembuffer);
DestroyRemoteBuffer;
end;
end;
end.
{----------------------------------------------------------}
unit uvirtalloc;
interface
uses
Windows, SysUtils;
function CreateRemoteBuffer(Pid : DWord; Size: Dword): PByte;
procedure WriteToRemoteBuffer(Source : PByte;
Dest : PByte;
Count : Dword);
function ReadRemoteBuffer (Source : PByte;
Dest : PByte;
Count : Dword): Dword;
procedure DestroyRemoteBuffer;
implementation
var
hProcess : THandle;
RemoteBufferAddr: PByte;
BuffSize : DWord;
function CreateRemoteBuffer;
begin
RemoteBufferAddr := nil;
hProcess := OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
if (hProcess = 0) then
RaiseLastWin32Error;
Result := VirtualAllocEx(hProcess,
nil,
Size,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
Win32Check(Result <> nil);
RemoteBufferAddr := Result;
BuffSize := Size;
end;
procedure WriteToRemoteBuffer;
var
BytesWritten: Dword;
begin
if hProcess = 0 then
Exit;
Win32Check(WriteProcessMemory(hProcess,
Dest,
Source,
Count,
BytesWritten));
end;
function ReadRemoteBuffer;
begin
Result := 0;
if hProcess = 0 then
Exit;
Win32Check(ReadProcessMemory(hProcess,
Source,
Dest ,
Count,
Result));
end;
procedure DestroyRemoteBuffer;
begin
if (hProcess > 0) then
begin
if Assigned(RemoteBufferAddr) then
Win32Check(Boolean(VirtualFreeEx(hProcess,
RemoteBufferAddr,
0,
MEM_RELEASE)));
CloseHandle(hProcess);
end;
end;
end.
{----------------------------------------------------------}
Other Source for NT, Win2k, XP only:
http://www.luckie-online.de/programme/luckiedipssfx.exe
(Complete demo to save/restore the positions of desktop icons, nonVCL)
Выделить родительский компонент, когда он невидим, в Design Time.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
When sie z.B. einen TPanel auf einen Formular auf alClient GгцЯе eingestellt haben, dann кцnnеn Sie das Formular Objekt darunter nicht mehr mit der Maus selektieren. Aber so geht''s: 1. Panel anclicken 2. ESC dгьскеn ESC selektiert automatisch die Eltern-Komponent des aktiven Objekts, also das TForm in diesem Fall. Auch bei "verschachtelte Komponente" кцnnеn Sie wiederholt auf ESC dгьскеn, um jeweils eine Ebene "hцhег" zu landen bzw. zu selektieren. Es ist auch manchmal schneller als die Component im IDE Explorer zu suchen... {**************************************************************} Maybe you have a Panel on your Form with Alignement:=alClient. You cannot select the underlying Form with the Mouse. The quick way to select it: 1. select the panel 2. Press ESC It works with all Child components. Also many times in a row. ESC always selects the Parent of the activ component. This''s sometimes faster than looking for the control in the IDE Explorer...Сохранить значение TDateTime в реестре.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
Registry;
// Write TDateTime to Registry
procedure Reg_WriteDateTime(dwRootKey: DWord; const sKey: string; const sField: string; aDate: TDateTime);
begin
with TRegistry.Create do
try
RootKey := dwRootKey;
if OpenKey(sKey, True) then
begin
try
WriteBinaryData(sField, aDate, SizeOf(aDate));
finally
CloseKey;
end;
end;
finally
Free;
end;
end;
// Read TDateTime from Registry
function Reg_ReadDateTime(dwRootKey: DWord; const sKey: string; const sField: string) : TDateTime;
begin
Result := 0; // default Return value
with TRegistry.Create do
begin
RootKey := dwRootKey;
if OpenKey(sKey, False) then
begin
try
ReadBinaryData(sField, Result, SizeOf(Result));
finally
CloseKey;
end;
end;
Free;
end;
end;
// Example:
// Write DateTimePicker1's DateTime to Registry
procedure TForm1.Button1Click(Sender: TObject);
begin
Reg_WriteDateTime(HKEY_CURRENT_USER, 'Software\TestXYZ\','DateTime',DateTimePicker1.DateTime);
end;
// Set DateTimePicker1's DateTime from Registry
procedure TForm1.Button2Click(Sender: TObject);
var
ATime: TDateTime;
begin
ATime := Reg_ReadDateTime(HKEY_CURRENT_USER, 'Software\TestXYZ\','DateTime');
if ATime <> 0 then
DateTimePicker1.DateTime := TDateTime(ATime);
end;
Преобразовать TDateTime в Unix TimeStamp.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Sometimes you want to communicate with mySQL or other databases using
the unix timestamp. To solve this difference you may want to convert your
TDateTime to the unix timestamp format and vice versa.
Um den in einigen Datenbanken verwendeten Unix Timestamp fur die Kommunikation
mit Delphi nutzbar zu machen, kann man mit dieser Unit das TDateTime Format
in den Unix Timestamp und umgekehrt umwandeln.
}
unit unix_utils;
interface
implementation
const
// Sets UnixStartDate to TDateTime of 01/01/1970
UnixStartDate: TDateTime = 25569.0;
function DateTimeToUnix(ConvDate: TDateTime): Longint;
begin
//example: DateTimeToUnix(now);
Result := Round((ConvDate - UnixStartDate) * 86400);
end;
function UnixToDateTime(USec: Longint): TDateTime;
begin
//Example: UnixToDateTime(1003187418);
Result := (Usec / 86400) + UnixStartDate;
end;
end.
Получение координат области Рабочего стола, не скрытой Панелью задач.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение координат области Рабочего стола, не скрытой Панелью задач
Зависимости: Windows
Автор: Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright: Dimka Maslov
Дата: 4 ноября 2002 г.
***************************************************** }
function GetDesktopRect: TRect;
var
RgnDesktop, RgnTrayWnd: HRGN;
function CreateWindowRgn(Handle: HWND): HRGN;
var
R: TRect;
begin
GetWindowRect(Handle, R);
with R do
Result := CreateRectRgn(Left, Top, Right, Bottom);
end;
begin
RgnDesktop := CreateWindowRgn(GetDesktopWindow);
try
RgnTrayWnd := CreateWindowRgn(FindWindow('Shell_TrayWnd', ''));
try
CombineRgn(RgnDesktop, RgnDesktop, RgnTrayWnd, RGN_DIFF);
GetRgnBox(RgnDesktop, Result);
finally
DeleteObject(RgnTrayWnd);
end;
finally
DeleteObject(RgnDesktop);
end;
end;
9 live by DuoMark SETUP.
Оформил: DeeCo Автор: http://www.cracklab.narod.ru
Взят на диске "Хакер №10".
Вот попалась мне эта прога, которая автоматически следит за изменениями в системных файлах и если эти изменения были зделаны несознательно и это, недай бог, привело к каким-то проблемам, то эта программка восстанавливает файлы так, что даже намёка на глюки не будет. Так вот, запустил я инсталяцию (setup.exe) и увидел душераздирающую надпись: "Извините, но триал этого сетапа истёк! сорри!". Ну ни хера себе, подумал я. Мало того, что сама прога триальная, так ещё и установка её триальная. "Я увидел это всё и стал подругам звонить..." Короче говоря, после недолгих раздумий я решил установить таки прогу (судя по виду установки - прога не крепкая). Первым делом я дизассемблировал прогу WDasm'om и начал искать сообщение об ошибке в String Reference, но ничего дельного там не нашел :( Затем по привычке открыл айс и ввёл такой вот бряк:
bpx GetLocalTimeи соответственно запустил прогу, послен нажатия ОК в первом окне, а как я уже говорил, после этого нажатия выскакивает окно о конце триальности, и выскочил в айсе, после чего нажал F11 (чтобы очутиться не в kernel32.dll, а в setup.exe) и вот что увидел:
call [kernel32!GetLocalTime] lea ecx, [esp+00] push ecx call [kernel32!GetSytemTime] mov dx, [esp+0A] .... ....Это всё не то, и до процедуры реальной проверки ещё нужно скролить и скролить, поэтому нажимаем F10 до тех пор, пока не увидим это место:
add edx, ecx cmp edx, -05 ; проверяем результаты проверок jl 00401589 ; если они негативные - прыгаем cmp edx, 5A ; опять проверим jge 00401589 ; опять прыгнем туда же :) cmp edx, 50 ; ещё проверка jl 00401517 ; ещё прыжок.Я сначала хотел убрать эти проверки, после чего у меня выскакивало таки то окно, но при нажатии ОК, выскакивало другое, что триальная установка ещё действует, но мне смотреть на эти галимые окна не захотелось и я начал думать дальше. Потом я снова проделал шаги, описанные выше и обнаружил, что чуть ниже последнего листинга (около 3 строчек вниз есть четрые подряд call'a:
call 00405470 ;загрузка текста для первого message ... ... call 00405670 ;вызов первого сообщения ... ... call 00405470 ;загрузка текста для второго message ... ... call 00405670 ;вызов второго сообщения :0040152C mov bl,01я подумал и придумал вот что: а если убрать все проверки, о которых я писал выше, и вместо них зделать прыжок (jmp) сразу за вызовы сообщений об окончании триальной установки и о его волшебном возобновлении :) и правда, когда я заменил первую строку проверок (cmp edx, -05) на (jmp 0040152C), то после этих изменений никаких сообщений не осталось (кроме приветственного) и потом сразу же начиналась установка. На таком простом примере я показал, как бороться с "триальными" Sеtuр'ами (хотя я чесно говоря с таким родом защиты столкнулся впервые, чтобы триальную прогу да ещё и триальным сетапом :) А для особо ленивых вот готовый крак:
----------вырезать тут-------- 9live's SETUP CRACK by vallkor//PTDS(vallkor@chat.ru) you can install it when you want! setup.exe 000008EB: 83 EB 000008EC: FA 3F 000008ED: FB 90 ----------вырезать тут--------исследовал и наваял туториал: vallkor //PTDS (vallkor@chat.ru) {Комманда PTDS нуждается в новых членах! Если вы считаете, что способны на многое и хотите поделиться своими знаниями и взамен получить новые пишите мне}.
Дубликат Paradox или dBase записи.
| Автор: Eryk Bottomley |
| Встречаются два администратора: (1)-Юникса, (2)-Винды (1) - Вот, на работе скукотища - делать совсем нечего. Никто не жалуется... (2) - А у меня всегда работа есть - каждые полчаса систему переставляю. (1) - Ну ни фига себе - ну ты крут. Как ты добился, чтобы винда 30 минут работала??? (2) - Да нет, это она 30 минут переставляется... |
Вот одно "кривое" решение:
procedure TForm1.Button1Click(Sender: TObject);
var
p : CURProps;
begin
Table2.Insert;
DbiGetCursorProps(Table1.Handle,p);
Move(Table1.ActiveBuffer^,Table2.ActiveBuffer^,p.iRecBufSize);
Table2.FieldByName('ID').AsInteger := Table1.FieldByName('ID').AsInteger + 1;
Table2.Post;
end;
|
...это работает на том "основании", что как Table1, так и Table2 ссылаются на один и тот же табличный файл. Вы могли бы осуществить это и с единственным TTable, если сохраните содержание в активном буфере (ActiveBuffer).
Ошибка дублирования идентификатора ресурса.
| Повесился как-то программист, и в кармане у него нашли записку: Critical error. System halted. |
Вероятно, вы сабкласситесь от VCL. Убедитесь в том, что идентификатор ресурса для вашей иконки уникальный. Просто загрузите ее в любой редактор ресурсов, и измените ее номер. После этого вы должны пересобрать вашу библиотеку.
Создание новой таблицы на основе структуры другой таблицы.
На ум сразу приходит операция присваивания значения свойству (стоящему с левой стороны от ':='), при которой Delphi в своих недрах вызывает метод 'write' и передает ему в виде единственного параметра все то, что находится в правой части выражения. Если свойство не имеет метода write, оно предназначено только для чтения. Вот определение свойства FieldDefs объекта TDataSet в файле DB.PAS:
property FieldDefs: TFieldDefs read FFieldDefs write SetFieldDefs; |
Как вы можете видеть, у него есть метод write. Следовательно, код:
Destination.FieldDefs := Source.FieldDefs; |
В действительности делает такую операцию:
Destination.SetFieldDefs(Source.FieldDefs); |
Вот определение свойства IndexDefs объекта TTable в файле DBTABLES.PAS file:
property IndexDefs: TIndexDefs read FIndexDefs; |
В этом случае метод write отсутствует, поэтому свойство имеет атрибут только для чтения. Тем не менее, для самого объекта TIndexDefs существует метод Assign. Следовательно, следующий код должен работать:
Source.IndexDefs.Update; Destination.IndexDefs.Assign(Source.IndexDefs); |
Также можно проверить, определен ли реально индекс, и, если нет, то при вызове IndexDefs.Assign вы можете получить исключение типа "List Index Out Of Bounds" (или что-то типа этого). Например, так:
if Source.IndexDefs.Count > 0 then... |
Вам нужно будет это сделать, поскольку метод TIndexDefs.Assign не проверяет это перед копированием индекс-информации. Также вам нет необходимости вызывать Clear до работы с IndexDefs, поскольку метод Assign сделает это и без вашего участия.
Дублируем TTable со всеми индексами.
type
TForm1 = class(TForm)
tbSource: TTable;
tbTarget: TTable;
end;
implementation
procedure TForm1.Button1Click(Sender: TObject);
begin
tbSource.TableName := 'Source.DB'; // The name of your tables which you want to copy from
tbTarget.TableName := 'Target.DB'; // The name of your tables which you will to copy to
// You Can set the tbSource.DataBaseName to an existing path/Alias
// where you store your DB
// You Can set the tbTarget.DataBaseName to an existing path/Alias
// where you want to store the duplicate DB
tbSource.StoreDefs := True;
tbTarget.StoreDefs := True;
tbSource.FieldDefs.Update;
tbSource.IndexDefs.Update;
tbTarget.FieldDefs := tbSource.FieldDefs;
tbTarget.IndexDefs := tbSource.IndexDefs;
tbTarget.CreateTable;
end;
|
Экономия памяти с дублирующими значениями в таблице.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Often there are lots of duplicate strings in a program,
for example take the following database:
Author Title
aaa xxx
aaa yyy
aaa zzz
Suppose you want to read it into your program and
create one record or class per line:
}
while not Table.EOF do
begin
author := Table['Author'];
title := Table['Title'];
MyClass := TMyClass(author, title);
StoreItSomewhere(MyClass);
Table.Next;
end;
{
You end up with three different strings containting the same author.
No problem with that as long as there are only three of them.
Now suppose you have got a database of 100,000 entries and lots of
duplicate author names.
If you do the above again you will end up with a huge memory overhead
due to fragmentation of the free memory on the heap.
In addition to that freeing the objects will result in a noticable delay
of the program while the memory manager is busy merging free memory blocks.
On the other hand, Delphi's AnsiStrings use reference counting so it
would be possible to assign the same string to many string variables
without copying the contents:
}
Author := 'aaa';
for i:=0 to 100000 do
begin
MyClass := TMyClass(Author);
StoreItSomewhere(MyClass);
end;
{
This will create 100000 strings containting 'aaa',
but store the actual contents only once,
because they are assigned the same string constant.
It would be nice to do the same with the above database example,
wouldn't it? But how do we get Delphi to realize that
the content is really the same?
Answer: A StringList
}
authors := TStringList.Create;
authors.Sorted := true;
authors.Duplicates := dupIgnore;
while not Table.EOF do
begin
author := Table['Author'];
title := Table['Title'];
authors.Add(author);
authors.Search(author, Idx);
author := authors[Idx];
MyClass := TMyClass(author, title);
StoreItSomewhere(MyClass);
Table.Next;
end;
authors.free;
{
This will only keep one string of each author's name and assign
it to all other occurences, even after the StringList has been freed again.
The wonders of reference counting!
I admit that the above example looks a bit odd. Nobody would load
such a database into memory. But I had a similar problem today and by using
the trick above I managed to reduce the memory usage of my program from 120 megabytes to 10
(basically nothing, given that it started out with 8 mb).
}
DirectX для начинающих. Часть пятая.
Оформил: DeeCo
Автор: Виктор Кода.
В этот раз мой новый урок будет посвящён азам использования интерфейса Direct3D - наиболее сложного и объёмного из состава DirectX8 SDK.Я не собираюсь рассказывать, как построить объёмный кубик или классический чайник в пространстве, нет. Вместо этого я решал затронуть довольно интересную тему - как организовать вывод 2D-графики посредством Direct3D. Я решил заняться этим вопросом, т. к. всё чаще API трёхмерного моделирования используются для создания плоских изображений. Соответсвенно и я собираюсь перевести свои программы на новы рельсы.
Вообще, интерфейсы IDirectDraw7 и IDirectDrawSurface7 - последние в составе SDK, которые предназначены для вывода двумерной графики путём битовых переносов. Эти интерфейсы были введены в DirectX версии 7 и по сути, уже ничего принципиально нового не содержали. Единственное отличие состяло в более тесной интеграции DirectDraw с Direct3D, но и это уже в прошлом. С началом разработки DirectX 8 отдел Microsoft принял решение навсегда остановить развитие этой компоненты, а Direct3D сделать полностью самодостаточным и единственным средством вывода графики в DirectX. Теперь он так и называется - DirectXGraphics. Впрочем, в составе SDK до сих пор присутствует файл ddraw.h, чтобы пользователи имели возможность и дальше работать с DirectDraw, и примеры по его использованию. Это как намёк на то, что не следут на 100% отказываться от старых приёмов работы с графикой.По-видимому, решение об остановке исходило из невозможности добавить в DirectDraw ещё сколь-нибудь существенных деталей. Аппаратно эффекты DirectDraw не поддерживаются и планов таких у производителей видеокарт не намечалось. В тоже время многие программисты уже начали применять Direct3D для вывода 2D-графики, игнорируя DirectDraw. Взвесив все "за" и "против", было решено доработать Direct3D как средство построения плоских изображений, а DirectDraw объявить бесперспективным. Вот так старейший компонент, когда-то служивший в DirectX единственным средством представления графики, теперь является анахронизмом...
Соответственно, и нам нет смысла долго "сидеть" на нём, а надо потихоньку перебираться в Direct3D, догонять технологии, так сказать. Но прежде всего необходимо оговорить ограничения, связанные с использованием этого интерфейса: Для вывода графики необходимо, чтобы компьютер был оснащён простейшим, но всё же 3D-ускорителем. И хотя вывод будет осуществляться и на обычной видеокарте, картинка будет искажена, а скорость вывода будет неприемлемо мала. Впрочем, в теперешнюю эпоху навороченных акселераторов простейшие их модели стоят копейки. Текстуры, содержащие изображение, по размерам должны быть кратны степени двойки. Т. е. 64Х64 или 256Х512 - подходит, а 63х255 - нет. Это вроде как на руку аппаратной части. А теперь приступим непосредственно к воплощению замысла на практике. Для этого сперва необходимо подключить два заголовочных файла, в которых энтузиасты JEDI разместили всё, что необходимо для полноценной работы с Direct3D версии 8:uses directxgraphics, d3dx8;Эти два файла представляют собой объединение многих других файлов из SDK для C/C++ (их названия приведены в самом начале). Первый файл содержит описание основных интерфейсов Direct3D, а второй - дополнительных интерфейсов Direct3DX. Это вспомогательная библиотека, содержащая интерфейсы высокого уровня и множество функций разного назначения, дополнение к основному комплексу. Обратите своё внимание на такую строчку из файла d3dx8.pas:
const d3dx8dll = 'D3DX8ab.dll';Это означает, что все функции экспортируются из указанной динамической библиотеки. Хотя она и поставляется в архиве с остальными файлами *.pas, но не входит в стандартный run-time от Microsoft. Т. е. ваши приложения, основанные на Direct3D, не смогут выполняться на компьютерах других пользователей, если только вы не будете каждый раз прикреплять к своим файлам нестандартную библиотеку. Откуда её выкопали JEDI - Бог его знает, однако нестандартный run-time - это плохо. Решением может служить изменение значения константы:
const d3dx8dll = 'd3dx8d.dll';
После такой замены всё работает вроде как нормально, во всяком случае, я не встретил ещё ни одной ошибки. Файл d3dx8d.dll я нашёл у себя в директории Windows/System, он входит в стандартный пакет DirectX 8. В любом случае, могу посоветовать такой приём: компилируйте все свои Direct3D-приложения с использованием d3dx8ab.dll, а когда возникнет необходимость передать программу другим пользователям, замените имя библиотеки и перекомпилируйте программу. Если всё работает нормально, можете смело распространять своё творение.
Также нужно заметить, что размер "пустого" выполняемого файла получается около 40 кб, а не 17-20. Причина этого кроется в том, что модуль d3dx8.pas подключает модуль sysutils.pas. Самое "смешное" в том, что с увеличенным размером приходится мириться из-за вызова единственной функции CompareMem() (из функции D3DXMatrixEqual()). Если вы такие же щепетильные в этом вопросе, как и я, то могу посоветовать такое решение: скопируйте функцию CompareMem() из sysutils.pas в d3dx8.pas, и удалите ссылку на модуль из списка uses. Размер минимальной программы уменьшится до стандартных для Delphi 17 кб. Разобравшись с заголовочными файлами, объявим далее необходимые переменные:var g_pD3D: IDirect3D8 = nil; // общий интерфейс Direct3D g_device: IDirect3DDevice8 = nil; // наша видеокарта g_pTexture: IDirect3DTexture8 = nil; // текстура g_pSprite: ID3DXSprite = nil; // спрайтТеперь необходимо провести инициализацию Direct3D:
// проверяем правильность динамической компоновки if @_Direct3DCreate8 = nil then exit; // получаем интерфейс IDirect3D8 g_pD3D := Direct3DCreate8(D3D_SDK_VERSION); if g_pD3D = nil then exit;
Здесь я проверяю адрес функции на корректность, ведь мы производим динамическую компоновку библиотеки, есть вероятность, что её не окажется на месте. Если всё в порядке, мы получаем интерфейс IDirect3D8. Это - первый интерфейс, с которым имеет дело программист. Его основная задача - общая работа с видеокартой и монитором - "создание" и получение характеристик видеокарты, перечисление доступных видеорежимов, дескрипторов мониторов и т.д.
При инициализации графического устройства нам обязательно понадобится текущий видеоформат. Его можно задать самостоятельно, но так как он может не поддерживаться установленной видеокартой, а проверять это в маленьком примере накладно, лучше воспользоваться форматом текущего видеорежима:// получаем формат текущего видеорежима if FAILED(g_pD3D.GetAdapterDisplayMode(D3DADAPTER_DEFAULT, d3ddm)) then exit;После этого необходимо заполнить поля структуры TD3DPRESENT_PARAMETERS:
// подготавливаем структуру TD3DPRESENT_PARAMETERS ZeroMemory(@d3dpp, sizeof(d3dpp)); d3dpp.Windowed := BOOL(FALSE); // полноэкранный режим d3dpp.BackBufferWidth := SCREEN_WIDTH; // высота и d3dpp.BackBufferHeight := SCREEN_HEIGHT; // ширина заднего буфера d3dpp.BackBufferFormat := d3ddm.Format; // формат буфера - как и у текущего экрана d3dpp.SwapEffect := D3DSWAPEFFECT_DISCARD; // тип сброса содержимого буфера в окно
Замечу, что поле Windowed поределяет, будем ли мы работать в оконном или полноэкранном режиме. Замените FALSE на TRUE, и получите вывод в окно.
Теперь необходимо инициализировать графическое устройство:// создаём устройство Direct3D if FAILED(g_pD3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, d3dpp, g_device)) then begin exit; end;Создаваемое устройство может быть различных типов. Поясню только самое общее. D3DDEVTYPE_HAL - это аппаратное устройство, где все операции выполняются графическим процессором видеокарты. Как раз это нам и нужно. D3DDEVTYPE_REF - программное устройство, все операции выполняет центральный процессор. Крайне медленно, зато можно использовать любые возможности Direct3D, вроде пиксельных и вертексных шейдероов. В основном этот параметр нужен для производителей видеокарт, чтобы они подгоняли результат аппаратного рендеринга к эталонному программному. D3DCREATE_SOFTWARE_PROCESSING - обработка вершин производится ЦП. Не очень быстро, так что в серьёзных программах лучше использовать другие флаги (их можно легко узнать из заголовочного файла), но для простоты я выбрал этот. Кстати, некоторые видеокарты могут работать только с указанием этого флага.
Полученный интерфейс IDirect3Ddevice8 представляет собой как бы абстракцию над видеокартой. Вызывая те или иные методы, мы отдаём команды графическому процессору. Именно этот интерфейс является основным в работе по построению изображений.
Для простоты здесь же созданим и объект, который будет служить спрайтом:result := SUCCEEDED(D3DXCreateSprite(g_device, g_pSprite));
Интерфейс ID3DXSprite как раз и служит для вывода простых спрайтов в Direct3D. Использовать его очень легко, т. к. этот интерфейс - просто надстройка над более низкоуровневымы операциями Direct3D. Я пробовал рисовать и вторым способом, но он более объёмен и сложен, так что для примера я выбрал первый. Как и всякое простое решение, он не позволит сделать всего, что может API. Если вам со временем станет чего-то не хватать, можно прибегнуть к явному программированию.
После инициализации Direct3D следующим шагом является загрузка текстуры:hr := D3DXCreatewTextureFromFileEx(g_device, pchar(strFileName), 0, 0, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, D3DXFILTER_NONE, D3DXFILTER_NONE, D3DCOLOR_XRGB(0, 255, 0), nil, nil, g_pTexture);Функция D3DXCreateTextureFromFileEx() является частью комплекса Direct3DX, и позволяет нам загружать текстуры из файлов разного формата. Результатом является корректный указатель на интерфейс IDirect3Dtexture8. Существует более простая реализация функции (без приставки Ex), но она не позволят задать цветовой ключ для текстуры, поэтому пришлось прибегнуть к этой. Не обращайте внимание на обилие параметров - половина из них взята "по умолчанию". Всегда можно написать собственную промежуточную функцию, и избавить код от захламления при загрузке множества текстур.
С цветовым ключом мы уже давно знакомы, но здесь есть два нюанса. Первый - это то, что цветовой ключ один для всей текстуры. Это может вызвать некоторые неудобства при размещении разных изображений в одной текстуре (что часто и делается), но они вполне решаемы с помощью какого-нибудь фотошопа. Второй - если необходимо задать чёрный цветовой ключ, не пишите вместо D3DCOLOR_XRGB( 0, 255, 0 ) просто 0 - это ошибка, т. к. данное значение сигнализирует о том, что цветовой ключ не требуется. Надо писать именно D3DCOLOR_XRGB( 0, 0, 0 ),или $FF000000, что то же самое.
Если теперь открыть bmp-файл, то сразу видно, что я разместил там два изображения. Как я уже говорил, размеры текстур должны равняться степени двойки. Поэтому мы не можем разместить изображение фона в растре размерностью 512*384, приходится его расширять до 512*512 пикселей. Чтобы зря не тратить память видеокарты, по возможности необходимо размещать в свободных участках какие-либо дополнительные изображения. В моём случае влез рисунок 128*128, хотя его первоначальные размеры были в два раза больше (пожертвовал для уменьшения архива). Если же нужно вместить что-то нестандартное, то придётся делать ещё одну текстуру, или расширять существующую, скажем, до 512*1024. Тут нужно пробовать разные варианты и комбинации, пока не будет найден какой-то компромисс. Кстати, если уж говорить о размерах текстур, так вот: Direct3D МОЖЕТ загрузить растр любого размера, но при рендеринге тем способом, который я собираюсь использовать, возникнут искажения. Этот вопрос мною ещё слабо изучен, поэтому лучше пока это "на потом", а пока выучить степени двойки наизусть.Теперь можно приступать непосредственно к рендерингу изображения. Для простоты я "завесил" экран одной частью текстуры (кстати, это небезызвестный кадр из альфа-версии DOOM 3 :). Сверху же будет выводиться спрайт, другая часть текстуры.
Итак, для начала необходимо очистить кадровый буфер и "скомандовать" GPU начать просчитывать новый кадр:// очищаем кадровый буфер g_device.Clear(0, nil, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0, 0); // начинаем рисовать изображение g_device.BeginScene(); Далее выводим фон: SetRect(rcTex, 0, 0, 512, 384); // область текстуры transv.x := 0; // координаты левой transv.y := 0; // верхней точки g_pSprite.Draw(g_pTexture, @rcTex, nil, nil, 0, @transv, $FFFFFFFF);С методом ID3DXSprite.Draw() надо разобраться.
function Draw(const pSrcTexture: IDirect3DTexture8; pSrcRect: PRect; pScaling, pRotationCenter: PD3DXVector2; const Rotation: Single; pTranslation: PD3DXVector2; const Color: TD3DColor): HResult; stdcall;Параметром pSrcTexture передаём наш интерфейс pTexture. PSrcRect - это область текстуры, которая будет "натягиваться" на полигон, имитирующий наш спрайт. PScaling - это параметр типа D3DXVECTOR2, задаёт масштабирование спрайта по осям X и Y. Значение 1.0 задаётся для вывода реальных размеров, при 0.5 произойдет уменьшение в два раза, при 2.0 - увеличение в два раза. PRotationCenter - это центр оси поворота. Rotation - это угол поворота в радианах. Т. к. обычно используюся градусы, необходимо преобразовывать градусную меру в радианную. Я отыскал вот такую формулу перехода:
A = 3.14 * n / 180 (радиан).
pTranslation - перенос системы координат, в просторечье позиция левого верхнего угла спрайта. Color - используется при альфа-прозрачности и при задании цветовых компонент. Параметр - типа D3DCOLOR, определённого как DWORD. Первые три бита описывают компоненты RGB, последний - альфа-прозрачность. Для простого отображения текстуры используется значение $FFFFFFFF. Чтобы понять, как нужно манипулировать этим значением, попробуйте для фона задать такое: $FF00FF00.При прорисовке фона мы указываем только область текстуры, позицию левого верхнего угла и цвет. Остальным параметрам передаём nil.
Теперь необходимо вывести спрайт и закончить рендеринг. Вот тут-то я и показал преимущества Direct3D над DirectDraw. То, чего нельзя было сделать ранее, можно легко использовать сейчас:SetRect(rcTex, 0, 384, 128, 512); // область текстуры transv.x := x - 64 * fScale; // координаты левой transv.y := y - 64 * fScale; // верхней точки scalev.x := fScale; // масштабирование по оси X и scalev.y := fScale; // Y rotv.x := 64 * fScale; // положение оси rotv.y := 64 * fScale; // поворота (здесь центр спрайта) g_pSprite.Draw(g_pTexture, @rcTex, // текстура и её область @scalev, // масштаб @rotv, // позиция оси поворота D3DX_PI * fAngle / 180.0, // угол поворота(перевод градуса в радианы) @transv, // позиция в окнонных координатах ((255 - alpha) shl 24) + $FFFFFF); // альфа-прозрачность // закончили рисовать изображение g_device.EndScene(); // выводим содержимое кадрового буфера на экран g_device.Present(nil, nil, 0, nil);
Спрайт можно поворачивать клавишами "вправо" и "влево", прозрачность меняется при нажатии на "вверх" и "вниз", масштабирование происходит при нажатии на "+" или "-" на основной клавиатуре.

//g_device.SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );и добавьте вызовы g_pSprite._Begin() после g_device.Begin() и g_pSprite._End() перед g_device.End(). Сможете увидеть, из чего состоят "спрайты" :) Ну вот и всё, что нужно при простом использовании Direct3D. Хочется ещё заметить, что такие спрайты используются не только в 2D-графике, но так же с успехом и при построении трёхмерных сцен. Например, при выводе HUD. Или вот, в Quake 3 - газы при стрельбе из Machine Gun'а ни что иное, как спрайт, помещённый перед стволом и отрисованный с некоторой прозрачностью. В общем, примеров использования достаточно. Конечно, после практики с интерфейсом ID3DXSprite не лишне попробовать и более низкоуровневые методы вывода изображения. http://www.delphi-jedi.org/DelphiGraphics/directx/headers/DirectXGraphics.zip - заголовочные файлы для работы с Direct3D. http://www.delphi-jedi.org/DelphiGraphics/directx/headers/DirectXGraphics.zip - все заголовочные файлы для работы с DirectX 8.
Скачать примеры к статье D3DXSprite.zip (95 K).
Смотрите по теме: DirectX для начинающих DirectX для начинающих. DirectInput API DirectX для начинающих. Sprite DirectX для начинающих. Sound DirectX для начинающих. Часть третья. Считывание и запись Полупрозрачные спрайты в DirectDraw Пишем DirectX-движок DirectX и DelphiКак заставить работать DelphiX и UnDelphiX под Delphi6.
Оформил: DeeCo
Автор: Michael Wilson.
ВведениеЭта статья расскажет вам как откомпилировать известные библиотеки DelphiX и UnDelphiX под Delphi 6.0. Весь процесс займет не больше 15-20 минут, кроме того вы изучите общие приемы, которые могут помочь в аналогичных ситуациях. Почему это статья, а не, скажем, патч или дополнение к Delphi? Есть две причины:
1) По соглашению об использовании библиотеки ("TERMS AND CONDITIONS OF USE"), нельзя рапространять измененные части дистрибутива DelphiX.
2) Действия, которые необходимо совершить для конвертации проекта под Delphi 6.0, делают DelphiX несовместимой для использования в Delphi 3-5.
Предупреждение: пожалуйста, сохраните резервную копию всех изменяемых файлов. Методы, изложенные здесь, протестированы на Delphi 6.0 Personal, если у вас другая редакция (например, Professional), могут возникнуть непредвиденные проблемы.
Итак, перекомпиляция:Шаг 1: Во-первых, нужно убедиться, что у вас установлена последняя версия DelphiX. Ее можно скачать с этого сайта. После распаковки архива запустите Delphi 6. Не запускайте файл install_for?.exe!
Шаг 2: В меню File выберите пункт Open и откройте DelphiX_for5.dpk. Delphi 6 преобразует пакет к 6-й версии.

Сохраните пакет как DelphiX_for6.dpk.
Шаг 3: Попробуйте нажать кнопку Install. Компилятор покажет вам ошибки, которые нужно исправить. Все ошибки относятся к модулю DXRender.pas.
[ Замечание: если вы компилируете библиотеку UnDelphiX].
Данное изменение касается вас только в случае компиляции библиотеки UnDelphiX. Этот фрагмент кода:
var NilGUID: TGUID absolute 0;
… нужно заменить на …
var NilGUID: TGUID;
[ Замечание: если вы компилируете библиотеку UnDelphiX].
Шаг 4: Первый тип ошибок, которые нужно исправить - это ошибка "Operand Size Mismatch". Всего должно быть около 40-50 ошибок такого типа. Исправить ее очень легко:

mov eax, Dest; add eax, TDXRMachine_Color.R + 1 // bad statement
... заменяется на ...
mov eax, Dest; add eax, BYTE(TDXRMachine_Color.R + 1) // good statement
Этот фрагмент просто извлекает младшие 8 бит TDXRMachine_Color.R - вам нужно исправить все строки, где есть ссылка на TDXRMachine_Color. Это делается очень легко с помощью пункта меню Search -> Replace...
Шаг 5: Второй тип ошибок, требующих исправления, - это "Inline Assember Syntax". Всего будет порядка 10-20 ошибок этого типа и они всегда возникают, если в исходном тексте есть упоминание об _AddTable или _SubTable. Исправляется ошибка также просто:
movzx edx, byte[offset _AddTable + edx + ebx]
... заменяется на ...
movzx edx, byte[offset(_AddTable + edx + ebx)]
После пройденных шагов компиляция должна заканчиваться только с одной фатальной ошибкой и одним предупреждением.
Шаг 6: Последняя и самая неприятная ошибка, связанная с модулем DsgnIntf.pas. Из-за нее не компилируется модуль DxReg.pas библиотеки DelphiX.

Есть два способа исправить ошибку:
1) Delphi Personal: во-первых, удалите все строки под объявлениями типов в DXReg.Pas. Затем удалите ссылки на RegisterComponentEditor и RegisterPropertyEditor в процедуре Register. Наконец, удалите весь код, который находится ниже процедуры RegisterComponents (убедитесь, что модуль по-прежнему заканчивается "end."). После вышеописанных действий модель DXReg.Pas должен выглядеть вот так:
unit DXReg;
interface
uses
Windows, SysUtils, Classes, Forms, Dialogs, Graphics, TypInfo,
DXDraws, DXSounds, DIB, Wave, DXInput, DXPlay, DXSprite,
DXClass;
procedure Register;
implementation
const
SNone = '(None)';
SSettingImage = '&Image...';
SSettingWave = '&Wave...';
SDXGFileFilter = 'DXG file(*.dxg)|*.dxg|All files(*.*)|*.*';
SDXGOpenFileFilter =
'DXG file(*.dxg)|*.dxg|Bitmap file(*.bmp)|*.bmp|All files(*.*)|*.*';
SDXWFileFilter = 'DXW file(*.dxw)|*.dxg|All files(*.*)|*.*';
SDXWOpenFileFilter =
'DXW file(*.dxw)|*.dxw|Wave file(*.wav)|*.wav|All files(*.*)|*.*';
SSinglePlayer = '&Single player';
SMultiPlayer1 = 'Multi player &1';
SMultiPlayer2 = 'Multi player &2';
SOpen = '&Open...';
SSave = '&Save..';
procedure Register;
begin
RegisterComponents('DelphiX',
[TDXDraw,
TDXDIB,
TDXImageList,
TDX3D,
TDXSound,
TDXWave,
TDXWaveList,
TDXInput,
TDXPlay,
TDXSpriteEngine,
TDXTimer,
TDXPaintBox]);
end;
end.
С таким решением проблемы вы не сможете использовать редактор свойств, включенный в DelphiX.
2) Delphi Professional: решение проблемы с профессиональной версией Delphi была подсказана Tommy "QuazzeMan" Jonsson:
A) В модуле DXReg.pas замените ссылку на DsgnIntf в списке USES на DesignIntf. b) Добавьте DesignEditors в список USES модуля DXReg.Pas c) Добавьте DesignIDE в секцию REQUIRED пакета.
Шаг 7: Все, можно наслаждаться работой DelphiX и UnDelphiX в шестой версии Delphi.
Динамическое добавление пунктов меню.

Новый элемент верхнего уровня:
procedure tform1.addmainitem(s: string);
var
newitem: Tmenuitem;
begin
newitem := tmenuitem.create(Mainmenu1);
newitem.caption := s;
{если вы хотите определить событие onclick
newitem.onclick:=Dynamenuclick; }
{добавляем это к верхнему уровню меню}
mainmenu1.items.insert(mainmenu1.items.count, newitem);
removemenu1.enabled := true;
addmenuitem1.enabled := true;
end;
|
Создание подменю:
procedure tform1.addsubitem(s: string; to : integer);
var
newitem, toitem: Tmenuitem;
begin
{to = верхний уровень меню для нового пункта}
toitem := mainmenu1.items[to ];
newitem := tmenuitem.create(toitem);
newitem.caption := s;
{если вы хотите определить событие onclick
newitem.onclick:=Dynamenuclick; }
toitem.onclick := nil;
toitem.insert(toitem.count, newitem);
removemenuitem1.enabled := true;
end;
|
Динамическое добавление пунктов меню 2.
Автор: Neil J. RubenkingВот пример программы, создающей структуру меню большой вложенности двумя различными способами. Она даст вам пищу для размышлений. Форма содержит компонент TMainMenu1 и, первоначально, одно подменю с именем SubMenu1.
unit Istopmnu;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, Menus;
type
TForm1 = class(TForm)
MainMenu1: TMainMenu;
SubMenu1: TMenuItem;
procedure AClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.AClick(Sender: TObject);
var
TM: TMenuItem;
Lev: Word;
begin
MessageBeep(0);
TM := Sender as TMenuItem;
if TM.Count > 0 then
Caption := 'подменю'
else
Caption := 'элемет меню';
Caption := Caption + ' с именем "' + TM.Name + '"';
Lev := 0;
while (TM.Parent <> nil) and (TM.Parent is TMenuItem) do
begin
TM := TM.Parent;
Inc(Lev);
end;
case Lev of
1: Caption := 'Верхний уровень ' + Caption;
2: Caption := '2-й уровень ' + Caption;
3: Caption := '3-й уровень ' + Caption;
else
Caption := Format('%d-й уровень %s', [Lev, Caption]);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
TM: TMenuItem;
N: Integer;
begin
TM := SubMenu1;
TM.Add(NewItem('&Элемент', 0, False, True, AClick, 0, 'MenuItem2'));
for N := 2 to 5 do
begin
TM.Add(TMenuItem.Create(nil));
TM := TM.Items[TM.Count - 1];
TM.Caption := '&Меню';
TM.Name := 'SubMenu' + IntToStr(N);
TM.OnClick := AClick;
TM.Add(NewItem('&Элемент', 0, False, True, AClick, 0,
'MenuItem' + IntToStr(N + 1)));
end;
MainMenu1.Items.Add(NewSubMenu('Меню&2', 0, 'SM1',
[NewItem('&Элемент', 0, False, True, AClick, 0, 'MI2'),
NewSubMenu('&Меню', 0, 'SM2',
[NewItem('&Элемент', 0, False, True, AClick, 0, 'MI3'),
NewSubMenu('&Меню', 0, 'SM3',
[NewItem('&Элемент', 0, False, True, AClick, 0, 'MI4'),
NewSubMenu('&Меню', 0, 'SM4',
[NewItem('&Элемент', 0, False, True, AClick, 0, 'MI5'),
NewSubMenu('&Меню', 0, 'SM5',
[NewItem('&Элемент', 0, False, True, AClick, 0, 'MI6')
])
])
])
])
]));
TM := MainMenu1.Items[1];
while TRUE do
begin
TM.OnClick := AClick;
if TM.Count < 2 then
Break;
TM := TM.Items[1];
end;
end;
end.
|
Динамическое добавление пунктов меню 3.
Вы можете использовать готовые функции, определенные в модуле Menus. Определения в Delphi 2:
function NewMenu(Owner: TComponent; const AName: string; Items: array of TMenuItem): TMainMenu; function NewPopupMenu(Owner: TComponent; const AName: string; Alignment: TPopupAlignment; AutoPopup: Boolean; Items: array of TMenuitem): TPopupMenu; function NewSubMenu(const ACaption: string; hCtx: Word; const AName: string; Items: array of TMenuItem): TMenuItem; function NewItem(const ACaption: string; AShortCut: TShortCut; AChecked, AEnabled: Boolean; AOnClick: TNotifyEvent; hCtx: Word; const AName: string): TMenuItem; function NewLine: TMenuItem; |
Это превращает вышепоставленную задачу в сущий пустяк.
Динамически создаваемые псевдонимы.
Использование псевдонима для доступа к базе данных обеспечивает независимость программы от размещения данных в системе, позволяет размещать программу работы с данными и базу данных на разных дисках компьютера, в том числе и на сетевом. Вместе с тем, для простых баз данных типичным решением является размещение базы данных в отдельном подкаталоге того каталога, в котором находится программа работы с базой данных. Таким образом, программа работы с базой данных всегда "знает", где находятся данные. При таком подходе можно отказаться от создания псевдонима при помощи BDE Administrator и возложить задачу создания псевдонима на программу работы с базой данных. Причем, псевдоним будет создаваться автоматически во время запуска программы и уничтожаться во время завершения ее работы. Очевидно, что такой подход облегчает администрирование базы данных.В качестве иллюстрации сказанного в листинге приведен вариант программы работы с базой данных "Школа", которая для доступа к базе данных использует динамически создаваемый псевдоним.
unit school3_;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, Db, DBTables, ExtCtrls, DBCtrls, StdCtrls;
type
TForm1 = class(TForm)
Table1: TTable; // таблица (вся база данных)
Query1: TQuery; // запрос (записи БД, удовлетворяющие критерию выбора)
DataSource1: TDataSource; // источник данных - таблица или запрос
DBGrid1: TDBGrid; // таблица для отображения БД или результата выполнения запроса
DBNavigator1: TDBNavigator;
DBText1: TDBText;
Button1: TButton; // кнопка запрос
Button2: TButton; // кнопка Все записи
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
// щелчок на кнопке Запрос
procedure TForm1.Button1Click(Sender: TObject);
var
fam: string[30];
begin
fam := InputBox('Выборка информации из БД',
'Укажите фамилию и щелкните на OK.', '');
if fam <> '' // пользователь ввел фамилию
then
begin
with form1.Query1 do begin
Close; // закрыть файл-результат выполнения предыдущего запроса
SQL.Clear; // удалить текст предыдущего запроса
// записываем новый запрос в свойство SQL
SQL.Add('SELECT Fam, Name, Class');
SQL.Add('FROM ":Школа:school.db"');
SQL.Add('WHERE');
SQL.Add('(Fam = "' + fam + '")');
SQL.Add('ORDER BY Name, Fam');
Open; // активизируем выполнение запроса
end;
if Query1.RecordCount <> 0 then
DataSource1.DataSet := Query1 // отобразить рез-т выполнения запроса
else begin
ShowMessage('В БД нет записей, удовлетворяющих критерию запроса.');
DataSource1.DataSet := Table1;
end;
end;
end;
// щелчок на кнопке Все записи
procedure TForm1.Button2Click(Sender: TObject);
begin
DataSource1.DataSet := Table1; // источник данных - таблица
end;
// активизация формы
procedure TForm1.FormActivate(Sender: TObject);
begin
with Session do
begin
ConfigMode := cmSession;
try
{ Если файл данных находиться в том же каталоге,
что и выполняемый файл программы, то в программе
путь к файлу данных может быть получен из командной
строки при помощи функции ExtractFilePath(ParamStr(0)).
В приведенном примере файл данных находиться в подкаталоге
DATA каталога программы. }
// создадим временный псевдоним для базы данны
AddStandardAlias('Школа',
ExtractFilePath(ParamStr(0)) + 'DATA\',
'PARADOX');
Table1.Active := True; // откроем базу данных
finally
ConfigMode := cmAll;
end;
end;
end;
end.
В рассматриваемом варианте программы предполагается, что база данных содержится в подкаталоге DATA того каталога, в котором находится выполняемый файл программы. Создает псевдоним процедура TForm1.FormActivate. Непосредственное создание псевдонима выполняет процедура AddstandardAlias, которой в качестве параметра передается имя псевдонима и соответствующее ему имя каталога. Так как во время разработки программы нельзя знать, в каком каталоге будет размещена программа работы с базой данных и, следовательно, подкаталог базы данных -DATA, имя каталога определяется во время работы программы путем обращения к функциям ParamStr(0) и ExtractFilePatch. Значение первой -полное имя выполняемого файла программы, второй — путь к этому файлу. Таким образом, процедуре AddstandardAiias передается полное имя каталога базы данных.
Динамическая и статическая загрузка DLL.
DLL возможно загружать двумя способами:
статически динамическиДавайте создадим простую библиотеку DLL:
{Project file name: c:\example\exdouble\exdouble.dpr}
library ExDouble;
// my simple dll
function calc_double(r: real): real; stdcall;
begin
result := r * 2;
end;
exports
calc_double index 1;
end;
|
Теперь посмотрим, как её можно загружать:
Статическая загрузка DLL
При таком способе загрузки достаточно поместить файл DLL в директорию приложения или в директорию Windows, или в Windows\System, Windows\Command. Однако, если система не найдёт этого файла в этих директория, то высветится сообщение об ошибке (DLL не найдена, или что-то в этом духе).
unit untMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
function calc_double(r: real): real; stdcall; external 'ExDouble.dll';
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
// в окошке сообщения будет цифра 21
ShowMessage(FloatToStr(calc_double(10.5)));
end;
end.
|
Динамическая загрузка DLL
При динамической загрузке требуется написать немного больше кода.
А вот как это выглядит:
unit untMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
Tcalc_double = function ( r: real ): real;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
hndDLLHandle: THandle;
calc_double: Tcalc_double;
begin
try
// загружаем dll динамически
hndDLLHandle := loadLibrary ( 'ExDouble.dll' );
if hndDLLHandle <> 0 then
begin
// получаем адрес функции
@calc_double := getProcAddress ( hndDLLHandle, 'calc_double' );
// если адрес функции найден
if addr ( calc_double ) <> nil then
begin
// показываем результат ( 21...)
showMessage ( floatToStr ( calc_double ( 10.5 ) ) );
end
else
// DLL не найдена ("handleable")
showMessage ( 'Function not exists...' );
end
else
// DLL не найдена ("handleable")
showMessage ( 'DLL not found...' );
finally
// liberar
freeLibrary ( hndDLLHandle );
end;
end;
end.
|
Динамические и виртуальные методы.
Согласно онлайновой документации, динамические и виртуальные методы семантически идентичны, единственно различие заключается в их реализации, нижеследующий код генерирует указанную ошибку компиляции:
type
t = class
function a : integer; {статический}
function b : integer; virtual;
function c : integer; dynamic;
property i : integer read a; { ok }
property j : integer read b; { ok }
property k : integer read c;
{ ОШИБКА: type mismatch (несовпадение типа) }
end;
|
Динамические массивы.
const
MaxBooleans = (High(Cardinal) - $F) div sizeof(boolean);
type
TBoolArray = array[1..MaxBooleans] of boolean;
PBoolArray = ^TBoolArray;
var
B: PBoolArray;
N: integer;
begin
N := 63579;
{= получение памяти под динамический массив.. =}
GetMem(B, N * sizeof(boolean));
{= работа с массивом... =}
B^[3477] := FALSE;
{= возвращение памяти в кучу =}
{$IFDEF VER80}
FreeMem(B, N * sizeof(boolean));
{$ELSE}
FreeMem(B);
{$ENDIF}
end.
|
Динамические массивы 2.
В. Возможно создавать динамически-изменяющиеся массивы в Delphi?
О. Да. Для начала вам необходимо создать тип массива, использующего самый большой размер, который вам, вероятно, может понадобиться. В действительности, при создании типа никакой памяти не распределяется. Вот когда вы создаете переменную этого типа, тогда компилятор пытается распределить для вас необходимую память. Вместо этого создайте переменную, являющуюся указателем на этот тип. Этим вы заставите компилятор распределить лишь четыре байта, необходимые для размещения указателя.
Прежде, чем вы сможете пользоваться массивом, вам необходимо распределить для него память. Используя AllocMem, вы можете точно управлять выделяемым размером памяти. Для того, чтобы определить необходимое количество байт, которые вы должны распределить, просто умножьте размер массива на размер отдельного элемента массива. Имейте в виду, что самый большой блок, который вы сможете распределить в любой момент в 16-битной среде равен 64Кб. Самый большой блок, который вы можете в любой момент распределить в 32-битной среде равен 4Гб. Для определения максимального числа элементов, которые вы можете иметь в вашем конкретном массиве (в 16-битной среде), разделите 65,520 на размер отдельного элемента. Например: 65520 div SizeOf(LongInt).
Пример объявления типа массива и указателя:
type ElementType = LongInt; const MaxArraySize = (65520 div SizeOf(ElementType)); (* в 16-битной среде *) type MyArrayType = array[1..MaxArraySize] of ElementType; var P: ^MyArrayType; const ArraySizeIWant: Integer = 1500; |
Затем, для распределения памяти под массив, вы могли бы использоваться следующую процедуру:
procedure AllocateArray;
begin
if ArraySizeIWant <= MaxArraySize then
P := AllocMem(ArraySizeIWant * SizeOf(LongInt));
end;
|
Вот процедура, которая с помощью цикла устанавливает величину каждого члена:
procedure AssignValues;
var
I: Integer;
begin
for I := 1 to ArraySizeIWant do
P^[I] := I;
end;
|
Помните также о том, что после использования массива всю распределенную память необходимо освободить. Вот пример того, как избавиться от этого массива:
procedure DeallocateArray; begin P := AllocMem(ArraySizeIWant * SizeOf(LongInt)); end; |
Ниже приведен пример динамического массива:
unit Unit1;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
ElementType = Integer;
const
MaxArraySize = (65520 div SizeOf(ElementType));
{ в 16-битной среде }
type
{ Создаем тип массива. Убедитесь в том, что вы установили
максимальный диапазон, который вам, вероятно, может понадобиться. }
TDynamicArray = array[1..MaxArraySize] of ElementType;
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
{ Создаем переменную типа указатель на ваш тип массива. }
P: ^TDynamicArray;
const
{ Это типизированные константы. В действительности они
являются статическими переменными, инициализирующимися
во время выполнения указанными в исходном коде значениями.
Это означает, что вы можете использовать типизированные
константы точно также, как и любые другие переменные.
Удобство заключается в автоматически инициализируемой величине. }
DynamicArraySizeNeeded: Integer = 10;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
{ Распределяем память для нашего массива. Будь внимательны
и распределяйте размер, в точности необходимый для размещения нового массива.
Если вы попытаетесь записать элемент, выходящий за допустимый диапазон,
компилятор не ругнется, но объект исключения вам обеспечен. }
DynamicArraySizeNeeded := 500;
P := AllocMem(DynamicArraySizeNeeded * SizeOf(Integer));
{ Как присвоить значение пятому элементу массива. }
P^[5] := 68;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
{ Вывод данных. }
Button1.Caption := IntToStr(P^[5]);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
{ Освобождаем распределенную для массива память. }
FreeMem(P, DynamicArraySizeNeeded * SizeOf(Integer));
end;
end.
|
Динамические массивы 3.
Автор: Robert Wittig Вот "демо-модуль", демонстрирующий три различных способа (далеко не все) создания динамических массивов. Все три способа для распределения достаточного количества памяти из кучи используют GetMem, tList используют для добавления элементов в список массива и используют tMemoryStream для того, чтобы распределить достаточно памяти из кучи и иметь к ней доступ, используя поток. Старый добрый GetMem вполне подходит для такой задачи при условии, что массив не слишком велик (<64K).PS. Я не стал ловить в коде исключения (с помощью блоков Try...Finally}, которые могли бы мне помочь выявить ошибки, связанные с распределением памяти. В реальной системе вы должны быть уверены в своем грациозном владении низкоуровневыми операциями с памятью.
{++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{ Форма, демонстрирующая различные методы создания массива с }
{ динамически изменяемым размером. Разместите на форме четыре кнопки,}
{ компоненты ListBox и SpinEdit и создайте, как показано ниже, }
{ обработчики событий, возникающие при нажатии на кнопки. Button1, }
{ Button2 и Button3 демонстрируют вышеуказанных метода. Button4 }
{ очищает ListBox для следующего примера. }
{++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
unit Dynarry1;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, Spin;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
SpinEdit1: TSpinEdit;
ListBox1: TListBox;
Button4: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
type
pSomeType = ^SomeType;
SomeType = Integer;
procedure TForm1.Button1Click(Sender: TObject);
type
pDynArray = ^tDynArray;
tDynArray = array[1..1000] of SomeType;
var
DynArray: pDynArray;
I: Integer;
begin
{ Распределяем память }
GetMem(DynArray, SizeOf(SomeType) * SpinEdit1.Value);
{ Пишем данные в массив }
for I := 1 to SpinEdit1.Value do
DynArray^[I] := I;
{ Читаем данные из массива }
for I := SpinEdit1.Value downto 1 do
ListBox1.Items.Add('Элемент ' + IntToStr(DynArray^[I]));
{ Освобождаем память }
FreeMem(DynArray, SizeOf(SomeType) * SpinEdit1.Value);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
List: tList;
Item: pSomeType;
I: Integer;
begin
{ Создаем список }
List := tList.Create;
{ Пишем данные для списка }
for I := 1 to SpinEdit1.Value do
begin
{ Распределяем уникальный экземпляр данных }
New(Item);
Item^ := I;
List.Add(Item);
end;
{ Читаем данные из списка - базовый индекс списка 0, поэтому вычитаем из I единицу }
for I := SpinEdit1.Value downto 1 do
ListBox1.Items.Add('Элемент ' +
IntToStr(pSomeType(List.Items[I - 1])^));
{ Освобождаем лист }
for I := 1 to SpinEdit1.Value do
Dispose(List.Items[I - 1]);
List.Free;
end;
procedure TForm1.Button3Click(Sender: TObject);
var
Stream: tMemoryStream;
Item: SomeType;
I: Integer;
begin
{ Распределяем память потока }
Stream := tMemoryStream.Create;
Stream.SetSize(SpinEdit1.Value);
{ Пишем данные в поток }
for I := 1 to SpinEdit1.Value do
{ Stream.Write автоматически отслеживает позицию записи,
поэтому при записи данных за ней следить не нужно }
Stream.Write(I, SizeOf(SomeType));
{ Читаем данные из потока }
for I := SpinEdit1.Value downto 1 do
begin
Stream.Seek((I - 1) * SizeOf(SomeType), 0);
Stream.Read(Item, SizeOf(SomeType));
ListBox1.Items.Add('Элемент ' + IntToStr(Item));
end;
{ Освобождаем поток }
Stream.Free;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
ListBox1.Items.Clear;
end;
end.
|
Динамические массивы 4.
Автор: Виталий.
Я хочу написать об использовании динамических массивов. Для того чтобы с ними работать можно использовать два вида объявлния в типах.
type DAr = array of real; var A: DAr; |
Или сразу.
var A:array of real; |
Таким образом мы объяыили ссылку на область памяти. Для того чтобы указать размер воспользуемся процедурой SetLength, ее можно использовать в любых местах и определять размера массива тот, который необходим в данную минуту.
SetLength(A,7) |
Так мы создали массив состоящий из 7 элементов начиная с 0. Важно! Первый элемент в динамическом массиве всегда нулевой. Для определения верхний границы используем функцию Hihg.
I:=High(A); |
I - верхняя граница. Для определения длины Length(A), для определения нижней границы Low(A). При нулевой длине массива High, возращает -1. Пример:
var a,b: array of integer; begin SetLength(a,2); SetLength(b,2); a[0]:=2; b[0]:=3; a:=b; b[0]:=4; end; |
P.S. Не советую изменять длину массивов в DLL, у меня при этом возникала ошибка Acess violation побороть ее мне так и не удалось.
Динамические массивы 5.
Автор: Igor Nikolaev aKa The SpriteИногда разработчик, работая с массивами, не знает какого размера массив ему нужен. Тогда Вам пригодится использование динамических массивов.
var intArray : array of integer; |
При таком объявлении размер массива не указывается. Что бы использовать его дальше необходимо определить его размер (обратите внимание, что размер динамического массива можно устанавливать в программе):
begin intArray:=(New(IntArray,100); //Размер массива? 100 end; |
Решение для динамически создаваемых компонентов.
Предупреждение: Если вы просто хотите во время выполнения приложения создавать компоненты необходимого вам типа, ознакомьтесь с файлом delphi\doc\VB2Delph.wri и следуйте его рекомендациям, лучшего способа изучения этой темы пока не существует. Данный совет повествует об использовании в Delphi RTTI. Во-первых, в вашем приложении необходимо зарегистрировать все классы, экземпляры которых вы собираетесь в каком-то месте кода создавать. Сделать это можно с помощью функций RegisterClass(), RegisterClasses() и RegisterClassAlias().Пример:
procedure TForm1.FormCreate(Sender: TObject); begin RegisterClasses([TButton, TEdit, TMemo, TLabel]); end; |
Пример:
procedure TForm1.CreateClick(Sender: TObject); begin SomeComponent:= TComponentClass(FindClass(ClassName.Text)).Create(Self); (SomeComponent as TControl).Parent := Self; end; |
Пример:
procedure TForm1.SetPropertyClick(Sender: TObject);
var
PropType: PTypeInfo;
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(SomeComponent.ClassInfo, PropertyName.Text);
PropType := PropInfo^.PropType;
case PropType^.Kind of
tkInteger:
SetOrdProp(SomeComponent, PropInfo,
StrToInt(PropertyValue.Text));
tkChar:
SetOrdProp(SomeComponent, PropInfo, Ord(PropertyValue.Text[1]));
tkEnumeration:
SetOrdProp(SomeComponent, PropInfo, GetEnumValue(PropType,
PropertyValue.Text));
tkFloat:
SetFloatProp(SomeComponent, PropInfo,
StrToFloat(PropertyValue.Text));
tkString:
SetStrProp(SomeComponent, PropInfo, PropertyValue.Text);
end;
end;
|
Это прекрасный способ, но он имеет потенциал для массового злоупотребления. Необходимо понимание других путей достижения этой цели и выбор соответствующей техники при создании своих проектов в Delphi.
Динамическое создание формы.
Автор: Steve Schafer Я хочу следующее: мой компонент должен "динамически" создавать форму. я не хочу включать имя модуля создаваемой формы в список используемых модулей моей текущей формы!!!Ок, но модуль, содержащий форму, должен включаться в ваш EXE-файл, после чего вы должны вызвать RegisterClass. Наилучшее место для размещения вызова - секция инициализации модуля, определяющего форму:
unit MyUnit; interface type TMyForm = class(TForm) ... implementation ... initialization RegisterClass(TMyForm); end. |
Теперь вы можете создавать экземпляр этой формы из любого места программы, например так:
var
SomeForm: TForm;
FormClass: TFormClass;
...
FormClass := TFormClass(FindClass('TMyForm'));
SomeForm := FormClass.Create(Application);
...
|
Динамическое распределение памяти.
| Дай бог памати...буквально метра 128. |
uses WinCRT;
procedure TForm1.Button1Click(Sender: TObject);
var
MyArray: array[0..30] of char;
b: ^char;
i: integer;
begin
StrCopy(MyArray, 'Delphi World - это круто!!!');
b := @MyArray;
for i := StrLen(MyArray) downto 0 do
begin
write(b^);
inc(b, sizeof(char));
end;
end;
|
Динамическое распределение памяти 2.
| - Что делает программист после запоя наутро? - Тестирует память. |
Скажем, ваша структура данных выглядит похожей на эту:
type
TMyStructure = record
Name: String[40];
Data: array[0..4095] of Integer;
end;
|
Она слишком большая для глобального распределения, так что вместо объявления глобальной переменной,
var MyData: TMyStructure; |
Объявляете указательный тип,
type PMyStructure = ^TMyStructure; |
И переменную этого типа,
var MyDataPtr: PMyStructure; |
Прежде, чем вы сможете использовать структуру данных, вы должны распределить ее в куче:
New(MyDataPtr); |
И получить к ней доступ через глобальные данные любым удобным для вас способом. Единственное отличие от традиционного способа заключается в необходимости использования символа "^" для обозначения указателя на данные:
MyDataPtr^.Name := 'Советы по Delphi'; MyDataPtr^.Data[0] := 12345; |
И, наконец, после использования памяти, освободите ее:
Dispose(MyDataPtr); |
Динамическое OLE.
| Чем компьютер лучше женщины: 1. Компьютер не скажет: "ты мне не нравишься, я люблю другого". 2. Кожух с компьютера снимается без сопротивления. 3. Изображение на компьютере легко поменять. 4. Звук на колонках легко выключить. 5. У компьютера не бывает месячных. 6. Компьютер не потащит тебя знакомится со своими родителями. 7. Память на компьютере можно увеличить. 8. У девушки не бывает режима турбо. 9. Вирусы компьютера пользователю не передаются. 10. Компьютер можно собрать из тех частей, которые тебе нравятся. 11. Компьютер можно отформатировать. 12. У компьютера не болит голова. 13. Компьютер можно иметь в присутствии других. 14. Компьютер можно иметь везде. 15. К компьютеру можно подключить дополнительные периферийные устройства. 16. Компьютер не залетит. 17. Компьютер потерпит любые извращения. 18. Компьютер не ревнует, когда вы имеете другой компьютер. 19. На компьютер можно поставить пароль. 20. Девушку нельзя перезагрузить и начать всё сначала. 21. Сетевой сервер может одновременно обслуживать уйму народа. 22. На компьютере можно играть с друзьями. 23. Компьютер не заставит тебя на нем женится. 24. Компьютер не обидится, если ты назовешь его другим именем. 25. На компьютере можно отлаживать чужие программы. 26. У компьютера не бывает тещи. 27. Компьютер всегда запомнит, что ему скажешь. 28. Компьютер никогда не опаздывает. 29. Компьютер не боится мышей. 30. Компьютер не требует постоянно спонсировать его. 31. С глюками компьютера легко справится. 32. Компьютер грузится быстрее. 33. Компьютер не волнует, когда ты кончишь. 34. Компьютер мыслит логически. 35. Компьютер не потеет. |
Некоторое время тому назад вы задавали вопрос по поводу динамического OLE без создания диалогового окна для просмотра файла. Я таки добился этого и вот код...
uses...BOleDefs...
procedure TPreview.previewOLEFile(fileName: string);
var
Info: ^BOleInitInfo;
cString: array[0..80] of char;
begin
Screen.Cursor := crHourGlass;
try
GetMem(Info, sizeof(BoleInitInfo));
Info^.How := BOLE_EMBED;
Info^.Where := BOLE_FILE;
Info^.pContainer := nil;
Info^.hIcon := 0;
Info^.Storage := nil;
StrPCopy(cString, fileName);
Info^.Path := cString;
oleDocument.PInitInfo := Info;
ReleaseOLEInitInfo(Info); { данная функция освобождает (getmem) память ! }
oleDocument.Visible := True;
finally
Screen.Cursor := crDefault;
end;
end;
|
Динамические PageControl и TabSheet.

var
T: TTabSheet;
P: TPageControl;
begin
// Создаем PageControl
// При создании получаем ссылку на PageControl, чтобы в дальнейшем на него ссылаться.
P := TPageControl.Create(application);
with P do
begin
Parent := Form1; // устанавливаем его как элемент управления формы.
Top := 30;
Left := 30;
Width := 200;
Height := 150;
end; // with TPageControl
// Создаем 3 страницы
T := TTabSheet.Create(P);
with T do
begin
Visible := True;
// Это необходимо, или форма не будет корректно перерисовываться
Caption := 'Страница 1';
PageControl := P; // Назначаем Tab в Page Control
end; // with
T := TTabSheet.Create(P);
with T do
begin
Visible := True;
// Это необходимо, или форма не будет корректно перерисовываться
Caption := 'Страница 2';
PageControl := P; // Назначаем Tab в Page Control
end; // with
T := TTabSheet.Create(P);
with T do
begin
Visible := True;
// Это необходимо, или форма не будет корректно перерисовываться
Caption := 'Страница 3';
PageControl := P; // Назначаем Tab в Page Control
end; // with
// Создаем 3 кнопки, 1 на страницу
with tbutton.create(application) do
begin
Parent := P.Pages[0]; // "Указываем" кнопке родительскую страницу
Caption := 'Привет, страница 1';
Left := 0;
Top := 0;
end; // with
with tbutton.create(application) do
begin
Parent := P.Pages[1]; // "Указываем" кнопке родительскую страницу
Caption := 'Привет, страница 2';
Left := 50;
Top := 50;
end; // with
with tbutton.create(application) do
begin
Parent := P.Pages[2]; // "Указываем" кнопке родительскую страницу
Caption := 'Привет, страница 3';
Left := 100;
Top := 90;
end; // with
// Это должно быть сделано, или Tab первоначально не синхронизируется
// с правильной страницей. Только в случае, если у вас более чем одна страница.
P.ActivePage := P.Pages[1];
P.ActivePage := P.Pages[0]; // Реально показываемая страница
end;
|
Динамические PageControl и TabSheet 2.
В данном документе показана технология динамического добавления страниц компонента PageControl (объектов TTabSheet) к элементу управления Windows 95/NT PageControl (объект TPageControl). Оба этих объекта объявлены в модуле ComCtrls. Поэтому убедитесь в том, что ComCtrls указан в списке используемых модулей. Как динамически создать PageControlПрежде, чем мы приступим к динамическому созданию страниц, давайте динамически создадим PageControl (если он еще не на форме). Это делается посредством вызова конструктора TPageControl Create с параметром owner, равным Self. Конструктор Create возвращает объектную ссылку на вновь созданный объект PageControl и назначает его переменной 'PageControl'. Вторым шагом будет установка свойства PageControl Parent в Self. Свойство Parent определяет где должен быть отображен новый PageControl; в нашем случае это будет сама форма. Вот кусок кода, демонстрирующий вышесказанное:
var PageControl : TPageControl; PageControl := TPageControl.Create(Self); PageControl.Parent := Self; |
Существует два основных способа добавления новых страниц к PageControl. Сначала вы должны динамически создать TTabSheet следующим образом:
var TabSheet : TTabSheet; TabSheet := TTabSheet.Create(Self); |
Затем ему необходимо присвоить заголовок следующей командой:
TabSheet.Caption := 'Закладка 1'; |
И, наконец, самая важное действие заключается в том, что новой странице необходимо сообщить, какому объекту PageControl она принадлежит. Это делается с помощью присваивания свойством TTabSheet PageControl переменной-ссылки TPageControl, типа той, которую мы создали выше (PageControl). Вот кусок кода, демонстрирующий вышесказанное:
TabSheet.PageControl := PageControl; |
Ключевым моментом при создании и размещении элемента управления на странице TabSheet является назначение свойства Parent на переменную-ссылку класса TTabSheet. Вот пример:
var Button : TButton; Button := TButton.Create(Self); Button.Caption := 'Кнопка 1'; Button.Parent := TabSheet; |
Полный код примера.
// Код использует форму с единственной на ней кнопкой.
unit DynamicTabSheetsUnit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, Buttons;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure TestMethod(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses ComCtrls;
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
PageControl: TPageControl;
TabSheet: TTabSheet;
begin
// Создаем PageControl
PageControl := TPageControl.Create(Self);
PageControl.Parent := Self;
// Создаем первую страницу и связываем ее с PageControl
TabSheet := TTabSheet.Create(Self);
TabSheet.Caption := 'Закладка 1';
TabSheet.PageControl := PageControl;
// Создаем первую страницу
with TButton.Create(Self) do
begin
Caption := 'Кнопка 1';
OnClick := TestMethod; // Назначаем обработчик события
Parent := TabSheet;
end;
// Создаем вторую страницу и связываем ее с PageControl
TabSheet := TTabSheet.Create(Self);
TabSheet.Caption := ' Закладка 2';
TabSheet.PageControl := PageControl;
end;
procedure TForm1.TestMethod(Sender: TObject);
begin
ShowMessage('Привет');
end;
end.
|
Как удобнее работать с динамически формируемыми запросами.
Автор: Nomadic В процессе работы с БД иногда необходимо выполнить какие-то мелкие запросы. Держать для этого где-то временную Query меня лично ломает, посему ловите творение (под Delphi) - модуль для создания временных TQuery и работы с ними.Примеры использования:
var
S: string;
...
S := FastLookUp(format('select A.F1 from A,B where A.F4=B.F4 and
B.F9=%d', [1]));
with GiveMeResultSet('select*from A where F1="777"') do
try
....
finally
Free; {не забудьте!}
end;
.....
if not ExecuteSQL('delete from A') then
ShowMessage('Something Wrong');
.....
|
Сам модуль идёт ниже.
{
Temporary Queries Creatin' and handlin'
(c) 1997-98 by Volok Alexander (D1/D2)
creation date: 30.10.1997
last update : 17.06.1998
}
unit TmpQuery;
interface
uses
DBTables;
const
InternalDBname = 'MAIN'; {Изменять по вкусу - TDataBase.DataBaseName}
type
TSQLScript = {$IFDEF WIN32}string{$ELSE}PChar{$ENDIF};
{Создаст куери с текстом запроса, но не откроет его}
function CreateTempQuery(SQLscript: TSQLscript): TQuery;
{Создаст куери и откроет запрос - не забудьте прибить}
function GiveMeResultSET(SQLscript: TSQLscript): TQuery;
{Проверит непустоту выборки, заданной ...}
function CheckExistence(SQLscript: TSQLscript): boolean;
{Вытащит аж одно значение(лукап) из выборки, заданной ...}
function FastLookUP(SQLscript: TSQLscript): string;
{Выполнит запрос и сообщит результат}
function ExecuteSQL(SQLscript: TSQLscript): boolean;
implementation
uses
Forms;
function CreateTempQuery(SQLscript: TSQLscript): TQuery;
begin
Result := TQuery.Create(Application);
with Result do
begin
DatabaseName := InternalDBname;
{$IFDEF WIN32}
SQL.Text := SQLscript;
{$ELSE}
SQL.SetText(SQLscript);
{$ENDIF}
end;
end;
function ExecuteSQL(SQLscript: TSQLscript): boolean;
begin
with CreateTempQuery(SQLscript) do
begin
try
ExecSQL;
Result := True;
except
Result := False;
end;
Free;
end;
end;
function CheckExistence(SQLscript: TSQLscript): boolean;
begin
with GiveMeResultSET(SQLscript) do
begin
Result := not EOF;
Free;
end;
end;
function GiveMeResultSET(SQLscript: TSQLscript): TQuery;
begin
Result := CreateTempQuery(SQLscript);
with Result do
try
Open;
except
Free;
Result := nil;
end;
end;
function FastLookUP(SQLscript: TSQLscript): string;
begin
with GiveMeResultSET(SQLscript) do
begin
try
Result := Fields[0].AsString;
except
Result := '';
end;
Free;
end;
end;
end.
|
Динамическое использование DLL.
| Автор: Dr. Bob |
| Y3K. На пляже мама с ребенком. - Мам, а откуда берется песок? - Из микросхем. Их сердцевина состоит из кремния. Его собирают, перемалывают и сжигают, и получают песок. |
Проследим, что получается, если вы используете в модуле явный импорт DLL или в вашем коде вы объявляете функцию DLL с внешним 'DLLNAME'. В этом случае компоновщик установит ссылки на DLL-функции в таблице импортируемых имен скомпонованного EXE-файла и загрузчик Windows автоматически загрузит DLL (во время выполнения главного блока) вместе с файлом EXE:
procedure Foo(X: Integer); external 'BAR' index 1; |
Динамически связанная DLL, использующая модуль явного импорта, пользуется методами VB, каковые вы также можете использовать, но делая это "вручную". С помощью LoadLibrary по мере необходимости вы загружаете DLL, получаете указатели на экспортируемые функции, которые вы предполагаете использовать с GetProcAddress и освобождаете DLL с помощью FreeLibrary, когда она вам больше не нужна:
var
Hbar: Thandle;
Foo: procedure(X: Integer);
begin
Hbar := LoadLibrary('BAR.DLL');
if Hbar >= 32 then { успешно }
begin
Foo := GetProcAddress(HBar, 'FOO');
...
Foo(1);
...
FreeLibrary(HBar);
end
else
MessageDlg('Ошибка: не могу найти BAR.DLL', mtError, [mbOk], 0)
end.
|
Удаление и добавление элементов в динамический массив.
Автор: http://sunsb.dax.ru Крутая штука динамический массив. Очень быстрая и здоровая реализация. Единственное, чего на мой взгляд не хватает, это механизма удаления элемента из середины массива и соответственно вставки в середину. Насколько я понял ( и проверил ), в памяти массив хранится по-разному в зависимости от типа его элементов. Скажм если в массиве строки(!! не shortString ) - хранятся указатели на них, а если прямоугольники (TRect) - то непосредственно сами прямоугольники.Ниже привожу подпрограммы удаления и добавления элемента.
procedure delElem( var A:TRectArray; Index:integer );
var Last : integer;
begin
Last:= high( A );
if Index < Last then move( A[Index+1], A[ Index ],
(Last-Index) * sizeof( A[Index] ) );
setLength( A, Last );
end;
procedure addElem( var A: TRectArray; Index: integer;
ANew: TRect );
var Len : integer;
begin
Len:= Length( A );
if Index > = Len then Index := Len+1;
setLength( A, Len+1);
move( A[Index], A[ Index+1 ],
(Len-Index) * sizeof( A[Index] ));
A[Index] := ANew;
end;
|
Подпрограмма delElem полностью универсальна, а в addElem Вам нужно поменять тип добовляемого елемента (ANew) на требуемый.
Использование DynArrayFromVariant.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Button1Click(Sender: TObject);
type
T2DIntArray = array of array of Integer;
var
DynArray: T2DIntArray;
V: Variant;
i, k: Integer;
begin
V := VarArrayCreate([0, 1, 0, 1], varInteger);
V[0, 0] := 00;
V[0, 1] := 01;
V[1, 0] := 10;
V[1, 1] := 11;
DynArrayFromVariant(Pointer(Dynarray), V, TypeInfo(T2DIntArray));
for i := 0 to High(Dynarray) do
for k := 0 to High(Dynarray[i]) do
memo1.Lines.add(IntToStr(DynArray[i, k]));
end;
{
The problem with DynArrayFromVariant is that you can only use it on
variant arrays with 0-based indexes, trying to use it on an array with 1
as lower bound blows up. Since the documentation is silent on that i
would consider it a bug.
}
Динамическая загрузка DLL.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
There are two possibilities to load a dll:
1. Static loading of a DLL means that the DLL is loaded when the application is executed.
This is the easier option to dynamic loading, as you'll see, however it means that if the DLL
is missing, your program will not run.
}
{Examples:}
{a. Import a function called MYImportFunction from MYLIBRARY.dll}
procedure MYImportFunction; external 'MYLIBRARY.dll';
{b. Import a routine under a different name from the one it has in the library. }
procedure MYImportFunction; external 'MYLIBRARY.dll' name 'MyNewImportFunctionName';
{c. By ordinal value: Has to be the same value as when using the exports
keyword when making the DLL}
procedure MYImportFunction; external 'MYLIBRARY.dll' index 10;
{
2. Dynamic loading
By dynamically loading a DLL you decide at runtime which DLL to use.
This means you can give your program different functionality depending on which
DLL's are present (freeware or shareware versions of a program).
Or also if you want to load a library whose name or path must be computed at
run time or generated from user input.
Dynamic loading of a DLL loads the DLL in your application when
it is needed and unload it once you its work is done.
It requires more code to use,
however it more resource friendly than static loading.
}
{**********************************************************}
{
Es gibt zwei Мцg1iсhкеitеn, eine DLL zu laden.
1. Statisches Laden
Beim statischen Laden wird die zu importierende Prozedur/Funktion mit "external" deklariert.
Die Datei 'MYLIBRARY.DLL' wird dann beim Programmstart geladen.
}
{Beispiele:}
{a. Importiert eine Funktion MYImportFunction aus der Dll MYLIBRARY.dll }
procedure MYImportFunction; external 'MYLIBRARY.dll';
{b. Man kann auch eine Routine unter einem anderen Namen importieren als der in der dll. }
procedure MYImportFunction; external 'MYLIBRARY.dll' name 'MyNewImportFunctionName';
{c. Einen Index angeben: }
procedure MYImportFunction; external 'MYLIBRARY.dll' index 10;
// 2. Dynamisches Laden
{
Beim Dynamischen Laden erfolgt der Zugriff auf die Routinen mit
direkten Windows-API-Aufrufen (LoadLibrary, FreeLibrary, GetProcAddress).
Die importierten Routinen werden ььег prozedurale Variablen referenziert.
Die importierten DLLs werden erst bei der Аиsfьhгиng des Quelltextes geladen.
Somit wird Speicherplatz eingespart und das Programm wird auch аиsgеfьhгt,
wenn einige DLLs fehlen.
}
{**********************************************************}
// Example for dynamically loading a DLL
// Beispiel um eine Dll dynamisch zu laden:
type
TDLLFunction = function(someParam: TSomeType): TSomeOtherType;
{
To execute such a function by name from a named DLL one could use a
"caller" function like
}
function CallDLLFunction(const dllname, functionname: string;
theParameter: TSomeType): TSomeOtherType;
var
hDLL: THandle;
theFunction: TDLLFunction;
buf: array [0..144] of Char;
begin
// Get a handle to the DLL module.
// das Handle zum DLL Modul ermitteln.
hDLL := LoadLibrary(StrPCopy(buf, dllname));
// If the handle is valid, try to get the function address.
// Wenn das Handle gь1tig ist, versuche die Adresse der Funktion zu ermitteln
if hDLL <> 0 then
begin
// Return the address of the specified exported (DLL) function.
// Adresse der Dll-Funktion ermitteln
try
@theFunction := GetProcAddress(hDll, StrPCopy(buf, functionname));
// If the function address is valid, call the function.
// Wenn die Funktion gefunden wurde...
if @theFunction <> nil then
Result := theFunction(theParameter)
else
raise EDLLException.CreateFmt('Unable to link to function %s in DLL %s!',
[functionname, dllname]);
finally
// Free the DLL module.
// Dll wieder freigeben.
FreeLibrary(hDLL);
end;
end
else
raise EDLLException.CreateFmt('Unable to load DLL %s!'#13#10 +
'Reason: %s.', [dllname, DLLErrors[hDLL]]);
end;
Динамический список.
unit dlist1_;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Edit1: TEdit; // фамилия
Edit2: TEdit; // имя
Button1: TButton; // кнопка Добавить
Button2: TButton; // кнопка Показать
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
type
TPStudent = ^TStudent; //указатель на тип TStudent
TStudent = record
f_name: string[20]; // фамилия
l_name: string[20]; // имя
next: TPStudent; // следующий элемент списка
end;
var
head: TPStudent; // начало (голова) списка
// добавить элемент в начало списка
procedure TForm1.Button1Click(Sender: TObject);
var
curr: TPStudent; // новый элемент списка
begin
new(curr); // выделить память для элемента списка
curr^.f_name := Edit1.Text;
curr^.l_name := Edit2.Text;
// добавление в начало списка
curr^.next := head;
head := curr;
// очистить поля ввода
Edit1.text := '';
Edit2.text := '';
end;
// вывести список
procedure TForm1.Button2Click(Sender: TObject);
var
curr: TPStudent; // текущий элемент списка
n: integer; // длина (кол-во элементов) списка
st: string; // строковое представление списка
begin
n := 0;
st := '';
curr := head; // указатель на первый элемент списка
while curr <> nil do
begin
n := n + 1;
st := st + curr^.f_name + ' ' + curr^.l_name + #13;
curr := curr^.next; // указатель на следующий элемент
end;
if n <> 0
then ShowMessage('Список:' + #13 + st)
else ShowMessage('В списке нет элементов.');
end;
end.
Скачать весь проект
Динамический список 2.
unit dlist2_;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Button1: TButton;
Button2: TButton;
Label3: TLabel;
Edit1: TEdit;
Edit2: TEdit;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
type
TPStudent = ^TStudent; //указатель на тип TStudent
TStudent = record
f_name: string[20]; // фамилия
l_name: string[20]; // имя
next: TPStudent; // следующий элемент списка
end;
var
head: TPStudent; // начало (голова) списка
procedure TForm1.Button1Click(Sender: TObject);
var
node: TPStudent; // новый узел списка
curr: TPStudent; // текущий узел списка
pre: TPStudent; // предыдущий, относительно curr, узел
begin
new(node); // создание нового элемента списка
node^.f_name := Edit1.Text; // фамилия
node^.l_name := Edit2.Text; // имя
// добавление узла в список
// сначала найдем подходящее место в списке для узла
curr := head;
pre := nil;
{ Внимание!
если приведенное ниже условие заменить
на (node.f_name>curr^.f_name)and(curr<>NIL)
то при добавлении первого узла возникает ошибка времени
выполнения, так как curr = NIL и, следовательно,
переменной curr.^name нет!
В используемом варианте условия ошибка не возникает, так как
сначала проверяется условие (curr <> NIL), значение которого
FALSE и второе условие в этом случае не проверяется.
}
while (curr <> nil) and (node.f_name > curr^.f_name) do
begin
// введенное значение больше текущего
pre := curr;
curr := curr^.next; // к следующему узлу
end;
if pre = nil
then
begin
// новый узел в начало списка
node^.next := head;
head := node;
end
else
begin
// новый узел после pre, перед curr
node^.next := pre^.next;
pre^.next := node;
end;
Edit1.text := '';
Edit2.text := '';
Edit1.SetFocus;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
curr: TPStudent; // текущий элемент списка
n: integer; // длина (кол-во элементов) списка
st: string; // строковое представление списка
begin
n := 0;
st := '';
curr := head;
while curr <> nil do
begin
n := n + 1;
st := st + curr^.f_name + ' ' + curr^.l_name + #13;
curr := curr^.next;
end;
if n <> 0
then ShowMessage('Список:' + #13 + st)
else ShowMessage('В списке нет элементов.');
end;
procedure TForm1.FormActivate(Sender: TObject);
begin
head := nil;
end;
end.
Скачать весь проект
Динамический список 3.
unit dlist3_;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Button1: TButton;
Button2: TButton;
Label3: TLabel;
Edit1: TEdit;
Edit2: TEdit;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
type
TPStudent = ^TStudent; //указатель на тип TStudent
TStudent = record
f_name: string[20]; // фамилия
l_name: string[20]; // имя
next: TPStudent; // следующий элемент списка
end;
var
head: TPStudent; // начало (голова) списка
procedure TForm1.Button1Click(Sender: TObject);
var
node: TPStudent; // новый узел списка
curr: TPStudent; // текущий узел списка
pre: TPStudent; // предыдущий, относительно curr, узел
begin
new(node); // создание нового элемента списка
node^.f_name := Edit1.Text;
node^.l_name := Edit2.Text;
// добавление узла в список
// сначала найдем подходящее место в списке для узла
curr := head;
pre := nil;
{ Внимание!
если приведенное ниже условие заменить
на (node.f_name>curr^.f_name)and(curr<>NIL)
то при добавлении первого узла возникает ошибка времени
выполнения, так как curr = NIL и, следовательно,
переменной curr.^name нет!
В используемом варианте условия ошибка не возникает, так как
сначала проверяется условие (curr <> NIL), значение которого
FALSE и второе условие в этом случае не проверяется.
}
while (curr <> nil) and (node.f_name > curr^.f_name) do
begin
// введенное значение больше текущего
pre := curr;
curr := curr^.next; // к следующему узлу
end;
if pre = nil
then
begin
// новый узел в начало списка
node^.next := head;
head := node;
end
else
begin
// новый узел после pre, перед curr
node^.next := pre^.next;
pre^.next := node;
end;
Edit1.text := '';
Edit2.text := '';
Edit1.SetFocus;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
curr: TPStudent; // текущий элемент списка
n: integer; // длина (кол-во элементов) списка
st: string; // строковое представление списка
begin
n := 0;
st := '';
curr := head;
while curr <> nil do
begin
n := n + 1;
st := st + curr^.f_name + ' ' + curr^.l_name + #13;
curr := curr^.next;
end;
if n <> 0
then ShowMessage('Список:' + #13 + st)
else ShowMessage('В списке нет элементов.');
end;
procedure TForm1.FormActivate(Sender: TObject);
begin
head := nil;
end;
// щелчок на кнопке Удалить
procedure TForm1.Button3Click(Sender: TObject);
var
curr: TPStudent; // текущий, проверяемый узел
pre: TPStudent; // предыдущий узел
found: boolean; // TRUE - узел, который надо удалить, есть в списке
begin
if head = nil then
begin
MessageDlg('Список пустой!', mtError, [mbOk], 0);
Exit;
end;
curr := head; // текущий узел - первый узел
pre := nil; // предыдущего узла нет
found := FALSE;
// найти узел, который надо удалить
while (curr <> nil) and (not found) do
begin
if (curr^.f_name = Edit1.Text) and (curr^.l_name = Edit2.Text)
then found := TRUE // нужный узел найден
else // к следующему узлу
begin
pre := curr;
curr := curr^.next;
end;
end;
if found then
begin
// нужный узел найден
if MessageDlg('Узел будет удален из списка!',
mtWarning, [mbOk, mbCancel], 0) <> mrYes
then Exit;
// удаляем узел
if pre = nil
then head := curr^.next // удаляем первый узел списка
else pre^.next := curr.next;
Dispose(curr);
MessageDlg('Узел' + #13 +
'Имя:' + Edit1.Text + #13 +
'Фамилия:' + Edit2.Text + #13 +
'удален из списка.',
mtInformation, [mbOk], 0);
end
else // узла, который надо удалить, в списке нет
MessageDlg('Узел' + #13 +
'Имя:' + Edit1.Text + #13 +
'Фамилия:' + Edit2.Text + #13 +
'в списке не найден.',
mtError, [mbOk], 0);
Edit1.Text := '';
Edit1.Text := '';
Edit1.SetFocus;
end;
end.
Скачать весь проект
Простое использование TParser.
Автор: Mike Scott ...можно ли с помощью TParser "вытащить" из строки целочисленные значения, разделенные между собой пробелом, например, из строки '345 998 223 546 777' получить пять чисел?Как этот пример? Он парсирует текст из поле редактирования с именем Edit1 и помещает полученные числа с список с именем Listbox1:
procedure TForm1.Button1Click(Sender: TObject);
var
Stream: TStream;
s: string;
begin
Stream := TMemoryStream.Create;
try
{ получаем текст области редактирования и помещаем его в поток }
s := Edit1.Text;
Stream.WriteBuffer(s[1], length(s));
{ сбрасываем стартовую позицию потока и создаем парсер }
Stream.Position := 0;
with TParser.Create(Stream) do
try
{ "добываем" числа и добавляем их в список }
while Token <> toEOF do
begin
CheckToken(toInteger); { возбуждаем исключение, если не toInteger }
Listbox1.Items.Add(IntToStr(TokenInt));
NextToken;
end;
finally
Free;
end;
finally
Stream.Free;
end;
end;
|
Как вы можете видеть, бОльшая часть кода тривиальна: работа с потоком, его создание и освобождение, и парсер, естественно, с обработкой ошибок<g>. CheckToken проверяет, является ли текущий признак ожидаемым, и, если нет, генерирует исключительную ситуацию. В нашем случае мы ищем целые (toInteger), и читаем их из TParser.TokenInt. Просто!
Расчет расстояния между двумя точками на земной поверхности.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Расчет расстояния между двумя точками на земной поверхности.
Расчет расстояния между 2мя точками на земной поверхности методом Винсенти.
Dimka Maslov:
Lat1, Lon1 - широта и долгота точки 1 в градусах
Lat2, Lon2 - широта и долгота точки 2 в градусах
Функция возвращает результат в метрах.
Автор, правда, забыл упомянуть о правиле знаков для южных широт и западных
долгот...
Зависимости: Math
Автор: Вячеслав
Copyright: Опубликован в Survey Review №175 за Апрель 1976г.
Дата: 19 марта 2003 г.
***************************************************** }
function Vincenty(Lat1, Lon1, Lat2, Lon2: Extended): Extended;
const // Параметры эллипсоида:
a = 6378245.0;
f = 1 / 298.3;
b = (1 - f) * a;
EPS = 0.5E-30;
var
APARAM, BPARAM, CPARAM, OMEGA, TanU1, TanU2,
Lambda, LambdaPrev, SinL, CosL, USQR, U1, U2,
SinU1, CosU1, SinU2, CosU2, SinSQSigma, CosSigma,
TanSigma, Sigma, SinAlpha, Cos2SigmaM, DSigma: Extended;
begin
lon1 := lon1 * (PI / 180);
lat1 := lat1 * (PI / 180);
lon2 := lon2 * (PI / 180);
lat2 := lat2 * (PI / 180); //Пересчет значений координат в радианы
TanU1 := (1 - f) * Tan(lat1);
TanU2 := (1 - f) * Tan(lat2);
U1 := ArcTan(TanU1);
U2 := ArcTan(TanU2);
SinCos(U1, SinU1, CosU1);
SinCos(U2, SinU2, CosU2);
OMEGA := lon2 - lon1;
lambda := OMEGA;
repeat //Начало цикла итерации
LambdaPrev := lambda;
SinCos(lambda, SinL, CosL);
SinSQSigma := (CosU2 * SinL * CosU2 * SinL) +
(CosU1 * SinU2 - SinU1 * CosU2 * CosL) *
(CosU1 * SinU2 - SinU1 * CosU2 * CosL);
CosSigma := SinU1 * SinU2 + CosU1 * CosU2 * CosL;
TanSigma := Sqrt(SinSQSigma) / CosSigma;
if TanSigma > 0 then
Sigma := ArcTan(TanSigma)
else
Sigma := ArcTan(TanSigma) + Pi;
if SinSQSigma = 0 then
SinAlpha := 0
else
SinAlpha := CosU1 * CosU2 * SinL / Sqrt(SinSQSigma);
if (Cos(ArcSin(SinAlpha)) * Cos(ArcSin(SinAlpha))) = 0 then
Cos2SigmaM := 0
else
Cos2SigmaM := CosSigma -
(2 * SinU1 * SinU2 / (Cos(ArcSin(SinAlpha)) * Cos(ArcSin(SinAlpha))));
CPARAM := (f / 16) * Cos(ArcSin(SinAlpha)) * Cos(ArcSin(SinAlpha)) *
(4 + f * (4 - 3 * Cos(ArcSin(SinAlpha)) * Cos(ArcSin(SinAlpha))));
lambda := OMEGA + (1 - CPARAM) * f * SinAlpha * (ArcCos(CosSigma) +
CPARAM * Sin(ArcCos(CosSigma)) * (Cos2SigmaM + CPARAM * CosSigma *
(-1 + 2 * Cos2SigmaM * Cos2SigmaM)));
until Abs(lambda - LambdaPrev) < EPS; // Конец цикла итерации
USQR := Cos(ArcSin(SinAlpha)) * Cos(ArcSin(SinAlpha)) *
(a * a - b * b) / (b * b);
APARAM := 1 + (USQR / 16384) *
(4096 + USQR * (-768 + USQR * (320 - 175 * USQR)));
BPARAM := (USQR / 1024) * (256 + USQR * (-128 + USQR * (74 - 47 * USQR)));
DSigma := BPARAM * SQRT(SinSQSigma) * (Cos2SigmaM + BPARAM / 4 *
(CosSigma * (-1 + 2 * Cos2SigmaM * Cos2SigmaM) - BPARAM / 6 * Cos2SigmaM *
(-3 + 4 * SinSQSigma) * (-3 + 4 * Cos2SigmaM * Cos2SigmaM)));
Result := b * APARAM * (Sigma - DSigma);
end;
Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
var
R: Extended;
begin
R := Vicenty(59.8833, 30.2333, 55.7667, 37.5833);
ShowMessageFmt('%g', [R]);
end;
Как сэкономить память в программах.
| Что общего между морской свинкой и женщиной-программистом? То, что морская свинка - она и не свинка, и не морская. |
Обычно, когда класс располагается в памяти, то между полями остаются небольшие пространства, несодержащие никакой информации. Оказывается можно избавиться от таких участков памяти и соответственно Ваше приложение будет меньше расходовать оперативной памяти.
Но сначала обратимся к основам типов данных, используемых в Delphi, и детально рассмотрим - сколько байт памяти занимает каждый тип данных:
1 байтbооlеаn, char and byte 2 байтsmаllInt, word, wordbool 4 байтstring, pointers, longint, integerТеперь давайте посмотрим на объявление класса в нашем исходном коде:
TMyClass = class
private
field1: char;
field2: longint;
field3: boolean;
field4: string;
field5: byte;
field6: integer;
public
procedure proc1;
end;
|
Теперь просуммируем байты, которы занимает каждый тип данных. По идее должно получиться 15 байт, но на самом деле это не так. Реальный размер, занимаемый данным экземпляром класса будет составлять 24 байта, т. е. 4 байта на каждое поле. Почему ? Потому что поумолчанию в Delphi, по правилам оптимизации, каждое поле располагается от предыдущего со сдвигом на 4 байта: field1 занимает 1 байт, поидее поле field2 должно следовать сразу же за field1, но по правилам оптимизации, остаются 3 байта не содержащие никакой информации, а следовательно напрасно потерянные. А если бы field2 был бы длиной в 1 байт или 2 байта, то он был бы помещён сразу же за полем field1, потому что это не нарушает правил оптимизации.
Какой же напрашивается вывод ? А если изменить порядок объявления переменных в классе ? Я просто сгруппировал переменные по их размеру (байтовому): вместе все однобайтовые, соответственно вместе все двухбайтовые и т.д.
Вот так стал выглядеть наш класс:
TMyClass = class
private
field1: char;
field3: boolean;
field5: byte;
field2: longint;
field4: string;
field6: integer;
public
procedure proc1;
end;
|
С такой организацией классы, его длина стала 16 байт (сэкономили 8 байт на каждом экземпляре данного класса). Конечно же это не большая экономия памяти, но в тех случая, когда класс инициализируется многократно либо класс довольно велик, то такая экономия довольно ощутима.
Обработка исключений EDBEngineError.
| Error 109: Error 108. |
В конструкции try..except для создания экземпляра DBEngineError необходим синтаксис, показаный ниже:
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
if Edit1.Text > ' ' then
begin
Table1.FieldByName('Number').AsInteger := StrToInt(Edit1.Text);
try
Table1.Post;
except
on E: EDBEngineError do
ShowError(E);
end;
end;
end;
|
Процедура ShowError в качестве параметра получает ссылку на объект EDBEngineError и "опрашивает" его на предмет наличия информации об ошибках. В приведенном ниже примере информация об ошибках отображается в компоненте TMemo. В реальной жизни отображение ошибок может и не понадобится, но информация о них является логикой работы приложения, расширяющая его функциональность так, чтобы оно могло правильно реагировать на них. Первым шагом необходимо вычислить количество реально произошедших ошибок. Эта величина хранится в свойстве ErrorCounnt. Данное свойство имеет тип Integer и может использоваться для создания цикла, в теле которого можно по очереди получить информацию о каждой ошибке, содержащейся в объекте. В теле цикла поочередно ErrorCount-раз перебираются все содержащиеся в объекте ошибки (как вы помните, каждая ошибка представляет собой запись-элемент свойства Errors), "достается" о них вся информация и помещается в компонент TMemo.
procedure TForm1.ShowError(AExc: EDBEngineError);
var
i: Integer;
begin
Memo1.Lines.Clear;
Memo1.Lines.Add('Количество ошибок: ' + IntToStr(AExc.ErrorCount));
Memo1.Lines.Add('');
{Перебираем все записи Errors}
for i := 0 to AExc.ErrorCount - 1 do
begin
Memo1.Lines.Add('Сообщение: ' + AExc.Errors[i].Message);
Memo1.Lines.Add(' Категория: ' +
IntToStr(AExc.Errors[i].Category));
Memo1.Lines.Add(' Код ошибки: ' +
IntToStr(AExc.Errors[i].ErrorCode));
Memo1.Lines.Add(' Субкод: ' +
IntTooStr(AExc.Errors[i].SubCode));
Memo1.Lines.Add(' Реальная ошибка: ' +
IntToStr(AExc.Errors[i].NativeError));
Memo1.Lines.Add('');
end;
end;
|
Создать редактируемый TListBox.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{NOTE : This will also work with an OwnerDraw ListBox
But be sure you "pad" the Left Property of the Edit so
Any icons etc, do not get covered up by the TEdit.
And also be aware that there will be a bit of "flashing"
if you do use an OwnerDraw ListBox}
{This is in a Popup menu.}
procedure TForm1.Change1Click(Sender: TObject);
var
I9: Integer;
ColInt: Integer;
LRect: TRect;
begin
LRect := ListBox1.ItemRect(ListBox1.ItemIndex);
{Set the size of the TEdit}
Edit1.Top := LRect.Top + 1;
Edit1.Left := LRect.Left + 1;
Edit1.Width := ListBox1.Canvas.TextWidth(ListBox1.Items.Strings[ListBox1.ItemIndex]) + 6;
Edit1.Height := (LRect.Bottom - LRect.Top) + 1;
Edit1.Text := ListBox1.Items.Strings[ListBox1.ItemIndex];
ListBox1.Selected[ListBox1.ItemIndex] := False;
Edit1.Visible := True;
Edit1.SelectAll;
Edit1.SetFocus;
end;
procedure TForm1.ListBox1Click(Sender: TObject);
begin
Edit1.Visible := False;
end;
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
var
I8: Integer;
begin
if Key = #13 then
begin
I8 := ListBox1.ItemIndex;
ListBox1.Items.Delete(ListBox1.ItemIndex);
ListBox1.Items.Insert(I8, Edit1.Text);
Edit1.Visible := False;
Key := #0;
end;
end;
Массив Edit-компонентов.
| Не стОит давать деньги программисту (особенно WINDOWS-программисту), т.к. если ему удастся на них что-нибудь купить , то он вернет Вам 0 (ноль) как результат успешно выполненного действия. |
procedure DoSomethingWithEditControls;
var
K: Integer;
EditArray: array[0..99] of Tedit;
begin
try
for K := 0 to 99 do
begin
EditArray[K] := TEdit.Create(Self);
EditArray[K].Parent := Self;
{Устанавливаем необходимые свойства TEdit}
SetSomeOtherPropertiesOfTEdit;
Left := 100;
Top := K * 10;
{Что-то делаем при перемещении мыши}
OnMouseMove := WhatToDoWhenMouseIsMoved;
end;
{Делаем все что хотим с полученным массивом Edit-компонентов}
DoWhateverYouWantToDoWithTheseEdits;
finally
for K := 0 to 99 do
EditArray[K].Free;
end;
end;
|
Матрица на основе TEdit.
| Автор: Mike Orriss |
| Для продвижения своих многоплатформных решений, одна из компьютерных компаний решила создать свой формат HELP-файлов на базе ".RTF" (rich text format). Новый формат было решено назвать ".RTFM" |
Допустим, если они имеют имена с Edit1 по Edit 9, то можно попробовать сделать так:
var
eds: array[1..3,1..3] of TEdit;
ix: integer;
ed: TEdit;
begin
for ix := 0 to 8 do
begin
ed := FindComponent('Edit'+IntToStr(ix+1)) as TEdit;
if ed <> nil then
eds[ix div 3 + 1,ix mod 3 + 1] := ed;
end;
end;
|
Затем, допустим, вам захотелось скопировать текст из строки 1 в строку 2:
for ix := 1 to 3 do eds[2,ix].Text := eds[1,ix].Text; |
Числовая маска компонента Edit.
| Автор: Олег Леонтьев |
| Я здесь лежал, и здесь мои друзья С таким похмельем долго жить нельзя Средь глючных вИндов в пьяной тишине, Какой-то юзер подпевает мне... |
Я предлагаю обработать событие OnKeyPress следующим образом:
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = ',' then
Key := DecimalSeparator; // запятую заменяем на разделитель для числа
if Key = '.' then
Key := DecimalSeparator; // точку заменяем на сепаратор (на всякий случай)
if not (Key in ['0'..'9','-',DecimalSeparator,#8,#13]) // символы которые пропускаются
or ((Key = DecimalSeparator) // все цифры знак минус разделитель
and (POS (DecimalSeparator, Edit1.Text) > 0)) // запрет на второй сепаратор
or ((Key = '-') and (POS ('-', Edit1.Text) > 0)) // запрет на второй минус
or ((Key = '-') and (Edit1.SelStart <> 0)) // запрет на минус не вначале цифры
then // (можно и предыдущей строчки но так лучше для понимания)
begin
Key := #0; // запрещенный символ заменяем нулем
MessageBeep (MB_OK); // биб в спикир
end;
end;
Преобразовать первую букву каждого слова к верхнему регистру в TEdit.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Edit1Change(Sender: TObject);
var
OldChange: TNotifyEvent;
OldStart: Integer;
begin
with (Sender as TEdit) do
begin
OldChange := OnChange;
OnChange := nil;
OldStart := SelStart;
if ((SelStart > 0) and (Text[SelStart - 1] = ' ')) or (SelStart = 1) then
begin
SelStart := SelStart - 1;
SelLength := 1;
SelText := AnsiUpperCase(SelText);
end;
OnChange := OldChange;
SelStart := OldStart;
end;
end;
Ограничение ввода в текстовое поле.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
In this exemple, the only keys allowed are the
same allowed in e-mail adresses.
}
procedure TForm1.Edit1KeyPress(Sender: TObject;
var Key: Char);
const
AllowedChars: string = 'abcdefghijklmnopq' +
'rstuvwxyz01234567_.@';
var
i: Integer;
Ok: Boolean;
begin
i := 0;
Ok := False;
{ If you erase next line, user won't be able to type backspace }
if Key = #8 then Ok := True;
repeat
i := i + 1;
if Key = AllowedChars[i] then Ok := True;
until (Ok) or (i = Length(AllowedChars));
if not Ok then Key := #0;
end;
Проблема потери фокуса для TEdit.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
To Reproduce the Problem: Um das Problem nachzuvollziehen: 1. Drop two TEdits onto a form. Plaziere zwei TEdits auf einer Form. 2. In the OnExit Event of Edit1 add the following code: Im OnExit Ereignis von Edit1 schreibe diesen Code: Application.MessageBox( 'Title','...', mb_ok ); 3. Run the application. Starte die Anwendung. 4. First select Edit1 then Edit2 Klicke auf Edit und dann auf Edit2 5. The Message box is shown. Click the OK button, and the Caret has dissapeared! Eine MessageBox wird angezeigt. Klicke auf den OK Button und der Cursor ist verschwunden! 6. How to handle this: Wie man das umgehen kann:
procedure TForm1.Edit1Exit(Sender: TObject);
begin
Application.MessageBox('qq','qq',mb_ok);
if Assigned(ActiveControl) then
PostMessage(ActiveControl.Handle,WM_SETFOCUS,0,0);
end;
Исключить звуковой сигнал в поле ввода.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// Either disable the Beep in the OnKeyPress handler:
// Unterdrucke den Beep-Ton entweder im OnKeyPress Ereignis:
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if key = #13 then // #13 = Return
begin
key := #0;
// Code...
end;
end;
// Or in the OnKeyDown-Handler:
// Oder im OnKeyDown Ereignis:
procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
Mgs: TMsg;
begin
if Key = VK_RETURN then
PeekMessage(Mgs, 0, WM_CHAR, WM_CHAR, PM_REMOVE);
end;
Как заставить TEdit не пикать при нажатии недопустимых клавиш.
Перехватите событие KeyPress и установите key = #0 для недопустимых клавиш.
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if ((UpCase(Key) < 'A') or (UpCase(Key) > 'Z')) then
Key := #0;
end;
|
Элемент управления Edit, реагирующий на событие OnTimer.
Как-то раз встала такая проблема: если пользователь какое-то время ничего не вводит в элемент управления Edit, то предупредить его об этом.
unit EditOnTime;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, ExtCtrls, StdCtrls;
type
TEditOnTime = class(TEdit)
private
FInterval: integer;
FTimer: TTimer;
FOnTimer: TNotifyEvent;
procedure SetInterval(Interval: integer);
procedure Timer(Sender: TObject);
protected
procedure KeyPress(var Key: char); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Interval: integer read FInterval write SetInterval default 750;
property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
end;
procedure register;
implementation
//******************* RegisterComponent
// Здесь мы регистрируем компонент в IDE
procedure register;
begin
RegisterComponents('MPS', [TEditOnTime]);
end;
//******************* TEditOnTime.SetInterval
// устанавливаем интервал таймера
procedure TEditOnTime.SetInterval(Interval: integer);
begin
FInterval := Interval;
if Assigned(FTimer) then
FTimer.Interval := FInterval;
end;
//******************* TEditOnTime.Create
constructor TEditOnTime.Create(AOwner: TComponent);
begin
FInterval := 750;
inherited Create(AOwner);
if not (csDesigning in ComponentState) then
try
FTimer := TTimer.Create(self);
FTimer.Enabled := false;
FTimer.Interval := FInterval;
FTimer.OnTimer := Timer;
except
FreeAndNil(FTimer);
end;
end;
//******************* TEditOnTime.Destroy
destructor TEditOnTime.Destroy;
begin
if Assigned(FTimer) then
FreeAndNil(FTimer);
inherited Destroy;
end;
//******************* TEditOnTime.OnTimer
procedure TEditOnTime.Timer(Sender: TObject);
begin
FTimer.Enabled := false;
if Assigned(FOnTimer) then
FOnTimer(self);
end;
//******************* TEditOnTime.KeyPress
procedure TEditOnTime.KeyPress(var Key: char);
begin
FTimer.Enabled := false;
inherited KeyPress(Key);
FTimer.Enabled := (Text <> '') and Assigned(FTimer)
and Assigned(FOnTimer);
end;
end.
|
Замена текста в поле TEdit.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure ReplaceText(Edit: TCustomEdit; strOLD, strNEW: string);
var
x, Position: integer;
tmpstr, tmpstr2: string;
begin
tmpstr := Edit.Text;
for x := 0 to Length(Edit.Text) do
begin
if Copy(Edit.Text, x, Length(strold)) = strold then
begin
tmpstr := Copy(Edit.Text, 0, x - 1) + strnew;
Position := x;
end;
end;
tmpstr2 := Edit.Text;
if Position <> 0 then
Edit.Text := tmpstr + Copy(tmpstr2, Position + Length(strOLD), Length(tmpstr2))
else
Edit.Text := tmpstr;
end;
// Example/Beispiel:
procedure TForm1.Button1Click(Sender: TObject);
begin
ReplaceText(Edit1, 'OldWord', 'NewWord');
end;
// With the StringReplace function:
procedure ReplaceText(Edit: TCustomEdit; strOLD, strNEW: string);
begin
Edit.Text := StringReplace(Edit1.Text, strOLD, strNEW, [rfReplaceAll]);
end;
Правое выравнивание текста в компоненте Edit.
| Утро. Квартира сисопа. Ночная подружка подходит к компу, смотрит в логи и говорит: - Да, и cps у тебя тоже маленький... |
TEdit1 = class(TEdit)
public
procedure CreateParams(var Params: TCreateParams); Override;
end;
procedure TEdit1.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.Style := Params.Style or ES_MULTILINE or ES_RIGHT;
end;
|
Edit с возможностью автоматического выбора.

| Автор: Константин Хрипков |
| EDIT твою мать! FORMAT тебя за ногу! |
...я тоже пишу в Delphi и могу поделиться своим опытом. В дополнение к этому письму, чтобы не быть голословным, прилагаю маленький компонент THintEdit, порожденный от TCustomEdit, который представляет собой с виду обычный TEdit элемент с возможностью автоматического выбора стринговых значений из скрытого списка (так, как это реализовано в Netscape Nаvigаtоr'е). Описание особенно не нужно, так как выполнено все достаточно элементарно: значения для выбора заносятся в свойство HintList, тип свойства TStrings. При нажатии клавиш вверх/вниз выбираются значения, соответствующие набранным начальным символам.
unit HintEdit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
THintEdit = class(TCustomEdit)
private
{ Private declarations }
FHintList: TStrings;
Searching,
CanSearch: boolean;
CurSPos: integer;
protected
{ Protected declarations }
procedure Change; override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
property HintList: TStrings read FHintList write FHintList;
destructor Destroy; override;
published
{ Published declarations }
property Anchors;
property AutoSelect;
property AutoSize;
property BiDiMode;
property BorderStyle;
property CharCase;
property Color;
property Constraints;
property Ctl3D;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property Font;
property HideSelection;
property ImeMode;
property ImeName;
property MaxLength;
property OEMConvert;
property ParentBiDiMode;
property ParentColor;
property ParentCtl3D;
property ParentFont;
property ParentShowHint;
property PasswordChar;
property PopupMenu;
property ReadOnly;
property ShowHint;
property TabOrder;
property TabStop;
property Text;
property Visible;
property OnChange;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDock;
property OnStartDrag;
end;
procedure Register;
implementation
{$R *.DCR}
procedure Register;
begin
RegisterComponents('Netscape', [THintEdit]);
end;
constructor THintEdit.Create;
begin
inherited;
FHintList := TStringList.Create;
Searching := false;
CanSearch := true;
CurSPos := -1;
end;
procedure THintEdit.Change;
var
i, l: integer;
begin
if Searching then
Exit;
if not CanSearch then
Exit;
if Text = '' then
exit;
l := Length(Text);
for i := 0 to FHintList.Count - 1 do
if Copy(FHintList[i], 1, l) = Text then
begin
Searching := true;
CurSPos := i;
Text := FHintList[i];
Searching := false;
SelStart := Length(Text);
SelLength := -(Length(Text) - l);
break;
end;
inherited;
end;
procedure THintEdit.KeyDown;
var
l: integer;
begin
if Chr(Key) in ['A'..'z', 'А'..'Я', 'а'..'я'] then
CanSearch := true
else
CanSearch := false;
case Key of
VK_DOWN:
begin
if (CurSPos < HintList.Count - 1) and (SelLength > 0) then
if Copy(FHintList[CurSPos + 1], 1, SelStart) = Copy(Text, 1, SelStart)
then
begin
l := SelStart;
Inc(CurSPos);
Text := FHintList[CurSPos];
SelStart := Length(Text);
SelLength := -(Length(Text) - l);
end;
Key := VK_RETURN;
end;
VK_UP:
begin
if (CurSPos > 0) and (SelLength > 0) then
if Copy(FHintList[CurSPos - 1], 1, SelStart) = Copy(Text, 1, SelStart)
then
begin
l := SelStart;
Dec(CurSPos);
Text := FHintList[CurSPos];
SelStart := Length(Text);
SelLength := -(Length(Text) - l);
end;
Key := VK_RETURN;
end;
VK_RETURN:
begin
SelStart := 0;
SelLength := Length(Text);
end;
end;
inherited;
end;
destructor THintEdit.Destroy;
begin
FHintList.Free;
inherited;
end;
|
Пасхальные яйца в Delphi и CBuilder.
Автор: Матвеев ИгорьНедавно я прочитал где-то о проекте JEDI и о том, что если в Delphi версии 6.0 (В остальных не знаю) в окне "О Delphi..." (Помошь->О Delphi...) при нажатой клавише Alt нажать последовательно J E D I - симпатично проплывет объявление об этом проекте. Сегодня я установил себе C++Builder и совершенно случайно заметил, что там все в том же диалоговом окне "About" при нажатой клавише Alt нажать T - показывается "C++Builder Team". Вот я и заинтересовался подобными штучками в продуктах Borland.
Итак, после определенного ряда испытаний удалось выяснить:
| Delphi 6.0: (При нажатой клавише Alt) | |
| 1: J E D I | Информация о пректе JEDI. |
| 2: T E A M | Команда разработчиков Delphi. |
| 3: Q U A L I T Y | Информация о Delphi QA Staff. |
| 4: D E V E L O P E R S | Информация о Delphi R&D Staff. |
| C++Builder 4.253 (При нажатой клавише Alt) | |
| 1: T | Команда разработчиков C++Builder. |
| 2: D | Информация о C++Builder R&D Staff. |
| 3: V E R S I O N | Информация о версии. |
Если у кого-то есть подобная информация - пишите E-mail: teap_leap@mail.ru.
Эллипсовидное окно.
Для начала нужно обеспечить возможность пользователю перемещать окно, хватаясь за клиентскую область, а не за заголовочную, т.к. полосы заголовка, собственно, у нас нет.
Сначала в частных объявлениях (после слова private) объявляем процедуру:
private procedure WMNCHitTest(var M: TWMNCHitTest); message wm_NCHitTest; |
Затем в разделе implementation описываем её так:
implementation
{$R *.DFM}
procedure TForm1.WMNCHitTest(var M: TWMNCHitTest);
begin
inherited;
if M.Result = htClient then
M.Result := htCaption;
end;
|
Далее самое главное. По созданию окна (событие OnCreate) пишем следующий код:
procedure TForm1.FormCreate(Sender: TObject); var hsWindowRegion: Integer; begin hsWindowRegion := CreateEllipticRgn(50, 50, 350, 200); SetWindowRgn(Handle, hsWindowRegion, true); end; |
Ещё одна защита ваших shareware программ и как её снять.
Автор: Bad_guy WEB сайт: http://cracklab.narod.ru Многие люди стремятся заниматься программированием, но существует и такая часть компьютерных профессионалов, как крэкеры, причём крэкеры не стесняются обычно заниматья и программированием тоже. Существует слух, что крэкер серьёзной программы не напишет, с этим мнением мне лично сложно согласиться, однако если представить человека погружённого в крэк целиком, естественно можно предположить, что серьёзнее кеймекера он программы не напишет. Всё же большинство крэкеров не абсолютные энтузиасты и равно как и крэком любят заниматься и программироаванием, а может даже больше программированием, к коим я, собственно говоря, и отношусь. ... Время шло, мой опыт рос, и в один прекрасный момент мою голову поразила мысль о том, что надо бы интернет - то как-нибудь окупать, вот и решил я тогда взять ноги в руки, прокопать как следует свои дельфовые проекты и создать коммерческий продукт. Есть идея - есть дело и через пару недель мой первый коммерческий проект был закончен, не буду вам говорить что это за программа, скажу лишь что продавать её я решил только на западе, потому что у нас в стране хроническая привычка покупать софт только в переходах метро, может быть конечно это и неплохо, ну да ладно... Во время написания этой программы я не мог не удилить внимания её защите, ведь можно сказать это моя слабость, а можно и - специализация, но защиту эту поленившись я конечно придумал не 100% надёжности, да и понадеялся, что за её цену на Западе даже и руки не станет пачкать никакой крэкер (зря я конечно так-то сам ведь знаю, что здесь энтузиазм главное), кстати, почитав конференцию шароварщиков я понял, что выход крэка не подрывает целиком их бизнес, только малая доля потенциальных покупателей использует крэк, остальные - всё равно купят (для России я думаю дело всё-таки обстоит иначе), ну, пожалуй хватит лирики...Суть моего метода защиты в программе была такова: Сначала я написал всю программу. Назвав её Freeware, можно было бы её выкладывать, но как бы не так (теперь я вообще наверно не одной фриверки не напишу - деньжатами запахло), затем я стал предпринимать попытки навешать всяких нагов и ограничений и это у меня тоже получилось. Защита была организована так: решено было использовать ключ, регистрационное имя в вычисление ключа не входило. В тех местах, где было ограничение (например, вместо окна сохранения, нажав кнопку "сохранить" вы получаете сообщение "Отключено в незарегверсии") я вставил вызов процедуры-пустышки, программа шла в ту пустышку, пустышка усиленно что-то шифровала, складывала, делила и _ВСЕГДА_ возвращала значение переменной, допустим число "17", этот значение сравнивалось с оригиналом и если не совпадало, происходил выход из программы, а если совпадало, то в переменную где было "17" записывался "0" и программа спокойно продолжала работать. В чём сила, брат, спросите вы ? А вот в чём вся сила: адрес этой вызываемой процедуры был не указан внутри кода, то есть было сделано не так:
Call 40152A |
А вот так:
Mov eax, regprocaddr Call eax |
Причём если regprocaddr не был известен (был равен 0 в незарегистрированной версии), то конечно программа показывала сообщение "Отключено в незарегверсии" и не шла в тот код, regprocaddr же каждый пользователь получал следующим образом: regcode, который присылался пользователю состоял из двух частей long intеgегов, в строковом формате, эти две части хогились и это и есть regprocaddr. Теперь было так: если regprocaddr > 0 тогда программа попадала на этот код:
Mov eax, regprocaddr Call eax |
Удачи вам в вашей деятельности, чем бы вы ни занимались, Bad_guy.
Извлечь Email адреса из DBX файлов Outlook.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
unit ExtractEmailsFunc;
interface
uses
Windows, SysUtils;
procedure CheckEMail(FilePath: string);
implementation
var
BufferSize: Integer;
function VerifyFile(strFileName: string): Integer;
var
intErro: Integer;
tsrFile: TSearchRec;
begin
intErro := FindFirst(strFileName, FaAnyFile, tsrFile);
if intErro = 0 then Result := tsrFile.Size
else
Result := -1;
FindClose(tsrFile);
end;
procedure CheckEMail(FilePath: string);
var
I: Integer;
hFile: Integer;
Buffer: PChar;
StrEmail: string;
begin
hFile := FileOpen(FilePath, fmOpenRead);
try
if hFile = 0 then Exit;
GetMem(Buffer, bufferSize + 1);
ZeroMemory(Buffer, BufferSize + 1);
try
FileRead(hFile, Buffer^, BufferSize);
I := 0;
while I <= BufferSize - 1 do
begin
StrEmail := '';
if Buffer[I] = '<' then
begin
Inc(I);
while (Buffer[I] <> '@') and (I <= BufferSize) do
begin
if (Buffer[I] = CHR(45)) or (Buffer[I] = CHR(46)) or
(Buffer[I] = CHR(90)) or ((Buffer[I] > CHR(49)) and (Buffer[I] <= CHR(57)))
or ((Buffer[I] >= CHR(65)) and (Buffer[I] <= CHR(90))) or
((Buffer[I] >= CHR(97)) and (Buffer[I] <= CHR(122))) then
begin
StrEmail := StrEmail + Buffer[I];
end
else
begin
StrEmail := '';
Break;
end;
Inc(I);
end;
if StrEmail <> '' then
begin
StrEmail := StrEmail + '@';
Inc(I);
while (Buffer[I] <> '.') and (I <= BufferSize) do
begin
if (Buffer[I] = CHR(45)) or (Buffer[I] = CHR(46)) or
(Buffer[I] = CHR(90)) or ((Buffer[I] >= CHR(49)) and (Buffer[I] <= CHR(57)))
or ((Buffer[I] >= CHR(65)) and (Buffer[I] <= CHR(90))) or
((Buffer[I] >= CHR(97)) and (Buffer[I] <= CHR(122))) then
begin
StrEmail := StrEmail + Buffer[I];
end
else
begin
StrEmail := '';
Break;
end;
Inc(I);
end;
if StrEmail <> '' then
begin
StrEmail := StrEmail + '.';
Inc(i);
while (Buffer[I] <> '>') and (I <= BufferSize) do
begin
if (Buffer[I] = CHR(45)) or (Buffer[I] = CHR(46)) or
(Buffer[I] = CHR(90)) or ((Buffer[I] >= CHR(49)) and (Buffer[I] <= CHR(57)))
or ((Buffer[I] >= CHR(65)) and (Buffer[I] <= CHR(90))) or
((Buffer[I] >= CHR(97)) and (Buffer[I] <= CHR(122))) then
begin
StrEmail := StrEmail + Buffer[I];
end
else
begin
StrEmail := '';
Break;
end;
Inc(I);
end;
if StrEmail <> '' then
begin
WriteLn(StrEmail);
Inc(I);
end;
end;
end;
end
else
Inc(I);
end;
finally
FreeMem(Buffer);
end;
finally
FileClose(hFile);
end;
end;
begin
BufferSize := VerifyFile(ParamStr(1));
if BufferSize <= 0 then Exit;
CheckEMail(ParamStr(1));
end.
Отправить Email скрытно от пользователя.
Автор: Павел Ну если не устраивает дельфовый SMTP клиент...Вот вам кусок,написанный на WinSock API пробуйте разобраться... это будет посложнее чем компоненты от NetMasters юзать... (Все это исполнялось в отдельном трейде,не забудьте про WSAStartUp/ClenUp) (каюсь, это кусок из моего трояна :).
function SMTPRecvReply(MailSocket: TSocket): Word;
var
Buffer: array[1..1024] of Byte;
Err: Integer;
BufStr: string;
begin
BufStr := '';
Recv(MailSocket, Buffer, SizeOf(Buffer), 0);
for Err := 1 to 3 do
BufStr := BufStr + Chr(buffer[Err]);
try
Result := StrToInt(BufStr);
except
Result := 0;
end;
end;
function SMTPSendString(MailSocket: TSocket; Str: string): Boolean;
var
Buffer: array[1..1024] of Byte;
Err: Integer;
begin
Result := FALSE;
ZeroMemory(@Buffer, SizeOf(Buffer));
if Length(Str) > 1000 then
Exit;
for Err := 1 to Length(Str) do
Buffer[Err] := Ord(Str[Err]);
Buffer[Err] := 13;
Buffer[Err + 1] := 10;
Err := WinSock.send(MailSocket, buffer, Err + 1, 0);
if Err > 0 then
Result := TRUE;
end;
function SMTPConnectAndSendMail(Arg: Pointer): DWord; stdcall;
var
MailSocket: TSocket;
MailSent: Boolean;
MTime: TSystemTime;
SMTPServer: TSockAddrIn;
Step: Byte;
OutOfSend: Boolean;
begin
MailSent := FALSE;
OutOfSend := FALSE;
MailSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ZeroMemory(@SMTPServer, SizeOf(SMTPServer));
SMTPServer.sin_family := AF_INET;
SMTPServer.sin_port := htons(25);
SMTPServer.sin_addr.S_addr := inet_addr(PChar(smtp));
Step := 0;
if Connect(MailSocket, SMTPServer, SizeOf(SMTPServer)) = 0 then
repeat
case SMTPRecvReply(MailSocket) of
220: SMTPSendString(MailSocket, 'HELO server.com');
250: case STEP of
0:
begin
SMTPSendString(MailSocket, 'MAIL FROM:' + mail);
Inc(Step);
end;
1:
begin
SMTPSendString(MailSocket, 'RCPT TO:' + mail);
Inc(Step);
end;
2:
begin
SMTPSendString(MailSocket, 'DATA');
Inc(Step);
end;
3:
begin
SMTPSendString(MailSocket, 'QUIT');
Inc(Step);
end;
end;
354:
begin
SMTPSendString(MailSocket, 'From:' + ServerID);
SMTPSendString(MailSocket, 'To:' + UserName);
SMTPSendString(MailSocket, 'Subject:' + LocalIPS + ' : ' + port +
' At ' + DateTimeToStr(Now));
SMTPSendString(MailSocket, CRLF);
for count := 1 to (txt.Count - 1) do
SMTPSendString(MailSocket, txt[count]);
SMTPSendString(MailSocket, CRLF + '.' + CRLF);
end;
221:
begin
OutOfSend := True;
MailSent := True
end;
else
OutOfSend := True;
file: //!Case
end;
until OutOfSend;
CloseSocket(MailSocket);
end;
ExitThread(0);
end;
Как отправить письмо скрытно от пользователя по протоколу SMTP.
Автор: BadBoy.
{
smtp - ip адрес smtp сервера
port - порт smtp сервера, по умолчанию 25
from - адрес отправителя
dest - адрес получателя
subject - тема письма
body - текст писма
Возвращает True если письмо было успешно отправленно...
}
function mail(smtp: string; port: integer; from, dest, subject,
body: string): bool;
const
cl = #13#10;
var
WSAData: TWSAData;
Host: TSockAddrIn;
Sock: TSocket;
res: Integer;
buff: array[1..255] of Char;
{ отправляем данные через сокет }
procedure senddata(str: string);
var
i: integer;
begin
for i := 1 to Length(str) do
if send(Sock, str[i], 1, 0) = SOCKET_ERROR then
exit;
end;
{ получаем ответ от команды }
function recvdata(accept: string): bool;
var
buff: array[1..255] of Char;
begin
res := recv(Sock, buff, SizeOf(buff), 0);
Result := (Res = SOCKET_ERROR) or (Copy(buff, 1, 3) = accept);
end;
begin
try
result := false;
{ инициализация сокета }
WSAStartUp(257, WSAData);
Sock := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if Sock = INVALID_SOCKET then
Exit;
{ устанавливаем хост и порт сервера }
res := inet_addr(PChar(smtp));
if res <= 0 then
exit;
Host.sin_family := AF_INET;
Host.sin_port := htons(port);
Host.sin_addr.S_addr := res;
{ подключаемся к серверу }
if connect(Sock, Host, SizeOf(Host)) > 0 then
Exit;
{ приветствие сервера }
if not recvdata('220') then
Exit;
{ EHLO }
senddata('EHLO' + cl);
if not recvdata('250') then
Exit;
{ MAIL FROM: }
senddata('MAIL FROM:' + from + cl);
if not recvdata('250') then
Exit;
{ RCPT TO: }
senddata('RCPT TO:' + dest + cl);
if not recvdata('250') then
Exit;
{ DATA }
senddata('DATA' + cl);
if not recvdata('354') then
Exit;
{ отправляем текст сообщения }
senddata('Subject:' + subject + cl + cl + body + cl + '.');
if not recvdata('250') then
Exit;
{ отключаемся от сервера }
senddata('QUIT' + cl);
result := true;
finally
{ убиваем сокет }
closesocket(sock);
WSACleanup;
end;
end;
{
mail('127.0.0.1',25,'bboy-ne@yandex.ru' ,'admin@company.mail', 'subj', 'body text');
p.s.: Тестировалась на e-mail сервере Alt-N Daemond Pro for Windows v6.5.1...
Функция делалась на основе статьи "Простой протокол передачи почты (SMTP)"
}
Как отправить письмо и указать тему, текст сообщения.
| Мужик работает на участке около своего дома. Из соседнего дома выходит его соседка-блондинка, открывает свой почтовый ящик, заглядывает туда, захлопывает и убегает обратно в дом. Немного погодя, соседка выбегает снова, заглядывает в почтовый ящик и снова убегает обратно. Когда все повторяется в третий раз, мужик не выдерживает: - В чем дело? Что-нибудь случилось? - Это точно, что что-то случилось. Мой дурацкий компьютер все время сообщает: "Уои'vе got mail" |
ShellExecute(Handle, 'open', 'mailto:lalala@lala.ru?par1=value1&par1=value1&...', nil, nil, SW_SHOWNORMAL); |
Третий параметр функции ShellExecute() - это строка в соответствии с протоколом mailto и правилами оформления URL:
mailto: тип протокола (может быть http: в этом случае оставшая URL и параметры запроса). lalala@lala.ru адрес получателя, можно включать несколько адресов, разделяя из символом ";" ? разделитель параметров от адреса par1=value1 имя параметра и его значение & разделитель параметровПротокол Mailto имеет следующую форму.
MAILTO:Recipients&Parameters |
PARAMETER DESCRIPTION |
Все данные указываемые в параметрах должны быть так называемые Internet safe characters. Используййте %0d для символа перевод строки (LF), %20 для пробела и так далее.
Пример:
mailto:email1;email2&cc=email3?subject=Это%20тема& body=это%20текст%20письма%0dЭто%20другая%20строка |
Вложенные свойства.
| Автор: Mike Scott (Mobius Ltd) |
| - Почему программисты весят больше чем другие люди? - Потому, что в каждом килограмме у них 1024 грамма! |
Для регистрации контейнерного класса в процедуре Register вы должны вызвать RegisterPropertyEditor. Приблизительно так:
RegisterPropertyEditor( TypeInfo( TYourContainedClass ), TYourContainer, 'YourContainedPropery', TClassProperty ) ; |
Посмотрите как это делаю я. Сохраните приведенный модуль с именем CONTAIN.PAS и добавите его к вашей палитре.
unit Contain;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, ExtCtrls;
type
TContainer = class(TComponent)
private
FImage: TImage;
public
constructor Create(AOwner: TComponent); override;
published
property Image: TImage read FImage write FImage;
end;
procedure Register;
implementation
uses DsgnIntf;
constructor TContainer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FImage := TImage.Create(Self);
end;
procedure Register;
begin
RegisterComponents('Samples', [TContainer]);
RegisterPropertyEditor(TypeInfo(TImage),
TContainer,
'Image',
TClassProperty);
end;
end.
|
Использовать фильтр объема для изображения.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure Emboss(ABitmap : TBitmap; AMount : Integer);
var
x, y, i : integer;
p1, p2: PByteArray;
begin
for i := 0 to AMount do
begin
for y := 0 to ABitmap.Height-2 do
begin
p1 := ABitmap.ScanLine[y];
p2 := ABitmap.ScanLine[y+1];
for x := 0 to ABitmap.Width do
begin
p1[x*3] := (p1[x*3]+(p2[(x+3)*3] xor $FF)) shr 1;
p1[x*3+1] := (p1[x*3+1]+(p2[(x+3)*3+1] xor $FF)) shr 1;
p1[x*3+2] := (p1[x*3+1]+(p2[(x+3)*3+1] xor $FF)) shr 1;
end;
end;
end;
end;
Как создать неактивное изображение из обычного (рельеф).
procedure TForm1.aaa(bmpFrom, bmpTo: Tbitmap);
var
TmpImage, Monobmp: TBitmap;
IRect: TRect;
begin
MonoBmp := TBitmap.Create;
TmpImage:=Tbitmap.Create;
TmpImage.Width := bmpFrom.Width;
TmpImage.Height := bmpFrom.Height;
IRect := Rect(0, 0, bmpFrom.Width, bmpFrom.Height);
TmpImage.Canvas.Brush.Color := clBtnFace;
try
with MonoBmp do
begin
Assign(bmpFrom);
Canvas.Brush.Color := clBlack;
if Monochrome then
begin
Canvas.Font.Color := clWhite;
Monochrome := False;
Canvas.Brush.Color := clWhite;
end;
Monochrome := True;
end;
with TmpImage.Canvas do
begin
Brush.Color := clBtnFace;
FillRect(IRect);
Brush.Color := clBlack;
Font.Color := clWhite;
CopyMode := MergePaint;
Draw(IRect.Left + 1, IRect.Top + 1, MonoBmp);
CopyMode := SrcAnd;
Draw(IRect.Left, IRect.Top, MonoBmp);
Brush.Color := clBtnShadow;
Font.Color := clBlack;
CopyMode := SrcPaint;
Draw(IRect.Left, IRect.Top, MonoBmp);
CopyMode := SrcCopy;
bmpTo.assign(TmpImage);
TmpImage.free;
end;
finally
MonoBmp.Free;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
aaa(image1.picture.bitmap, image2.picture.bitmap);
Image2.invalidate;
end;
|
Удаление содержимого каталога.
Автор: demon-777 WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Удаление содержимого каталога
Зависимости: Windows, SysUtils, Classes, Masks
Автор: demon-777, demon-777@yandex.ru, Питер
Copyright: Dimka Maslov
Дата: 26 мая 2002 г.
***************************************************** }
procedure DeleteFiles(Path: string);
var
Mask: string;
SearchRec: TSearchRec;
FindResult: Integer;
List: TStringList;
i: Integer;
begin
Mask := ExtractFileName(Path);
if Mask = '' then
Mask := '*.*';
Path := ExtractFilePath(Path);
if Path = '' then
Path := IncludeTrailingBackslash(GetCurrentDir);
List := TStringList.Create;
try
FindResult := FindFirst(Path + '*.*', faAnyFile, SearchRec);
try
while FindResult = 0 do
with SearchRec do
begin
if ((Attr and faDirectory) = 0) and MatchesMask(Name, Mask) then
List.Add(Name);
FindResult := FindNext(SearchRec);
end;
for i := 0 to List.Count - 1 do
DeleteFile(Path + List[i]);
finally
FindClose(SearchRec);
end;
finally
List.Free;
end;
end;
Очистить очередь сообщений клавиатуры и мышки.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure EmptyKeyQueue;
var
Msg: TMsg;
begin
while PeekMessage(Msg, 0, WM_KEYFIRST, WM_KEYLAST,
PM_REMOVE or PM_NOYIELD) do;
end;
procedure EmptyMouseQueue;
var
Msg: TMsg;
begin
while PeekMessage(Msg, 0, WM_MOUSEFIRST, WM_MOUSELAST,
PM_REMOVE or PM_NOYIELD) do;
end;
Очистить корзину.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure EmptyRecycleBin;
const
SHERB_NOCONFIRMATION = $00000001;
SHERB_NOPROGRESSUI = $00000002;
SHERB_NOSOUND = $00000004;
type
TSHEmptyRecycleBin = function(Wnd: HWND;
pszRootPath: PChar;
dwFlags: DWORD): HRESULT; stdcall;
var
SHEmptyRecycleBin: TSHEmptyRecycleBin;
LibHandle: THandle;
begin { EmptyRecycleBin }
LibHandle := LoadLibrary(PChar('Shell32.dll'));
if LibHandle <> 0 then @SHEmptyRecycleBin :=
GetProcAddress(LibHandle, 'SHEmptyRecycleBinA')
else
begin
MessageDlg('Failed to load Shell32.dll.', mtError, [mbOK], 0);
Exit;
end;
if @SHEmptyRecycleBin <> nil then
SHEmptyRecycleBin(Application.Handle,
nil,
SHERB_NOCONFIRMATION or SHERB_NOPROGRESSUI or SHERB_NOSOUND);
FreeLibrary(LibHandle); @SHEmptyRecycleBin := nil;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
EmptyRecycleBin;
end;
{
Minimum operating systems: Windows XP/2000, Windows NT 4.0 with Internet Explorer 4.0,
Windows 98, Windows 95 with Internet Explorer 4.0
(shell32.dll version 4.71 or later)
}
Очистка таблицы (с проверкой).
TTable имеет метод EmptyTable.Пример:
try
with Table1 do
begin
Close;
Exclusive := true;
Open;
EmptyTable;
Close;
end;
except
on E: EDBEngineError do
ShowMessage(E.Message);
end;
|
Очистить TImage.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// clear a Image's picture Image1.Picture := nil; // set the standard windows icon for the application Application.Icon := nil;
Как позволить или запретить выдачу звукового сигнала в динамик (Beep).
С помощью функции Beep можно выводить звуковой сигнал. напишите это слово по событию OnTimer для компонента TTimer и вы услышите звуковые сигналы. Теперь вы сможете запрещать или разрешать подобные Веер'ы.
uses shellapi; //выключить systemparametersinfo(spi_setbeep, 0, nil, 0); //включить systemparametersinfo(spi_setbeep, 1, nil, 0); |
Эмуляция нажатия клавиши в активном окне.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Эмуляция нажатия клавиши в активном окне
VKey - код виртуальной клавиши (см. описание констант VK_xxxx)
Зависимости: Windows
Автор: Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright: Dimka Maslov
Дата: 29 апреля 2002 г.
***************************************************** }
procedure PressKey(VKey: Byte);
begin
keybd_event(VKey, 0, 0, 0);
keybd_event(VKey, 0, KEYEVENTF_KEYUP, 0);
end;
64-битное кодирование 4.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
const
Codes64 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/';
function Encode64(S: string): string;
var
i: Integer;
a: Integer;
x: Integer;
b: Integer;
begin
Result := '';
a := 0;
b := 0;
for i := 1 to Length(s) do
begin
x := Ord(s[i]);
b := b * 256 + x;
a := a + 8;
while a >= 6 do
begin
a := a - 6;
x := b div (1 shl a);
b := b mod (1 shl a);
Result := Result + Codes64[x + 1];
end;
end;
if a > 0 then
begin
x := b shl (6 - a);
Result := Result + Codes64[x + 1];
end;
end;
function Decode64(S: string): string;
var
i: Integer;
a: Integer;
x: Integer;
b: Integer;
begin
Result := '';
a := 0;
b := 0;
for i := 1 to Length(s) do
begin
x := Pos(s[i], codes64) - 1;
if x >= 0 then
begin
b := b * 64 + x;
a := a + 6;
if a >= 8 then
begin
a := a - 8;
x := b shr a;
b := b mod (1 shl a);
x := x mod 256;
Result := Result + chr(x);
end;
end
else
Exit;
end;
end;
Кодирование и раскодирование паролей.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// This example shows how you can encrypt strings
// using special security string.
// You can decode data only if you know security string.
// I suppose, there is no chance to hack security string, using any analyse algorythms.
// Every time you call this function, you will
// have a new result even if all params are constant
// NOTE: Don`t forget to call "Randomize" proc before using this functions.
const
Codes64 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/';
// you must use this function to generate special
// security string, which is used in main encode/decode routines.
// NOTE: you must generate the security string only once and then use it in encode/decode functions.
function GeneratePWDSecutityString: string;
var
i, x: integer;
s1, s2: string;
begin
s1 := Codes64;
s2 := '';
for i := 0 to 15 do
begin
x := Random(Length(s1));
x := Length(s1) - x;
s2 := s2 + s1[x];
s1 := Copy(s1, 1,x - 1) + Copy(s1, x + 1,Length(s1));
end;
Result := s2;
end;
// this function generate random string using
// any characters from "CHARS" string and length
// of "COUNT" - it will be used in encode routine
// to add "noise" into your encoded data.
function MakeRNDString(Chars: string; Count: Integer): string;
var
i, x: integer;
begin
Result := '';
for i := 0 to Count - 1 do
begin
x := Length(chars) - Random(Length(chars));
Result := Result + chars[x];
chars := Copy(chars, 1,x - 1) + Copy(chars, x + 1,Length(chars));
end;
end;
// This will encode your data.
// "SecurityString" must be generated using method
// described above, and then stored anywhere to
// use it in Decode function.
// "Data" is your string (you can use any characters here)
// "MinV" - minimum quantity of "noise" chars before each encoded data char.
// "MaxV" - maximum quantity of "noise" chars before each encoded data char.
function EncodePWDEx(Data, SecurityString: string; MinV: Integer = 0;
MaxV: Integer = 5): string;
var
i, x: integer;
s1, s2, ss: string;
begin
if minV > MaxV then
begin
i := minv;
minv := maxv;
maxv := i;
end;
if MinV < 0 then MinV := 0;
if MaxV > 100 then MaxV := 100;
Result := '';
if Length(SecurityString) < 16 then Exit;
for i := 1 to Length(SecurityString) do
begin
s1 := Copy(SecurityString, i + 1,Length(securitystring));
if Pos(SecurityString[i], s1) > 0 then Exit;
if Pos(SecurityString[i], Codes64) <= 0 then Exit;
end;
s1 := Codes64;
s2 := '';
for i := 1 to Length(SecurityString) do
begin
x := Pos(SecurityString[i], s1);
if x > 0 then s1 := Copy(s1, 1,x - 1) + Copy(s1, x + 1,Length(s1));
end;
ss := securitystring;
for i := 1 to Length(Data) do
begin
s2 := s2 + ss[Ord(Data[i]) mod 16 + 1];
ss := Copy(ss, Length(ss), 1) + Copy(ss, 1,Length(ss) - 1);
s2 := s2 + ss[Ord(Data[i]) div 16 + 1];
ss := Copy(ss, Length(ss), 1) + Copy(ss, 1,Length(ss) - 1);
end;
Result := MakeRNDString(s1, Random(MaxV - MinV) + minV + 1);
for i := 1 to Length(s2) do Result := Result + s2[i] + MakeRNDString(s1,
Random(MaxV - MinV) + minV);
end;
// This will decode your data, encoded with the function above, using specified "SecurityString".
function DecodePWDEx(Data, SecurityString: string): string;
var
i, x, x2: integer;
s1, s2, ss: string;
begin
Result := #1;
if Length(SecurityString) < 16 then Exit;
for i := 1 to Length(SecurityString) do
begin
s1 := Copy(SecurityString, i + 1,Length(securitystring));
if Pos(SecurityString[i], s1) > 0 then Exit;
if Pos(SecurityString[i], Codes64) <= 0 then Exit;
end;
s1 := Codes64;
s2 := '';
ss := securitystring;
for i := 1 to Length(Data) do if Pos(Data[i], ss) > 0 then s2 := s2 + Data[i];
Data := s2;
s2 := '';
if Length(Data) mod 2 <> 0 then Exit;
for i := 0 to Length(Data) div 2 - 1 do
begin
x := Pos(Data[i * 2 + 1], ss) - 1;
if x < 0 then Exit;
ss := Copy(ss, Length(ss), 1) + Copy(ss, 1,Length(ss) - 1);
x2 := Pos(Data[i * 2 + 2], ss) - 1;
if x2 < 0 then Exit;
x := x + x2 * 16;
s2 := s2 + chr(x);
ss := Copy(ss, Length(ss), 1) + Copy(ss, 1,Length(ss) - 1);
end;
Result := s2;
end;
Зашифровать и расшифровать строку.
unit uEncrypt;
interface
function Decrypt(const S: AnsiString; Key: Word): AnsiString;
function Encrypt(const S: AnsiString; Key: Word): AnsiString;
implementation
const
C1 = 52845;
C2 = 22719;
function Decode(const S: AnsiString): AnsiString;
const
Map: array[Char] of Byte = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2,
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0);
var
I: LongInt;
begin
case Length(S) of
2:
begin
I := Map[S[1]] + (Map[S[2]] shl 6);
SetLength(Result, 1);
Move(I, Result[1], Length(Result))
end;
3:
begin
I := Map[S[1]] + (Map[S[2]] shl 6) + (Map[S[3]] shl 12);
SetLength(Result, 2);
Move(I, Result[1], Length(Result))
end;
4:
begin
I := Map[S[1]] + (Map[S[2]] shl 6) + (Map[S[3]] shl 12) +
(Map[S[4]] shl 18);
SetLength(Result, 3);
Move(I, Result[1], Length(Result))
end
end
end;
function PreProcess(const S: AnsiString): AnsiString;
var
SS: AnsiString;
begin
SS := S;
Result := '';
while SS <> '' do
begin
Result := Result + Decode(Copy(SS, 1, 4));
Delete(SS, 1, 4)
end
end;
function InternalDecrypt(const S: AnsiString; Key: Word): AnsiString;
var
I: Word;
Seed: Word;
begin
Result := S;
Seed := Key;
for I := 1 to Length(Result) do
begin
Result[I] := Char(Byte(Result[I]) xor (Seed shr 8));
Seed := (Byte(S[I]) + Seed) * Word(C1) + Word(C2)
end
end;
function Decrypt(const S: AnsiString; Key: Word): AnsiString;
begin
Result := InternalDecrypt(PreProcess(S), Key)
end;
function Encode(const S: AnsiString): AnsiString;
const
Map: array[0..63] of Char = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
'abcdefghijklmnopqrstuvwxyz0123456789+/';
var
I: LongInt;
begin
I := 0;
Move(S[1], I, Length(S));
case Length(S) of
1:
Result := Map[I mod 64] + Map[(I shr 6) mod 64];
2:
Result := Map[I mod 64] + Map[(I shr 6) mod 64] +
Map[(I shr 12) mod 64];
3:
Result := Map[I mod 64] + Map[(I shr 6) mod 64] +
Map[(I shr 12) mod 64] + Map[(I shr 18) mod 64]
end
end;
function PostProcess(const S: AnsiString): AnsiString;
var
SS: AnsiString;
begin
SS := S;
Result := '';
while SS <> '' do
begin
Result := Result + Encode(Copy(SS, 1, 3));
Delete(SS, 1, 3)
end
end;
function InternalEncrypt(const S: AnsiString; Key: Word): AnsiString;
var
I: Word;
Seed: Word;
begin
Result := S;
Seed := Key;
for I := 1 to Length(Result) do
begin
Result[I] := Char(Byte(Result[I]) xor (Seed shr 8));
Seed := (Byte(Result[I]) + Seed) * Word(C1) + Word(C2)
end
end;
function Encrypt(const S: AnsiString; Key: Word): AnsiString;
begin
Result := PostProcess(InternalEncrypt(S, Key))
end;
end.
{**************************************************************}
// Example:
{**************************************************************}
procedure TForm1.Button1Click(Sender: TObject);
const
my_key = 33189;
var
sEncrypted, sDecrypted :AnsiString;
begin
// Encrypt a string
sEncrypted := Encrypt('this is a sample text
to encrypt...abcd 123 {}[]?=)=(',my_key);
// Show encrypted string
ShowMessage(sEncrypted);
// Decrypt the string
sDecrypted := Decrypt(sEncrypted,my_key);
// Show decrypted string
ShowMessage(sDecrypted);
end;
|
Шифрование исполняемого файла.
Автор: DDA WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Шифрование исполняемого файла
В данном примере реализована процедура шифрования и расшифрования
одной только процедуры "вывода сообщения".Процедура изночально
не зашифровано ,что ни есть хорошо.Для того что бы процедура была
при запуске программы уже зашифрована надо написать внешнюю программку,
которая ба сканировала ехе-шник и находила определенные метки
(начальную и конечную) и шифровала бы всё между ними.Таким образом
вы получите готовый ехе-шник с уже зашифрованой(зашифроваными)процедурой.
Для того чтоб некоторым умным людям (crackers)жить было не легко,
после расшифровки и выполнения процедуры можно её опять зашифровать.
Более потробную информацию читайте в статье на «Королевства Delphi»
http://delphi.vitpc.com/mastering/safe/safe.htm
Там же читайте Открытый проект "Анти крэковые мучения"
http://delphi.vitpc.com/mastering/safe/index.htm
Зависимости: Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms,Dialogs, StdCtrls;
Автор: DDA, Vologda
Copyright: «Королевства Delphi» Защита от несанкционированного
использования программ, написанных на Delphi
Дата: 3 марта 2004 г.
***************************************************** }
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure metka1;
//Начальная метка - нужна что бы находить потом в exe файле эти символы и знать
//откуда начинать шифровать
//Здесь метка представляет из себя десятичные числа от 0 до 256 через запятые
//Если метка 50,60,70,80,90 то это соответствует символам 2<FPZ
//Кстати, такого кол-во символов в метке может быть мало и лучше использовать больше
//Т.к если программа большая то такие последовательности могут встретиться не один раз
begin
asm
DB 50,60,70,80,90 //2<FPZ это метка начальная
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
beep;
ShowMessage('Период использования программы истёк!');
end;
procedure metka2;
//Конечная метка - нужна чтобы знать до куда нужно шифровать в файле и
//докуда расшифровывать в памяти
begin
asm
DB 68,68,67,45,61 //DDA-= это метка конечная
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
//Процедура Расшифрования(В даном примере и зашифрования)
var
ptrAddr: Pointer; {для Адреса процедуры вывода сообщения}
dwOldProtect: DWORD;
begin
ptrAddr := @TForm1.Button1Click; //Получаем адрес процедуры вывода сообщения
VirtualProtect(@TForm1.Button1Click, 2048, PAGE_READWRITE, @dwOldProtect);
//2048 это размер в байтах с которыми можно работать(по моему)
//Если процедура большая то нужно подбирать соответствующий размер
while ptrAddr <> @metka2 do
//шифрование(слабое) выполняем пока не дойдем то конечной метки
begin
Byte(ptrAddr^) := Byte(ptrAddr^) xor $41;
//каждый байт в памяти ксорится с кодом 65'A'(к примеру)
//Используйте более надёжные алгоритмы шифрования
inc(Integer(ptrAddr));
end;
exit; //нужно так сделать что б компилятор включил эти процедуры
metka1; //в код,т.к Delphi не вставляет в результирующий код процедуры,
metka2; //которые никогда не используются
end;
//Такое шифрование взято для примера и не рекомендуется для использования серьёзной защиты
//Также рекомендуется использовать и проверочную контрольную сумму для проверки
//целостности кода программы.
//Теперь осталось только сделать отдельную программу которая искала бы
//в этом exe файле начальную и конечную метки и шифровало бы всё между ними
//Но для демонстрации можно посмотреть результат и сейчас
//Запустите программу и нажмите на кнопку 1 -Должно вывестись сообщение и звук.сигнал
//А если нажать на кнопку 2 - то прцедура нажатия на кнопку 1 зашифруется
//И если после этого нажать на кнопку 1 -то должно произойти что-то не предсказуемое
//т.к процедура зашифрованна и что-то нормального вы не увидите.
//Источник информации: CopyRight «Королевства Delphi»
//"Защита от несанкционированного использования программ, написанных на Delphi"
end.
Генерация 96-битного ключа для шифрования с помощью Encrypt.
Автор: Kronprince WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Генерация 96-битного ключа для шифрования с помощью Encrypt()
Генерация 96-битного ключа для шифрования Encrypt()
Зависимости: System
Автор: kronprince, kronprince@mail.ru, ICQ:119018798, Полтава
Copyright: © kronprince - собственная разработка
Дата: 23 июня 2003 г.
***************************************************** }
// Генерация 96-битного ключа для шифрования Encrypt()
procedure GenerateKey96(var StartKey, MultKey, AddKey: integer);
var
i: integer;
a: array[1..12] of byte;
procedure FillKey(var VarKey: integer; index: integer);
var
j: integer;
begin
for j := 0 to sizeof(VarKey) - 1 do
VarKey := VarKey or a[index + j] shl (8 * j);
end;
begin
System.Randomize();
// Это непосредственно типа случайная генерация :)
for i := Low(a) to High(a) do
a[i] := System.Random(High(byte));
// А тут распихивание по ключевым полям с соответствующей стартовой позиции
FillKey(StartKey, 1);
FillKey(MultKey, 5);
FillKey(AddKey, 9);
end;
Пример использования:
GenerateKey96(StartKey, MultKey, AddKey); Encrypt(S, StartKey, MultKey, AddKey);
Шифрование текстовых полей таблицы Interbase.
Автор: Vemer WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Шифрование текстовых полей таблицы Interbase
Простенько но шустро шифрует текстовую строку VarChar в текстовую
строку VarChar, пригодную для сохранения в текстовом поле базы Interbase.
НЕ ЗАТРАГИВАЕТ ПРОБЕЛЫ!
Метод генерации (в начале) и изменения ключа (в 2-местах!) в вашей программе
лучше изменить от исходного в сторону усложнения, но не трогать "And 127" в конце.
Для работы с Char возможно стоит добавить "Trim(S1);" в самое начало.
Зависимости: Windows, SysUtils, DB
Автор: Vemer, Vemer@List.Ru, Петрозаводск
Copyright: Vemer
Дата: 16 ноября 2003 г.
***************************************************** }
function Morf(S1: string; T: Boolean): string;
var
LS: Byte; //длина строки
KB: Byte; //ключ, байт
I: Byte; //Счетчик
B: Byte; //байт строки
W: Smallint; //буфер для ключа
S2: string; //результат
begin
LS := Length(S1);
S2 := '';
KB := LS * LS; //-------Изменить/Усложнить!And 127 не трогать!
if T = False then // Выбор между шифрованием и дешифрованием
begin
for I := 1 to LS do
begin //шифрование
B := Byte(S1[I]);
if B > 32 then
begin
W := B + KB;
if W > 255 then
W := W - 223;
B := W;
end; //If
S2 := S2 + Chr(B);
KB := (KB + (I * 2)) and 127;
//-(1) Изменить/Усложнить! And 127 не трогать!
end; //For
end
else
begin //дешифровка
for I := 1 to LS do
begin
B := Byte(S1[I]);
if B > 32 then
begin
W := B - KB;
if W < 33 then
W := W + 223;
B := W;
end; //If
S2 := S2 + Chr(B);
KB := (KB + (I * 2)) and 127;
//---(2) Сделать как в (1)! And 127 не трогать!
end; //For
end; //Else
Result := S2;
end;
Пример использования:
// В вычисляемом поле для отображения текста в нормальном виде: TableNewName.Value := Morf(TableName.Value, True); // Для занесения в таблицу в зашифрованном виде например из Edit: TableName.Value := Morf(Edit.Text, False);
Шифрование текста по введенному паролю.
Автор: Danger WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Шифрование текста
Процедура шифрует текст основываясь на введенном пароле.
Зависимости: Windows, SysUtils, Classes
Автор: Danger, robinzon2000@pochtamt.ru, Киев
Copyright: Danger
Дата: 04 мая 2002 г.
***************************************************** }
var
s: string;
procedure Code(var text: string; password: string;
decode: boolean);
var
i, PasswordLength: integer;
sign: shortint;
begin
PasswordLength := length(password);
if PasswordLength = 0 then
Exit;
if decode then
sign := -1
else
sign := 1;
for i := 1 to Length(text) do
text[i] := chr(ord(text[i]) + sign *
ord(password[i mod PasswordLength + 1]));
end;
Пример использования:
procedure TForm1.Button1Click(Sender: TObject); begin s := Memo1.Text; code(s, Edit1.Text, false); Memo1.Text := s; end; procedure TForm1.Button2Click(Sender: TObject); begin code(s, Edit1.Text, true); Memo1.Text := s; end;
Простое симметричное шифрование строк.
Автор: Anatoly Podgoretsky WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Шифрование строки
Предназначена для простого шифрование строк и паролей, ключ 96 бит, шифрование
симметричное.
Зависимости: UBPFD.decrypt
Автор: Anatoly Podgoretsky, anatoly@podgoretsky.com, Johvi
Copyright: (c) Anatoly Podgoretsky, 1996
Дата: 26 апреля 2002 г.
***************************************************** }
const
StartKey = 471; // Start default key
MultKey = 62142; // Mult default key
AddKey = 11719; // Add default key
// обязательно смените ключи до использования
function Encrypt(const InString: string; StartKey, MultKey, AddKey: Integer):
string;
var
I: Byte;
// Если поменять тип переменной I на Integer, то будет возможно
// шифрование текста длиной более 255 символом - VID.
begin
Result := '';
for I := 1 to Length(InString) do
begin
Result := Result + CHAR(Byte(InString[I]) xor (StartKey shr 8));
StartKey := (Byte(Result[I]) + StartKey) * MultKey + AddKey;
end;
end;
Пример использования:
if Encrypt(S, StartKey, MultKey, AddKey) <> OriginalPwd then ...
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Расшифровка строки
Предназначена для расшифровки строки, ранее зашифрованной фукцией UBPFD.Encrypt
Зависимости: UBPFD.Encrypt
Автор: Anatoly Podgoretsky, anatoly@podgoretsky.com, Johvi
Copyright: (c) Anatoly Podgoretsky, 1996
Дата: 26 апреля 2002 г.
***************************************************** }
const
StartKey = 471; // Start default key
MultKey = 62142; // Mult default key
AddKey = 11719; // Add default key
// обязательно смените ключи до использования
{$R-}
{$Q-}
function Decrypt(const InString: string; StartKey, MultKey, AddKey: Integer):
string;
var
I: Byte;
// Если поменять тип переменной I на Integer, то будет возможно
// шифрование текста длиной более 255 символом - VID.
begin
Result := '';
for I := 1 to Length(InString) do
begin
Result := Result + CHAR(Byte(InString[I]) xor (StartKey shr 8));
StartKey := (Byte(InString[I]) + StartKey) * MultKey + AddKey;
end;
end;
{$R+}
{$Q+}
Пример использования:
S := 'Ваш старый пароль: ';
Шифрование и дешифрование текстов по принципу S-Coder со скрытым ключом.
Автор: Yuri Btr WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Шифрование и дешифрование текстов по принципу S-Coder со скрытым ключом
После прменения выполнения функции Encrypt с входным исходным текстом,
результат будет иметь символы в диапазоне от 0 - 255 в ASCII таблице,
следовательно если вам надо сохранить где либо шифровку, то лучше
преобразовать в числовой вид - 16разрядный hex из ASCII таблицы (функция Write)
Зависимости: Windows, SysUtils
Автор: Yuri Btr, btr1978@rambler.ru, ICQ:125988632, Керчь
Copyright: EFD Systems http://www.mindspring.com/~efd
Дата: 23 мая 2002 г.
***************************************************** }
procedure EnCipher(var Source: AnsiString);
{Low order, 7-bit ASCII (char. 32-127) encryption designed for database use.
Control and high order (8 bit) characters are passed through unchanged.
Uses a hybrid method...random table substitution with bit-mangled output.
No passwords to worry with (the built-in table is the password). Not industrial
strength but enough to deter the casual hacker or snoop. Even repeating char.
sequences have little discernable pattern once encrypted.
NOTE: When displaying encrypted strings, remember that some characters
within the output range are interpreted by VCL components; for
example, '&'.}
asm
Push ESI //Save the good stuff
Push EDI
Or EAX,EAX
Jz @Done
Push EAX
Call UniqueString
Pop EAX
Mov ESI,[EAX] //String address into ESI
Or ESI,ESI
Jz @Done
Mov ECX,[ESI-4] //String Length into ECX
Jecxz @Done //Abort on null string
Mov EDX,ECX //initialize EDX with length
Lea EDI,@ECTbl //Table address into EDI
Cld //make sure we go forward
@L1:
Xor EAX,EAX
Lodsb //Load a byte from string
Sub AX,32 //Adjust to zero base
Js @Next //Ignore if control char.
Cmp AX,95
Jg @Next //Ignore if high order char.
Mov AL,[EDI+EAX] //get the table value
Test CX,3 //screw it up some
Jz @L2
Rol EDX,3
@L2:
And DL,31
Xor AL,DL
Add EDX,ECX
Add EDX,EAX
Add AL,32 //adjust to output range
Mov [ESI-1],AL //write it back into string
@Next:
Dec ECX
Jnz @L1
// Loop @L1 //do it again if necessary
@Done:
Pop EDI
Pop ESI
Jmp @Exit
// Ret //this does not work with Delphi 3 - EFD 971022
@ECTbl: //The encipher table
DB 75,85,86,92,93,95,74,76,84,87,91,94
DB 63,73,77,83,88,90,62,64,72,78,82,89
DB 51,61,65,71,79,81,50,52,60,66,70,80
DB 39,49,53,59,67,69,38,40,48,54,58,68
DB 27,37,41,47,55,57,26,28,36,42,46,56
DB 15,25,29,35,43,45,14,16,24,30,34,44
DB 06,13,17,23,31,33,05,07,12,18,22,32
DB 01,04,08,11,19,21,00,02,03,09,10,20
@Exit:
end;
procedure DeCipher(var Source: AnsiString);
{Decrypts a string previously encrypted with EnCipher.}
asm
Push ESI //Save the good stuff
Push EDI
Push EBX
Or EAX,EAX
Jz @Done
Push EAX
Call UniqueString
Pop EAX
Mov ESI,[EAX] //String address into ESI
Or ESI,ESI
Jz @Done
Mov ECX,[ESI-4] //String Length into ECX
Jecxz @Done //Abort on null string
Mov EDX,ECX //Initialize EDX with length
Lea EDI,@DCTbl //Table address into EDI
Cld //make sure we go forward
@L1:
Xor EAX,EAX
Lodsb //Load a byte from string
Sub AX,32 //Adjust to zero base
Js @Next //Ignore if control char.
Cmp AX,95
Jg @Next //Ignore if high order char.
Mov EBX,EAX //save to accumulate below
Test CX,3 //unscrew it
Jz @L2
Rol EDX,3
@L2:
And DL,31
Xor AL,DL
Add EDX,ECX
Add EDX,EBX
Mov AL,[EDI+EAX] //get the table value
Add AL,32 //adjust to output range
Mov [ESI-1],AL //store it back in string
@Next:
Dec ECX
Jnz @L1
// Loop @L1 //do it again if necessary
@Done:
Pop EBX
Pop EDI
Pop ESI
Jmp @Exit
// Ret Does not work with Delphi3 - EFD 971022
@DCTbl: //The decryption table
DB 90,84,91,92,85,78,72,79,86,93,94,87
DB 80,73,66,60,67,74,81,88,95,89,82,75
DB 68,61,54,48,55,62,69,76,83,77,70,63
DB 56,49,42,36,43,50,57,64,71,65,58,51
DB 44,37,30,24,31,38,45,52,59,53,46,39
DB 32,25,18,12,19,26,33,40,47,41,34,27
DB 20,13,06,00,07,14,21,28,35,29,22,15
DB 08,01,02,09,16,23,17,10,03,04,11,05
@Exit:
end;
procedure Crypt(var Source: Ansistring; const Key: AnsiString);
{Encrypt AND decrypt strings using an enhanced XOR technique similar to
S-Coder (DDJ, Jan. 1990). To decrypt, simply re-apply the procedure
using the same password key. This algorithm is reasonably secure on
it's own; however,there are steps you can take to make it even more
secure.
1) Use a long key that is not easily guessed.
2) Double or triple encrypt the string using different keys.
To decrypt, re-apply the passwords in reverse order.
3) Use EnCipher before using Crypt. To decrypt, re-apply Crypt
first then use DeCipher.
4) Some unique combination of the above
NOTE: The resultant string may contain any character, 0..255.}
asm
Push ESI //Save the good stuff
Push EDI
Push EBX
Or EAX,EAX
Jz @Done
Push EAX
Push EDX
Call UniqueString
Pop EDX
Pop EAX
Mov EDI,[EAX] //String address into EDI
Or EDI,EDI
Jz @Done
Mov ECX,[EDI-4] //String Length into ECX
Jecxz @Done //Abort on null string
Mov ESI,EDX //Key address into ESI
Or ESI,ESI
Jz @Done
Mov EDX,[ESI-4] //Key Length into EDX
Dec EDX //make zero based
Js @Done //abort if zero key length
Mov EBX,EDX //use EBX for rotation offset
Mov AH,DL //seed with key length
Cld //make sure we go forward
@L1:
Test AH,8 //build stream char.
Jnz @L3
Xor AH,1
@L3:
Not AH
Ror AH,1
Mov AL,[ESI+EBX] //Get next char. from Key
Xor AL,AH //XOR key with stream to make pseudo-key
Xor AL,[EDI] //XOR pseudo-key with Source
Stosb //store it back
Dec EBX //less than zero ?
Jns @L2 //no, then skip
Mov EBX,EDX //re-initialize Key offset
@L2:
Dec ECX
Jnz @L1
@Done:
Pop EBX //restore the world
Pop EDI
Pop ESI
end;
function Encrypt(text: Ansistring): Ansistring;
//Это прсто интерфейс функций EnCipher и Crypt для шифрования строки текста
begin
{шифруем текст}
EnCipher(Text);
{зашифровываем ключом в Edit1}
Crypt(Text, Form1.Edit1.Text);
Result := Text;
end;
function Decrypt(text: Ansistring): Ansistring;
//Это прсто интерфейс функций EnCipher и Crypt для дешифрования строки текста begin
begin
{расшифровываем ключом в Edit1}
Crypt(Text, Form1.Edit1.Text);
{расшифровываем результат}
DeCipher(Text);
Result := Text;
end;
function Write(text: Ansistring): Ansistring;
var
i: integer;
begin
Result := '';
for i := 1 to Length(text) do
{получаем hex код из текста}
Result := Result + InttoHex(ord(text[i]), 2);
end;
function Read(text: Ansistring): Ansistring;
var
i: integer;
begin
Result := '';
for I := 1 to Length(text) do
if odd(i) then
{получаем текст из hex кода}
Result := Result + Chr(StrtoInt('$' + text[i] + text[i + 1]));
end;
Пример использования:
// функции вставляем после Implementation и после {$R *.dfm}
// В программе пишем просто:
procedure TForm1.Button1Click(Sender: TObject);
begin
{Задайте здесь ваш секретный ключ}
Edit1.text := 'My special hidden key';
{Шифруем и преобразовываем тест в Memo1 и заносим результат в Memo2}
Memo2.text := Write(Encrypt(Memo1.text));
{Дешифруем и преобразовываем тест в Memo2 и заносим результат в Memo3}
Memo3.text := Decrypt(Read(Memo2.text));
));
end;
Алгоритм 128-битного шифрования (TEA).
Автор: Valts Silaputnins WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Алгоритм 128-битного шифрования (TEA)
Зависимости: system
Автор: Valts Silaputnins, valts@velns.org
Copyright: Valts Silaputnins
Дата: 19 августа 2002 г.
***************************************************** }
unit ucrypt;
interface
type
TEAKey = array[0..3] of cardinal;
//Use 64-bit aligned data size (8,16...) or else some data will be left unencrypted!
procedure TEA_Encode(Input, Output: pointer; size: integer; key: TEAKey);
procedure TEA_Decode(Input, Output: pointer; size: integer; key: TEAKey);
implementation
type
TEAData = array[0..1] of cardinal;
PTEAKey = ^TEAKey;
PTEAData = ^TEAData;
procedure TEA_Cipher(v: PTEAData; var w: PTEAData; k: PTEAKey);
var
y, z, sum, delta, n: Cardinal;
begin
y := (v)[0];
z := (v)[1];
sum := 0;
delta := $9E3779B9;
n := 32;
while (n > 0) do
begin
inc(y, (z shl 4 xor z shr 5) + z xor sum + (k)[sum and 3]);
inc(sum, delta);
inc(z, (y shl 4 xor y shr 5) + y xor sum + (k)[sum shr 11 and 3]);
dec(n);
end;
(w)[0] := y;
(w)[1] := z;
end;
procedure TEA_DeCipher(v: PTEAData; var w: PTEAData; k: PTEAKey);
var
y, z, sum, delta, n: Cardinal;
begin
y := v[0];
z := v[1];
sum := $0C6EF3720;
delta := $9E3779B9;
n := 32;
while (n > 0) do
begin
dec(z, (y shl 4 xor y shr 5) + y xor sum + k[sum shr 11 and 3]);
dec(sum, delta);
dec(y, (z shl 4 xor z shr 5) + z xor sum + k[sum and 3]);
dec(n);
end;
w[0] := y;
w[1] := z;
end;
procedure TEA_EnDec(encode: boolean; Input, Output: pointer; size: integer; key:
TEAKey);
var
DataIn, DataOut: TEAData;
DOut: PTEAData;
i, sz: integer;
begin
DOut := @DataOut;
sz := (size shr 3) shl 3;
i := 0;
repeat
DataIn[0] := Cardinal((pointer(Cardinal(Input) + Cardinal(i)))^);
DataIn[1] := Cardinal((pointer(Cardinal(Input) + Cardinal(i + 4)))^);
if encode then
TEA_Cipher(@DataIn, DOut, @key)
else
TEA_DECipher(@DataIn, DOut, @key);
Cardinal(pointer(Cardinal(Output) + Cardinal(i))^) := DataOut[0];
Cardinal(pointer(Cardinal(Output) + Cardinal(i + 4))^) := DataOut[1];
inc(i, 8);
until i >= sz;
end;
procedure TEA_Encode(Input, Output: pointer; size: integer; key: TEAKey);
begin
TEA_EnDec(true, Input, Output, size, key);
end;
procedure TEA_Decode(Input, Output: pointer; size: integer; key: TEAKey);
begin
TEA_EnDec(false, Input, Output, size, key);
end;
end.
Шифрование текста.
Метод основан на сложении текста и пароля: "мой текст" + "пароль" = ('м'+'п')('о'+'а')... То есть каждый символ получают путем сложения соответствующих символов текста и пароля. Под "сложением символов" я подразумеваю сложение номеров этих символов. Обычно пароль длиннее текста, поэтому его размножают: "парольпар". Чтобы расшифровать текст, нужно проделать обратную операцию, то есть из текста вычесть пароль.При нажатии на Button1 эта программа шифрует текст из Memo1 при помощи пароля из Edit1. Результат сохраняется в строку s. Для наглядности зашифрованный текст также помещается в Memo1. При нажатии на Button2 текст из s расшифровывается. Если Вы нажмете Button1 два раза подряд, получится зашифрованный зашифрованный текст. Вернуть начальный текст можно будет двумя нажатиями на Button2. Но, поскольку в результате шифрования в строке могут появится.
var
s: string;
procedure Code(var text: string; password: string;
decode: boolean);
var
i, PasswordLength: integer;
sign: shortint;
begin
PasswordLength := length(password);
if PasswordLength = 0 then Exit;
if decode
then sign := -1
else sign := 1;
for i := 1 to Length(text) do
text[i] := chr(ord(text[i]) + sign *
ord(password[i mod PasswordLength + 1]));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
s := Memo1.Text;
code(s, Edit1.Text, false);
Memo1.Text := s;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
code(s, Edit1.Text, true);
Memo1.Text := s;
end;
|
Шифрование текста 2.
{$I-,R-}
Unit Crypter;
interface
Uses Objects;
procedure EnCrypt(var Pntr: Array of Char; ArrLen: Word; password: string);
{ - Закриптовать блок }
procedure DeCrypt(var Pntr: Array of Char; ArrLen: Word; password: string);
{ - Раскиптовать блок }
procedure EnCryptStream(var st: tStream; Password: String);
{ - Закриптовать поток }
procedure DeCryptStream(var st: tStream; Password: String);
{ - Раскиптовать поток }
implementation
procedure EnCrypt(var Pntr: Array of Char; ArrLen:Word; password: string);
var
len,pcounter: byte;
x:Word;
begin
len := length(password) div 2;
pcounter := 1;
for x:=0 to ArrLen-1 do begin
Pntr[x] := chr(ord(password[pcounter]) + ord(Pntr[x]) + len);
inc(pcounter);
if pcounter > length(password) then pcounter := 1;
end;
end;
procedure DeCrypt(var Pntr: Array of Char; ArrLen:Word; password: string);
var
len,pcounter: byte;
x:Word;
begin
len := length(password) div 2;
pcounter := 1;
for x:=0 to ArrLen-1 do begin
Pntr[x] := chr(ord(Pntr[x]) - ord(password[pcounter]) - len);
inc(pcounter);
if pcounter > length(password) then pcounter := 1;
end;
end;
type
pBuffer = ^tBuffer;
tBuffer = Array[1..$FFFF] of Char;
procedure EnCryptStream(var st: tStream; Password: String);
var
buf: pBuffer;
StSize, StPos, p: Longint;
begin
if (@st=nil) or (Password='') then exit;
New(buf);
StPos:=st.GetPos;
StSize:=st.GetSize;
st.Reset;
st.Seek(0);
repeat
p:=st.GetPos;
if SizeOf(Buf^)> St.GetSize-St.GetPosthen st.Read(buf^,St.GetSize-St.GetPos)
else st.Read(buf^,SizeOf(Buf^));
EnCrypt(buf^,SizeOf(buf^),password);
st.Reset;
st.Seek(p);
st.Write(buf^,SizeOf(Buf^));
until (St.GetSize=St.GetPos);
st.Seek(StSize);
st.Truncate;
st.Seek(StPos);
Dispose(buf);
end;
procedure DeCryptStream(var st: tStream; Password: String);
var
buf: pBuffer;
StSize, StPos, p: Longint;
begin
if (@st=nil) or (Password='') then exit;
New(buf);
StPos:=st.GetPos;
StSize:=st.GetSize;
st.Reset;
st.Seek(0);
repeat
p:=st.GetPos;
if SizeOf(Buf^)> St.GetSize-St.GetPosthen st.Read(buf^,St.GetSize-St.GetPos)
else st.Read(buf^,SizeOf(Buf^));
DeCrypt(buf^,SizeOf(buf^),password);
st.Reset;
st.Seek(p);
st.Write(buf^,SizeOf(Buf^));
until (St.GetSize=St.GetPos);
st.Seek(StSize);
st.Truncate;
st.Seek(StPos);
Dispose(buf);
end;
end.
|
Шифрование строки InString, с возможностью корректного сохранения результата шифрования в TEXT-FILE.
Автор: VID WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Шифрование строки InString, с возможностью корректного
сохранения результата шифрования в TEXT-FILE
Функция представляет модификацию функции UBPFD.Encrypt.
Отличие от указанной функции заключается в том, что функция
EncryptEX возвращает результат, обработанный функцией
UBPFD.StгТоАsсii, т.е. обеспечивает возможность
корректного сохранения шифр-текста в текстовый файл.
Зависимости: UBPFD.Encrypt, UBPFD.StrToAscii
Автор: VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright: VID
Дата: 30 апреля 2002 г.
***************************************************** }
function EncryptEX(const InString: string; StartKey, MultKey, AddKey: Integer):
string;
begin
Result := StrTOAscii(Encrypt(InString, StartKey, MultKey, AddKey));
end;
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Расшифровка строки InString, зашифрованной с помощью функции EncryptEX
Функция является модификацией UBPFD.Decrypt. Отличие в том,
что UBPFD.DecryptEX расшифровывает шифр-текст, зашифрованный
с помощью функции UBPFD.EncryptEX
Зависимости: UBPFD.Decrypt, UBPFD.АsсiiТоStг
Автор: VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright: VID
Дата: 30 апреля 2002 г.
***************************************************** }
function DecryptEX(const InString: string; StartKey, MultKey, AddKey: Integer):
string;
begin
Result := Decrypt(AsciiToStr(InString), StartKey, MultKey, AddKey);
end;
Пример использования:
var
S: string;
begin
S := UBPFD.EncryptEX('String', 1, 1, 1);
// S является источником данных для функции UBPFD.DecryptEX
ShowMessage('Расшифровка: ' + UBPFD.DecryptEX(S, 1, 1, 1));
end;
URL кодирование строки.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> URL кодирование строки
Функция производит так назваемое URL кодирование строк для использования
в http запросах. Т.е. все алфавитно-цифровые символы и знак подчёикивания
'_' остаются неизменными, пробел заменяется на '+', а все остальные символы
на знак процента '%' с двумя шестнадцатеричными цифрами.
Зависимости: Windows
Автор: Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright: Dimka Maslov
Дата: 27 мая 2002 г.
***************************************************** }
function UrlEncode(Str: string): string;
function CharToHex(Ch: Char): Integer;
asm
and eax, 0FFh
mov ah, al
shr al, 4
and ah, 00fh
cmp al, 00ah
jl @@10
sub al, 00ah
add al, 041h
jmp @@20
@@10:
add al, 030h
@@20:
cmp ah, 00ah
jl @@30
sub ah, 00ah
add ah, 041h
jmp @@40
@@30:
add ah, 030h
@@40:
shl eax, 8
mov al, '%'
end;
var
i, Len: Integer;
Ch: Char;
N: Integer;
P: PChar;
begin
Result := '';
Len := Length(Str);
P := PChar(@N);
for i := 1 to Len do
begin
Ch := Str[i];
if Ch in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
Result := Result + Ch
else
begin
if Ch = ' ' then
Result := Result + '+'
else
begin
N := CharToHex(Ch);
Result := Result + P;
end;
end;
end;
end;
Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
begin
S := UrlEncode('Delphi World');
ShellExecute(Handle, 'open',
PChar('http://www.yandex.ru/yandsearch?text=' + S), '', '', SW_SHOWNORMAL);
end;
{ **** UBPFD *********** by delphibase.endimus.com ****
>> URL декодирование строки
Функция выполняет URL декодирование строки, заменяя все подстроки вида
'%HH', где 'HH' - шестнадцатеричные цифры, на соответствующие символы.
Зависимости: Windows
Автор: Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright: Dimka Maslov
Дата: 27 мая 2002 г.
***************************************************** }
function UrlDecode(Str: string): string;
function HexToChar(W: word): Char;
asm
cmp ah, 030h
jl @@error
cmp ah, 039h
jg @@10
sub ah, 30h
jmp @@30
@@10:
cmp ah, 041h
jl @@error
cmp ah, 046h
jg @@20
sub ah, 041h
add ah, 00Ah
jmp @@30
@@20:
cmp ah, 061h
jl @@error
cmp al, 066h
jg @@error
sub ah, 061h
add ah, 00Ah
@@30:
cmp al, 030h
jl @@error
cmp al, 039h
jg @@40
sub al, 030h
jmp @@60
@@40:
cmp al, 041h
jl @@error
cmp al, 046h
jg @@50
sub al, 041h
add al, 00Ah
jmp @@60
@@50:
cmp al, 061h
jl @@error
cmp al, 066h
jg @@error
sub al, 061h
add al, 00Ah
@@60:
shl al, 4
or al, ah
ret
@@error:
xor al, al
end;
function GetCh(P: PChar; var Ch: Char): Char;
begin
Ch := P^;
Result := Ch;
end;
var
P: PChar;
Ch: Char;
begin
Result := '';
P := @Str[1];
while GetCh(P, Ch) <> #0 do
begin
case Ch of
'+': Result := Result + ' ';
'%':
begin
Inc(P);
Result := Result + HexToChar(PWord(P)^);
Inc(P);
end;
else
Result := Result + Ch;
end;
Inc(P);
end;
end;
Кодировка полиалфавитным шифром Вигенера - xor кодировка.
Автор: DiVo WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Кодировка полиалфавитным шифром Вигенера - xor кодировка
Кодировка полиалфавитным шифром Вигенера - xor кодировка
одна функция для кодирования и декодирования
Input - входная строка. При кодировании это незакодированная строка,
при декодировнии это закодированная строка.
Key - слово ключ один и тот же в обоих случаях.
При совпадении символов во входной строке и
строке ключе на выходе получается символ '#0'.
Зависимости: Стандартные модули
Автор: Ru, DiVo_Ru@rambler.ru, Одесса
Copyright: DiVo 2002 creator Ru
Дата: 18 ноября 2002 г.
***************************************************** }
function VigenerCoDec(Input, Key: pchar): pchar
var
i, j: integer;
tmps, text: string;
begin
text := Input;
for i := 1 to length(text) do
begin
if i > length(key) then
j := i mod length(key)
else
j := i;
tmps := tmps + chr((ord(text[i])) xor (ord(key[j])));
end;
result := pchar(tmps);
end;
Пример использования:
Text := edit1.text; K := edit2.text; Edit3.text := VigenerCoDec(Text, K); // Закодировать: Input := 'Привет я РУ'; Key := 'hello'; result := 'ГуднйзНвН++'; // Восстановить: Input := 'ГуднйзНвН++'; Key := 'hello'; result := 'Привет я РУ';
Шифрование Bitmap.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure EncryptBMP(const BMP: TBitmap; Key: Integer);
var
BytesPorScan: Integer;
w, h: integer;
p: pByteArray;
begin
try
BytesPorScan := Abs(Integer(BMP.ScanLine[1]) -
Integer(BMP.ScanLine[0]));
except
raise Exception.Create('Error');
end;
RandSeed := Key;
for h := 0 to BMP.Height - 1 do
begin
P := BMP.ScanLine[h];
for w := 0 to BytesPorScan - 1 do
P^[w] := P^[w] xor Random(256);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
EncryptBMP(Image1.Picture.Bitmap, 623);
Image1.Refresh;
end;
{ Call the function again to decrypt it }
{ Zum Entschlusseln die Funktion nochmals aufrufen }
Шифрование файлов, строк и памяти.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
unit EZCrypt;
{modeled by Ben Hochstrasser(bhoc@surfeu.ch) after some code snippet from borland}
interface
uses Windows, Classes;
type
TWordTriple = Array[0..2] of Word;
function FileEncrypt(InFile, OutFile: String; Key: TWordTriple): boolean;
function FileDecrypt(InFile, OutFile: String; Key: TWordTriple): boolean;
function TextEncrypt(const s: string; Key: TWordTriple): string;
function TextDecrypt(const s: string; Key: TWordTriple): string;
function MemoryEncrypt(Src: Pointer; SrcSize: Cardinal; Target: Pointer; TargetSize: Cardinal; Key: TWordTriple): boolean;
function MemoryDecrypt(Src: Pointer; SrcSize: Cardinal; Target: Pointer; TargetSize: Cardinal; Key: TWordTriple): boolean;
implementation
function MemoryEncrypt(Src: Pointer; SrcSize: Cardinal; Target: Pointer; TargetSize: Cardinal; Key: TWordTriple): boolean;
var
pIn, pOut: ^byte;
i : Cardinal;
begin
if SrcSize = TargetSize then
begin
pIn := Src;
pOut := Target;
for i := 1 to SrcSize do
begin
pOut^ := pIn^ xor (Key[2] shr 8);
Key[2] := Byte(pIn^ + Key[2]) * Key[0] + Key[1];
inc(pIn);
inc(pOut);
end;
Result := True;
end else
Result := False;
end;
function MemoryDecrypt(Src: Pointer; SrcSize: Cardinal; Target: Pointer; TargetSize: Cardinal; Key: TWordTriple): boolean;
var
pIn, pOut: ^byte;
i : Cardinal;
begin
if SrcSize = TargetSize then
begin
pIn := Src;
pOut := Target;
for i := 1 to SrcSize do
begin
pOut^ := pIn^ xor (Key[2] shr 8);
Key[2] := byte(pOut^ + Key[2]) * Key[0] + Key[1];
inc(pIn);
inc(pOut);
end;
Result := True;
end else
Result := False;
end;
function TextCrypt(const s: string; Key: TWordTriple; Encrypt: Boolean): string;
var
bOK: Boolean;
begin
SetLength(Result, Length(s));
if Encrypt then
bOK := MemoryEncrypt(PChar(s), Length(s), PChar(Result), Length(Result), Key)
else
bOK := MemoryDecrypt(PChar(s), Length(s), PChar(Result), Length(Result), Key);
if not bOK then Result := '';
end;
function FileCrypt(InFile, OutFile: String; Key: TWordTriple; Encrypt: Boolean): boolean;
var
MIn, MOut: TMemoryStream;
begin
MIn := TMemoryStream.Create;
MOut := TMemoryStream.Create;
Try
MIn.LoadFromFile(InFile);
MOut.SetSize(MIn.Size);
if Encrypt then
Result := MemoryEncrypt(MIn.Memory, MIn.Size, MOut.Memory, MOut.Size, Key)
else
Result := MemoryDecrypt(MIn.Memory, MIn.Size, MOut.Memory, MOut.Size, Key);
MOut.SaveToFile(OutFile);
finally
MOut.Free;
MIn.Free;
end;
end;
function TextEncrypt(const s: string; Key: TWordTriple): string;
begin
Result := TextCrypt(s, Key, True);
end;
function TextDecrypt(const s: string; Key: TWordTriple): string;
begin
Result := TextCrypt(s, Key, False);
end;
function FileEncrypt(InFile, OutFile: String; Key: TWordTriple): boolean;
begin
Result := FileCrypt(InFile, OutFile, Key, True);
end;
function FileDecrypt(InFile, OutFile: String; Key: TWordTriple): boolean;
begin
Result := FileCrypt(InFile, OutFile, Key, False);
end;
Шифрование текста по ключевым числам.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
This two functions are used to encrypt and decrypt text.
Here's how to use it:
The four entries Key1, Key2, Key3 and Key4 are numbers
that can range from 1 to 120. In order to decrypt a text,
you must use the same numbers you used to encrypt the text.
No one that doesn't know what values were used on Key1, Key2, Key3 and Key4
will be able to decrypt your text!
Note that Key1*Key4 MUST be different than Key2*Key3.
If any Key is zero, or Key1*Key4 is equal to Key2*Key3,
the function will return ''.
In Brief:
Key1, Key2, Key3, Key4 : integer from range[1..120]
Key1*Key4 Key2*Key3
}
function Encrypt(Text : string; Key1, Key2, Key3, Key4 : Integer) : string;
var
BufS, Hexa, Hexa1, Hexa2 : string;
BufI, BufI2, Sc, Sl, Num1, Num2, Num3, Num4, Res1, Res2, Res3, Res4 : Integer;
begin
Sl := Length(Text);
Sc := 0;
BufS := '';
if (Key1 in [1 .. 120]) and (Key2 in [1 .. 120]) and (Key3 in [1 .. 120]) and (Key4 in [1 .. 120]) then
begin
BufI := Key1 * Key4;
BufI2 := Key3 * Key2;
BufI := BufI - BufI2;
if BufI = 0 then
begin
Result := '';
Exit;
end;
end
else
begin
Result := '';
Exit;
end;
repeat
Inc(Sc);
if Sc > Sl then Num1 := 0 else Num1 := Ord(Text[Sc]);
Inc(Sc);
if Sc > Sl then Num2 := 0 else Num2 := Ord(Text[Sc]);
Inc(Sc);
if Sc > Sl then Num3 := 0 else Num3 := Ord(Text[Sc]);
Inc(sc);
if Sc > Sl then Num4 := 0 else Num4 := Ord(Text[Sc]);
Res1 := Num1 * Key1;
BufI := Num2 * Key3;
Res1 := Res1 + BufI;
Res2 := Num1 * Key2;
BufI := Num2 * Key4;
Res2 := Res2 + BufI;
Res3 := Num3 * Key1;
BufI := Num4 * Key3;
Res3 := Res3 + BufI;
Res4 := Num3 * Key2;
BufI := Num4 * Key4;
Res4 := Res4 + BufI;
for BufI := 1 to 4 do
begin
case BufI of
1 : Hexa := IntToHex(Res1, 4);
2 : Hexa := IntToHex(Res2, 4);
3 : Hexa := IntToHex(Res3, 4);
4 : Hexa := IntToHex(Res4, 4);
end;
Hexa1 := '$' + Hexa[1] + Hexa[2];
Hexa2 := '$' + Hexa[3] + Hexa[4];
if (Hexa1 = '$00') and (Hexa2 = '$00') then
begin
Hexa1 := '$FF';
Hexa2 := '$FF';
end;
if Hexa1 = '$00' then Hexa1 := '$FE';
if Hexa2 = '$00' then
begin
Hexa2 := Hexa1;
Hexa1 := '$FD';
end;
BufS := BufS + Chr(StrToInt(Hexa1)) + Chr(StrToInt(Hexa2));
end;
until Sc >= Sl;
Result := BufS;
end;
function Decrypt(Text : string; Key1, Key2, Key3, Key4 : Integer) : string;
var
BufS, Hexa1, Hexa2 : string;
BufI, BufI2, Divzr, Sc, Sl, Num1, Num2, Num3, Num4, Res1, Res2, Res3, Res4 : Integer;
begin
Sl := Length(Text);
Sc := 0;
BufS := '';
if (Key1 in [1 .. 120]) and (Key2 in [1 .. 120]) and (Key3 in [1 .. 120]) and (Key4 in [1 .. 120]) then
begin
Divzr := Key1 * Key4;
BufI2 := Key3 * Key2;
Divzr := Divzr - BufI2;
if Divzr = 0 then
begin
Result := '';
Exit;
end;
end
else
begin
Result := '';
Exit;
end;
repeat
for BufI := 1 to 4 do
begin
Inc(Sc);
Hexa1 := IntToHex(Ord(Text[Sc]), 2);
Inc(Sc);
Hexa2 := IntToHex(Ord(Text[Sc]), 2);
if Hexa1 = 'FF' then
begin
Hexa1 := '00';
Hexa2 := '00';
end;
if Hexa1 = 'FE' then Hexa1 := '00';
if Hexa1 = 'FD' then
begin
Hexa1 := Hexa2;
Hexa2 := '00';
end;
case BufI of
1 : Res1 := StrToInt('$' + Hexa1 + Hexa2);
2 : Res2 := StrToInt('$' + Hexa1 + Hexa2);
3 : Res3 := StrToInt('$' + Hexa1 + Hexa2);
4 : Res4 := StrToInt('$' + Hexa1 + Hexa2);
end;
end;
BufI := Res1 * Key4;
BufI2 := Res2 * Key3;
Num1 := BufI - BufI2;
Num1 := Num1 div Divzr;
BufI := Res2 * Key1;
BufI2 := Res1 * Key2;
Num2 := BufI - BufI2;
Num2 := Num2 div Divzr;
BufI := Res3 * Key4;
BufI2 := Res4 * Key3;
Num3 := BufI - BufI2;
Num3 := Num3 div Divzr;
BufI := Res4 * Key1;
BufI2 := Res3 * Key2;
Num4 := BufI - BufI2;
Num4 := Num4 div Divzr;
BufS := BufS + Chr(Num1) + Chr(Num2) + Chr(Num3) + Chr(Num4);
until Sc >= Sl;
Result := BufS;
end;
Способ шифрования и дешифрования пароля.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Button1Click(Sender: TObject);
var
s: String[255];
c: array[0..255] of Byte absolute s;
i: Integer;
begin
{encode}
s := 'SwissDelphiCenter.ch';
for i := 1 to Ord(s[0]) do c[i] := 23 xor c[i];
Label1.Caption := s;
{Decode}
s := Label1.Caption;
for i := 1 to Length(s) do s[i] := Char(23 xor Ord(c[i]));
Label2.Caption := s;
end;
Определяем номер последнего дня в месяце.
Автор: Ревенко Алексей.
// Колическтво дней в любом месяце любого
// года можно получить с помощью EndOfAMonth
var
YYYY, MM, DD: Word;
D: TDateTime;
begin
DecodeDate(Date, YYYY, MM, DD);
D := EndOfAMonth(YYYY, {Номер месяца});
DecodeDate(D, YYYY, MM, DD); // DD - номер последнего дня в месяце
end;
Увеличить процессорное время, выделяемое программе.
| Фирма Intel после выпуска процессоров Pentium 1, Pentium 2, Pentium 3, Pentium 4 выпустила новые процессоры Pentium жив и Pentium возвращается. |
Следующий пример изменяет приоритет приложения. Изменение приоритета следует использовать с осторожностью - т.к. присвоение слишком высокого приоритета может привести к медленной работе остальных программ и системы в целом ;-).
procedure TForm1.Button1Click(Sender: TObject); var ProcessID : DWORD; ProcessHandle : THandle; ThreadHandle : THandle; begin ProcessID := GetCurrentProcessID; ProcessHandle := OpenProcess(PROCESS_SET_INFORMATION, false, ProcessID); SetPriorityClass(ProcessHandle, REALTIME_PRIORITY_CLASS); ThreadHandle := GetCurrentThread; SetThreadPriority(ThreadHandle, THREAD_PRIORITY_TIME_CRITICAL); end; |
Как осуществить ввод текста в компоненте Label.
| Все, конечно, слышали выражение "программа партии". А что было бы, если бы политические партии действительно писали программы? Программа КПРФ Последние версии документированы как совместимые с другими программами, но на самом деле после инсталляции стремятся их уничтожить. Интерфейс текстовый, белые буквы на красном фоне. Ядро написано более ста лет назад, с тех пор не только не исправлены старые глюки, но и добавлены многие новые. Считает, что все файлы должны быть одинакового размера. Периодически объявляет какое-нибудь расширение вредным и удаляет файлы с этим расширением по всему диску. Запросы на подтверждение имеют единственный вариант ответа: "Даешь!" Пытается выделять под свои задачи ресурсы, не заботясь об их физическом наличии, и в случае неудачи блокирует вывод сообщений об ошибках. При запросе диагностики выводит заранее сформированный файл, сообщающий, что все хорошо. Не принимает электронную почту извне и прибивает задачи, пытающиеся ее отправить. Не совместима с современной техникой. Не может быть деинсталлирована легальными средствами. Программа ЛДПР Имеет яркий, аляповатый интерфейс и неотключаемые звуковые эффекты повышенной громкости. Может быть инсталлирована хоть на ХТ, однако заявляет о чрезвычайно высоких требованиях к системным ресурсам и на любой машине стремится захватить их как можно больше. В сообщениях активно использует нецензурную лексику, в особенности если пользователь - женщина. Постоянно грозится взломать сервер Пентагона и устроить mail-bombing президенту США, но физически не содержит модулей, способных на что-то подобное. Выводит множество предупреждений и сообщений об ошибках, в том числе самых невероятных, но никогда не виснет, не падает и не выгружается из памяти, даже если пользователь очень захочет. При выводе диагностики начинает торговаться с пользователем, обещая вывести хорошие результаты, если ей выделят больше ресурсов. Запрашивает высокую цену за инсталляцию и вдвое большую - за деинсталляцию. Программа "Яблока" Имеет красивый, стильный, но не всем понятный интерфейс. Отказывается работать с другими программами. Содержит мощный диагностический модуль, всегда готова дать пользователю подробный совет в любой ситуации, но на предложение выполнить конкретную операцию неизменно выдает сообщение с аргументированным объяснением, почему данная операция выполнена быть не может. Работает только на компьютерах фирмы Apple. Программа НДР Некогда была главным системным модулем и по-прежнему пытается выполнять эту функцию. Имеет непонятный интерфейс и нечитабельную документацию. Сообщения об ошибках приписывает предыдущим и последующим версиям, а также внешним процессам. Пытается решить проблему нехватки ресурсов, постоянно прося пользователя вставить новую дискету и обещая впоследствии отдать две. Когда задача запрашивает память, сообщает, что память успешно выделена, но на самом деле выделяет ее не раньше чем через полгода, отобрав у других задач. В конечном итоге наглухо вешает машину. |
Многие программисты задавая такой вопрос получают на него стандартный ответ "используй edit box." На самом же деле этот вопрос вполне решаем, хотя лейблы и не основаны на окне и, соответственно не могут получать фокус ввода и, соответственно не могут получать символы, вводимые с клавиатуры. Давайте рассмотрим шаги, которые были предприняты мной для разработки данного компонента.
Первый шаг, это кнопка, которая может отображать вводимый текст:
type
TInputButton = class(TButton)
private
procedure WmChar (var Msg: TWMChar); message wm_Char;
end;
procedure TInputButton.WmChar (var Msg: TWMChar);
var
Temp: string;
begin
if Char (Msg.CharCode) = #8 then
begin
Temp := Caption;
Delete (Temp, Length (Temp), 1);
Caption := Temp;
end
else
Caption := Caption + Char (Msg.CharCode);
end;
|
С меткой (label) дела обстоят немного сложнее, так как прийдётся создать некоторые ухищрения, чтобы обойти её внутреннюю структуру. Впринципе, проблему можно решить созданием других скрытых компонент (кстати, тот же edit box). Итак, посмотрим на объявление класса:
type
TInputLabel = class (TLabel)
private
MyEdit: TEdit;
procedure WMLButtonDown (var Msg: TMessage); message wm_LButtonDown;
protected
procedure EditChange (Sender: TObject);
procedure EditExit (Sender: TObject);
public
constructor Create (AOwner: TComponent); override;
end;
|
Когда метка (label) создана, то она в свою очередь создаёт edit box и устанавливает несколько обработчиков событий для него. Фактически, если пользователь кликает по метке, то фокус перемещается на (невидимый) edit box, и мы используем его события для обновления метки. Обратите внимание на ту часть кода, которая подражает фокусу для метки (рисует прямоугольничек), основанная на API функции DrawFocusRect:
constructor TInputLabel.Create (AOwner: TComponent); begin inherited Create (AOwner); MyEdit := TEdit.Create (AOwner); MyEdit.Parent := AOwner as TForm; MyEdit.Width := 0; MyEdit.Height := 0; MyEdit.TabStop := False; MyEdit.OnChange := EditChange; MyEdit.OnExit := EditExit; end; procedure TInputLabel.WMLButtonDown (var Msg: TMessage); begin MyEdit.SetFocus; MyEdit.Text := Caption; (Owner as TForm).Canvas.DrawFocusRect (BoundsRect); end; procedure TInputLabel.EditChange (Sender: TObject); begin Caption := MyEdit.Text; Invalidate; Update; (Owner as TForm).Canvas.DrawFocusRect (BoundsRect); end; procedure TInputLabel.EditExit (Sender: TObject); begin (Owner as TForm).Invalidate; end; |
Использование Enter как Tab в TDBGrid.
| Заядлый компьютерщик заходит в банк снять денег. Кассирша ему: - Сколько? - Мне плиз 1024 рубля! |
Данный код включает обработку клавиши Enter для всего приложения, включая поля и пр.. Код для работы с компонентом DBGrid заключен в блок ELSE. Приведенный код не имитирует поведение клавиши Tab, связанное с переходом на следующую запись когда курсор достигает последней колонки табличной сетки, в нашем случае он перемещается на первую колонку.
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
{ Это обработчик события OnKeyPress для ФОРМЫ! }
{ Вы должны также установить свойство формы KeyPreview в True }
begin
if Key = #13 then { если это клавиша Enter }
if not (ActiveControl is TDBGrid) then
begin { если не на TDBGrid }
Key := #0; { гасим клавишу Enter }
Perform(WM_NEXTDLGCTL, 0, 0);
{ перемещаемя на следующий элемент управления }
end
else if (ActiveControl is TDBGrid) then { если это TDBGrid }
with TDBGrid(ActiveControl) do
if selectedindex < (fieldcount - 1) then { увеличиваем поле }
selectedindex := selectedindex + 1
else
selectedindex := 0;
end;
|
Перейти по Enter на следующее поле в TDBGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then
begin
if DBGrid1.Columns.Grid.SelectedIndex < DBGrid1.Columns.Count - 1 then
DBGrid1.Columns[DBGrid1.Columns.grid.SelectedIndex + 1].Field.FocusControl
else
begin
Table1.Next;
DBGrid1.Columns[0].field.FocusControl;
end;
end;
end;
Запрос пароля при инициализации приложения.
Автор: Xavier Pacheco
{
Copyright © 1998 by Delphi 4 Developer's Guide - Xavier Pacheco and Steve Teixeira
}
program Initialize;
uses
Forms,
Dialogs,
Controls,
MainFrm in 'MainFrm.pas' {MainForm};
{$R *.RES}
var
Password: string;
begin
if InputQuery('Password', 'Enter your password', PassWord) then
if Password = 'D5DG' then
begin
// Other initialization routines can go here.
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end
else
MessageDlg('Incorrect Password, terminating program', mtError, [mbok], 0);
end.
Как подменить нажатия ENTER на нажатия TAB.
| Программист в церкви молится: - Во имя отца и сына и святого духа. Enter. |
Существует множество методов решения этой проблемы, но самый быстрый способ, это перехват нажатия клавиш, перед тем как их получит форма:
В секции формы PRIVATE добавьте:
procedure CMDialogKey(var Msg: TWMKey); message CM_DIALOGKEY; |
В секции IMPLEMENTATION добавьте:
procedure TForm1.CMDialogKey(var Msg: TWMKey);
begin
if not (ActiveControl is TButton) then
if Msg.Charcode = 13 then
Msg.Charcode := 9;
inherited;
end;
|
Тем самым мы исключаем срабатывания нашей подмены, если фокус находится на кнопке.
Чтобы ускорить работу приложения, не надо активизировать свойство формы KEYPREVIEW.
Как подменить нажатия ENTER на нажатия TAB 2.
| Тонет программист. И во все горло орет: - F1, F1, F1, тьфу ты, помогите, помогите, помогите!!! |
Как-то бухгалтер, который пользовался моей программой, заявил, что ему не удобно перескакивать пустые поля в форме кнопкой Tab, и что намного удобнее это делать обычным Enter-ом. Предлагаю посмотреть, как я решил эту проблемму.
procedure Tform1.FormKeyDown(Sender: TObject;
var Key: Word; Shift: TShiftState);
var
ACtrl: TWinControl;
begin
if key = 13 then
begin
ACtrl := ActiveControl;
if ACtrl is TCustomMemo then
exit;
repeat
ACtrl:= FindNextControl(ACtrl,true,true,false);
until
(ACtrl is TCustomEdit) or
(ACtrl is TCustomComboBox) or
(ACtrl is TCustomListBox) or
(ACtrl is TCustomCheckBox) or
(ACtrl is TRadioButton);
ACtrl.SetFocus ;
end;
end;
|
Не забудьте установить свойство формы KeyPreview в true.
Как Вы можете видеть; этот код использует функцию FindNextControl, которая ищет следующий свободный контрол.
Так как все формы в моём приложении наследуются от одной, то достаточно поместить этот код в главную форму и после этого все формы будут реагировать на нажатие Enter подобным образом.
Как подменить нажатия ENTER на нажатия TAB 3.
procedure TForm1.FormCreate(Sender: TObject);
begin
Form1.KeyPreview := true;
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
c: TControl;
begin
if Key <> 13 then
Exit;
repeat
c := Form1.FindNextControl(Form1.ActiveControl, true, true, true);
(c as TWinControl).SetFocus;
until
c is TEdit;
end;
|
Как подменить нажатия ENTER на нажатия TAB 4.
| Компьютерный интим - клавиатуру тискают, мышку лапают, а коврик гладят. |
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = Chr(VK_RETURN) then
begin
Perform(WM_NEXTDLGCTL,0,0);
key:= #0;
end;
end;
|
Как подменить нажатия ENTER на нажатия TAB 5.
| Разговаривают два программера: - Представляешь, был вчера на презентации, а там... И коньячок, и вискарек, и водочка! Через полчаса - уже никакой, а на столах - не убавляется, обидно просто. Ну, я не растерялся, пошел в сортир, поблевал хорошенько - и продолжил! - Вот и мне CTRL+ALT+DEL всегда помогает. |
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char); begin if Key = #13 then begin SelectNext(Sender as TWinControl, True, True); Key := #0; end; end; |
Работа клавиши Enter как Tab.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
This code gives the key the same habbit as the key to
change focus between Controls.
Mit diesem Code kann man sich mit der Enter Taste zwischen mehreren
Textfeldern bewegen.(anstatt nur mit der Tabulatortaste)
}
// Form1.KeyPreview := True !
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then
begin
Key := #0;
{ check if SHIFT - Key is pressed }
if GetKeyState(VK_Shift) and $8000 <> 0 then
PostMessage(Handle, WM_NEXTDLGCTL, 1, 0)
else
PostMessage(Handle, WM_NEXTDLGCTL, 0, 0);
end;
end;
Получить список шрифтов и их размеров для текущего принтера.
Следующий пример помещает в компонент TMemo список шрифтов и их размеров для текущего принтера.
uses Printers;
function EnumFontFamilyProc(var lf: TLogFont;
var tm: TNewTextMetric;
FontType: integer;
var Memo: TMemo): integer
{$IFDEF WIN32} stdcall;
{$ELSE}; export;
{$ENDIF}
begin
Memo.Lines.Add(StrPas(@lf.lfFaceName) +
#32 + IntToStr(lf.lfHeight));
result := 1;
end;
function EnumFontFamiliesProc(var lf: TLogFont;
var tm: TNewTextMetric;
FontType: integer;
var Memo: TMemo): integer
{$IFDEF WIN32} stdcall;
{$ELSE}; export;
{$ENDIF}
begin
if FontType = TRUETYPE_FONTTYPE then
begin
Memo.Lines.Add(StrPas(@lf.lfFaceName) + #32 + 'All Sizes');
end
else
EnumFontFamilies(Printer.Handle,
@lf.lfFaceName,
@EnumFontFamilyProc,
LongInt(@Memo));
result := 1;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
tm: TTextMetric;
i: integer;
begin
if PrintDialog1.Execute then
begin
EnumFontFamilies(Printer.Handle,
nil,
@EnumFontFamiliesProc,
LongInt(@Memo1));
end;
end;
|
Пример EnumWindows.
Автор: Paul Powers (Borland).
Создайте форму и разместите на ней два компонента ListBox. Скопируйте код, показанный ниже. Запустите SysEdit. Запустите форму Delphi. Первый ListBox должен содержать список всех запущенных приложений. Дважды щелкните на SysEdit и нижний ListBox покажет дочернее MDI-окно программы SysEdit.
unit Wintask1;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
ListBox2: TListBox;
procedure FormCreate(Sender: TObject);
procedure ListBox1DblClick(Sender: TObject);
private
function enumListOfTasks(hWindow: hWnd): Bool; export;
function enumListOfChildTasks(hWindow: hWnd): Bool; export;
end;
THoldhWnd = class(TObject)
private
public
hWindow: hWnd;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
enumWindows(@TForm1.EnumListOfTasks, Longint(Self));
if (ListBox1.Items.Count > 0) then
ListBox1.ItemIndex := 0;
end;
function TForm1.enumListOfTasks(hWindow: hWnd): Bool;
var
HoldString: PChar;
WindowStyle: Longint;
IsAChild: Word;
HoldhWnd: THoldhWnd;
begin
GetMem(HoldString, 256);
HoldhWnd := THoldhWnd.Create;
HoldhWnd.hWindow := hWindow;
WindowStyle := GetWindowLong(hWindow, GWL_STYLE);
WindowStyle := WindowStyle and Longint(WS_VISIBLE);
IsAChild := GetWindowWord(hWindow, GWW_HWNDPARENT);
{Добавляем строку с текстом задачи или именем класса и дескриптор в ListBox1.Items }
if (GetWindowText(hWindow, HoldString, 255) > 0) and
(WindowStyle > 0) and (IsAChild = Word(nil)) then
ListBox1.Items.AddObject(StrPas(HoldString), TObject(HoldhWnd))
else if (GetClassName(hWindow, HoldString, 255) > 0) and
(WindowStyle > 0) and (IsAChild = Word(nil)) then
ListBox1.Items.AddObject(Concat('<', StrPas(HoldString), '>'),
TObject(HoldhWnd));
FreeMem(HoldString, 256);
HoldhWnd := nil;
Result := TRUE;
end;
function TForm1.enumListOfChildTasks(hWindow: hWnd): Bool;
var
HoldString: PChar;
WindowStyle: Longint;
IsAChild: Word;
HoldhWnd: THoldhWnd;
begin
GetMem(HoldString, 256);
HoldhWnd := THoldhWnd.Create;
HoldhWnd.hWindow := hWindow;
WindowStyle := GetWindowLong(hWindow, GWL_STYLE);
WindowStyle := WindowStyle and Longint(WS_VISIBLE);
IsAChild := GetWindowWord(hWindow, GWW_HWNDPARENT);
{Добавляем строку с текстом задачи или именем класса и дескриптор в ListBox1.Items }
if (GetWindowText(hWindow, HoldString, 255) > 0) and
(WindowStyle > 0) and (IsAChild <> Word(nil)) then
ListBox2.Items.AddObject(StrPas(HoldString), TObject(HoldhWnd))
else if (GetClassName(hWindow, HoldString, 255) > 0) and
(WindowStyle > 0) and (IsAChild = Word(nil)) then
ListBox2.Items.AddObject(Concat('<', StrPas(HoldString), '>'),
TObject(HoldhWnd));
FreeMem(HoldString, 256);
HoldhWnd := nil;
Result := TRUE;
end;
procedure TForm1.ListBox1DblClick(Sender: TObject);
begin
enumChildWindows(THoldhWnd(ListBox1.Items.Objects[ListBox1.ItemIndex]).hWindow,
@TForm1.enumListOfChildTasks, Longint(Self));
ListBox2.RePaint;
end;
end.
|
Использование перечислимых констант.
Автор: Dan Miser(Med Data Systems, Inc.) В Инспекторе Объектов я пытаюсь получить раскрывающийся список для моего перечислимого типа...Для этого вам необходимо создать собственный редактор свойства. Ниже приведен его простой пример:
type
TBaudRateProperty = class(TStringProperty)
public
function GetAttributes: TPropertyAttributes; override;
procedure GetValues(Proc: TGetStrProc); override;
function GetValue: string; override;
procedure SetValue(const Value: string); override;
end;
...
type
TBaudRate = (br110, br300, br600, br1200, br2400, br4800, br9600, br14400,
br19200, br38400, br56000, br128000, br256000);
const
BaudList: array[TBaudRate] of string[7] =
('110', '300', '600', '1200', '2400', '4800', '9600', '14400', '19200',
'38400', '56000', '128000', '256000');
{TBaudRateProperty}
function TBaudRateProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paValueList];
end;
procedure TBaudRateProperty.GetValues(Proc: TGetStrProc);
var
i: TBaudRate;
begin
for i := Low(TBaudRate) to High(TBaudRate) do
Proc(BaudList[i]);
end;
function TBaudRateProperty.GetValue: string;
begin
Result := BaudList[TBaudRate(GetOrdValue)];
end;
procedure TBaudRateProperty.SetValue(const Value: string);
var
i: TBaudRate;
begin
for i := Low(TBaudRate) to High(TBaudRate) do
if BaudList[i] = Value then
begin
SetOrdValue(integer(i));
EXIT;
end;
inherited SetValue(Value);
end;
|
Перечислить диски.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Adds all fixed drives into Combobox1.
To enumerate another type of drive,
i.e all CD-ROMs just change the DRIVE_FIXED constant to DRIVE_CDROM.
Fugt all fixen Laufwerke in Combobox1 ein.
Um z.B alle CD-Rom Laufwerke zu ermitteln,
einfach DRIVE_CDROM anstatt die Konstante DRIVE_FIXED nehmen.
}
procedure List_Drives;
const
DRIVE_UNKNOWN = 0;
DRIVE_NO_ROOT_DIR = 1;
DRIVE_REMOVABLE = 2;
DRIVE_FIXED = 3;
DRIVE_REMOTE = 4;
DRIVE_CDROM = 5;
DRIVE_RAMDISK = 6;
var
r: LongWord;
Drives: array[0..128] of char;
pDrive: PChar;
begin
r := GetLogicalDriveStrings(SizeOf(Drives), Drives);
if r = 0 then Exit;
if r > SizeOf(Drives) then
raise Exception.Create(SysErrorMessage(ERROR_OUTOFMEMORY));
pDrive := Drives;
while pDrive^ <> #0 do
begin
if GetDriveType(pDrive) = DRIVE_FIXED then
Form1.ComboBox1.Items.Add(pDrive);
Inc(pDrive, 4);
end;
end;
Использовать EnumFontFamilies.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Example for the use of EnumFontFamilies. This example requires two
listboxes on a form.
Beispiel fur die Verwendung vo EnumFontFamilies. Das Beispiel benotigt
2 Listboxen auf der Form.
}
procedure TForm1.FormCreate(Sender: TObject);
begin
Listbox1.Items.Assign(Screen.Fonts);
end;
function EnumProc(var elf: TEnumLogFont;
var ntm: TNewTextmetric;
FontType: Integer;
listbox: TListbox): Integer; stdcall;
var
S: string;
begin
if fonttype = TRUETYPE_FONTTYPE then
begin
listbox.Items.Add(Format('Name: %s', [elf.elfFullName]));
listbox.Items.Add(Format('Style: %s', [elf.elfStyle]));
end
else
listbox.Items.Add(Format('Name: %s', [elf.elfLogfont.lfFacename]));
listbox.Items.Add(Format('Size: %d', [elf.elfLogFont.lfHeight]));
listbox.Items.Add(Format('Weight: %d', [elf.elfLogFont.lfWeight]));
if elf.elfLogFont.lfItalic <> 0 then
listbox.Items.Add('This font is italic');
case FontType of
DEVICE_FONTTYPE: S := 'device font';
RASTER_FONTTYPE: S := 'raster font';
TRUETYPE_FONTTYPE: S := 'truetype font'
else
S := 'unknown font type';
end;
listbox.Items.Add(Format('This is a %s', [S]));
Result := 1;
end;
procedure TForm1.ListBox1Click(Sender: TObject);
begin
listbox2.Clear;
with listbox1 do
if ItemIndex >= 0 then
EnumFontFamilies(Self.Canvas.Handle, PChar(Items[ItemIndex]),
@EnumProc, Longint(Listbox2));
end;
Перечислить формы и дочерние формы.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
type
PWindows = ^TWindows;
TWindows = record
WindowHandle: HWND;
WindowText: string;
end;
type
TForm1 = class(TForm)
Button1: TButton;
TreeView1: TTreeView;
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
PNode, CNode: TTreeNode;
AWindows: PWindows;
implementation
{$R *.DFM}
function EnumChildWindowsProc(Wnd: HWnd; Form: TForm1): Bool; export;
{$ifdef Win32} stdcall; {$endif}
var
Buffer: array[0..99] of Char;
begin
GetWindowText(Wnd, Buffer, 100);
//if StrLen(Buffer) 0 then
if StrPas(Buffer) = '' then Buffer := 'Empty';
new(AWindows);
with AWindows^ do
begin
WindowHandle := Wnd;
WindowText := StrPas(Buffer);
end;
CNode := Form1.TreeView1.Items.AddChildObject(PNode,
AWindows^.WindowText + ':' +
IntToHex(AWindows^.WindowHandle, 8), AWindows);
if GetWindow(Wnd, GW_CHILD) 0 then
begin
PNode := CNode;
Enumchildwindows(Wnd, @EnumChildWindowsProc, 0);
end;
Result := True;
end;
function EnumWindowsProc(Wnd: HWnd; Form: TForm1): Bool;
export; {$ifdef Win32} stdcall; {$endif}
var
Buffer: array[0..99] of Char;
begin
GetWindowText(Wnd, Buffer, 100);
//if StrLen(Buffer) 0 then
if StrPas(Buffer) = '' then Buffer := 'Empty';
new(AWindows);
with AWindows^ do
begin
WindowHandle := Wnd;
WindowText := StrPas(Buffer);
end;
PNode := Form1.TreeView1.Items.AddObject(nil, AWindows^.WindowText + ':' +
IntToHex(AWindows^.WindowHandle, 8), AWindows);
EnumChildWindows(Wnd, @EnumChildWindowsProc, 0);
Result := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
EnumWindows(@EnumWindowsProc, Longint(Self));
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Dispose(AWindows);
end;
end.
{**********************************************}
{ Other Code by NicoDE
{**********************************************}
type
PMyEnumParam = ^TMyEnumParam;
TMyEnumParam = record
Nodes: TTreeNodes;
Current: TTreeNode;
end;
function EnumWindowsProc(Wnd: HWND; Param: PMyEnumParam): BOOL; stdcall;
const
MyMaxName = 64;
MyMaxText = 64;
var
ParamChild: TMyEnumParam;
ClassName: string;
WindowText: string;
begin
Result := True;
SetLength(ClassName, MyMaxName);
SetLength(ClassName, GetClassName(Wnd, PChar(ClassName), MyMaxName));
SetLength(WindowText, MyMaxText);
SetLength(WindowText, SendMessage(Wnd, WM_GETTEXT, MyMaxText, lParam(PChar(WindowText))));
ParamChild.Nodes := Param.Nodes;
ParamChild.Current := Param.Nodes.AddChildObject(Param.Current,
'[' + ClassName + '] "' + WindowText + '"' + ' Handle: ' + IntToStr(Wnd), Pointer(Wnd));
EnumChildWindows(Wnd, @EnumWindowsProc, lParam(@ParamChild));
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Param: TMyEnumParam;
begin
Param.Nodes := TreeView1.Items;
Param.Current := TreeView1.TopItem;
TreeView1.Items.BeginUpdate;
EnumWindows(@EnumWindowsProc, lParam(@Param));
TreeView1.Items.EndUpdate;
end;
Получить текст элемента перечисляемого типа.
// For example, if you have some enum type
// Als Beispiel, wenn dieser Aufzahlungstyp vorhanden ist
{....}
type
TYourEnumType = (One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten);
{....}
{
And you want in run-time to get a string with same value for each of
them (for example, fill the Listbox items with enum values), then you
can use the next procedure:
}
uses TypInfo;
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
for i := Ord(Low(TYourEnumType)) to Ord(High(TYourEnumType)) do
ListBox1.Items.Add(GetEnumName(TypeInfo(TYourEnumType), i));
end;
|
Перечислить сетевые соединения.
| Речь сисадмина к девушке, пришедшей регистрироваться на сервере: - С точки зрения сетевой безопасности вы представляете собой потенциальную дырку. |
{
From the MS-DOS prompt, you can enumerate the network
connections (drives) by using the following command:
net use
Programmatically, you would call WNetOpenEnum() to start
the enumeration of connected resources and
WNetEnumResources() to continue the enumeration.
The following sample code enumerates the network connections:
}
procedure TForm1.Button1Click(Sender: TObject);
var
i, dwResult: DWORD;
hEnum: THandle;
lpnrDrv: PNETRESOURCE;
s: string;
const
cbBuffer: DWORD = 16384;
cEntries: DWORD = $FFFFFFFF;
begin
dwResult := WNetOpenEnum(RESOURCE_CONNECTED,
RESOURCETYPE_ANY,
0,
nil,
hEnum);
if (dwResult <> NO_ERROR) then
begin
ShowMessage('Cannot enumerate network drives.');
Exit;
end;
s := '';
repeat
lpnrDrv := PNETRESOURCE(GlobalAlloc(GPTR, cbBuffer));
dwResult := WNetEnumResource(hEnum, cEntries, lpnrDrv, cbBuffer);
if (dwResult = NO_ERROR) then
begin
s := 'Network drives:'#13#10;
for i := 0 to cEntries - 1 do
begin
if lpnrDrv^.lpLocalName <> nil then
s := s + lpnrDrv^.lpLocalName + #9 + lpnrDrv^.lpRemoteName;
Inc(lpnrDrv);
end;
end
else if dwResult <> ERROR_NO_MORE_ITEMS then
begin
s := s + 'Cannot complete network drive enumeration';
GlobalFree(HGLOBAL(lpnrDrv));
break;
end;
GlobalFree(HGLOBAL(lpnrDrv));
until (dwResult = ERROR_NO_MORE_ITEMS);
WNetCloseEnum(hEnum);
if s = '' then s := 'No network connections.';
ShowMessage(s);
end;
{***********************************************************************
FindComp Unit from
Fatih Olcer
fatiholcer@altavista.com
***********************************************************************}
unit FindComp;
interface
uses
Windows, Classes;
function FindComputers: DWORD;
var
Computers: TStringList;
implementation
uses
SysUtils;
const
MaxEntries = 250;
function FindComputers: DWORD;
var
EnumWorkGroupHandle, EnumComputerHandle: THandle;
EnumError: DWORD;
Network: TNetResource;
WorkGroupEntries, ComputerEntries: DWORD;
EnumWorkGroupBuffer, EnumComputerBuffer: array[1..MaxEntries] of TNetResource;
EnumBufferLength: DWORD;
I, J: DWORD;
begin
Computers.Clear;
FillChar(Network, SizeOf(Network), 0);
with Network do
begin
dwScope := RESOURCE_GLOBALNET;
dwType := RESOURCETYPE_ANY;
dwUsage := RESOURCEUSAGE_CONTAINER;
end;
EnumError := WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0,
@Network, EnumWorkGroupHandle);
if EnumError = NO_ERROR then
begin
WorkGroupEntries := MaxEntries;
EnumBufferLength := SizeOf(EnumWorkGroupBuffer);
EnumError := WNetEnumResource(EnumWorkGroupHandle, WorkGroupEntries,
@EnumWorkGroupBuffer, EnumBufferLength);
if EnumError = NO_ERROR then
begin
for I := 1 to WorkGroupEntries do
begin
EnumError := WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0,
@EnumWorkGroupBuffer[I], EnumComputerHandle);
if EnumError = NO_ERROR then
begin
ComputerEntries := MaxEntries;
EnumBufferLength := SizeOf(EnumComputerBuffer);
EnumError := WNetEnumResource(EnumComputerHandle, ComputerEntries,
@EnumComputerBuffer, EnumBufferLength);
if EnumError = NO_ERROR then
for J := 1 to ComputerEntries do
Computers.Add(Copy(EnumComputerBuffer[J].lpRemoteName,
3, Length(EnumComputerBuffer[J].lpRemoteName) - 2));
WNetCloseEnum(EnumComputerHandle);
end;
end;
end;
WNetCloseEnum(EnumWorkGroupHandle);
end;
if EnumError = ERROR_NO_MORE_ITEMS then
EnumError := NO_ERROR;
Result := EnumError;
end;
initialization
Computers := TStringList.Create;
finalization
Computers.Free;
end.
|
Перечислить процессы и завершить их.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
With the following routines it ist simply easy to kill a running process.
First build a form with a TListview with 3 columns and a TButton
to refresh the running processes.
Attach the Refreshclick-procedure to the TButton and the
ListViewDblClick-procedure with the TListview
The TListview shows the processes.
With a Doubleclick on one of the processnames you can kill this running process.
Don't forget to include TLHelp32 into your uses-clause!
Mit der nachfolgend aufgefuhrten Routinen konnen Sie die in einer
Windowssitzung laufenden Prozesse aufzeigen und bei Bedarf auch
entfernen. Hierfur benotigen Sie ein Formobject, ein ListViewobject und zu-
mindest ein ButtonObject. Verknupfen Sie das Buttonobject mit dem BtnRefreshClick
damit gleich beim Start des Programms alle Prozesse angezeigt werden.
Zum loschen eines Prozesses mussen Sie eine Verknupfung zwischen DblClick
des Listviewobject mit der Procedure ListviewDblClick.
Wie aus den beigefugten Routinen ersichtlich, kann auch ein einzelner Prozess
gesucht und terminiert werden. Die hierzu erforderlichen Schritte konnen aus
der Refreshroutine entnommen werden.
Wichtig ist die Einbindung der Unit TlHelp32 !
}
interface
uses
{...,}TLHelp32 {important !}
// Global Variables, Globale Variablen
var
aSnapshotHandle: THandle;
aProcessEntry32: TProcessEntry32;
implementation
procedure TForm1.BtnRefreshClick(Sender: TObject);
var
i: Integer;
bContinue: BOOL;
NewItem: TListItem;
begin
ListView1.Items.Clear;
aSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
aProcessEntry32.dwSize := SizeOf(aProcessEntry32);
bContinue := Process32First(aSnapshotHandle, aProcessEntry32);
while Integer(bContinue) <> 0 do
begin
NewItem := ListView1.Items.Add;
NewItem.Caption := ExtractFileName(aProcessEntry32.szExeFile);
NewItem.subItems.Add(IntToHex(aProcessEntry32.th32ProcessID, 4));
NewItem.subItems.Add(aProcessEntry32.szExeFile);
bContinue := Process32Next(aSnapshotHandle, aProcessEntry32);
end;
CloseHandle(aSnapshotHandle);
end;
procedure TForm1.ListView1DblClick(Sender: TObject);
var
Ret: BOOL;
PrID: Integer; // processidentifier
Ph: THandle; // processhandle
begin
with ListView1 do
begin
if MessageDlg('Do you want to Terminate "' + ItemFocused.Caption + '"?' + ^J +
'It''s possible the system becames instable or out of' + ^J +
'control......',
mtConfirmation, [mbYes, mbNo], 0) = mrYes then
begin
PrID := StrToInt('$' + ItemFocused.SubItems[0]);
Ph := OpenProcess(1, BOOL(0), PrID);
Ret := TerminateProcess(Ph, 0);
if Integer(Ret) = 0 then
MessageDlg('Cannot terminate "' + ItemFocused.Caption + '"',
mtInformation, [mbOK], 0)
else
ItemFocused.Delete;
end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
{
ListView1.Columns.Add;
ListView1.Columns.Add;
ListView1.Columns.Add;
ListView1.ViewStyle := vsReport;
}
BtnRefresh.Click;
end;
{Only Win95/Win98/ME}
Как сделать текстовый список всех доступных свойств перечисляемого типа.
Автор: Sen WEB-сайт: http://www.lmc-mediaagentur.de I need to get a list of strings (like a StringList) with the possible values for a TBrushStyle property (bsSolid, bsClear, bsHorizontal, for example). I want to build a ComboBox with this options. How can I set the property Items of my ComboBox directly with all the values from the enumerated type TBrushStyle? My ComboBox will be alike the Property Editor for this type.You can use runtime type information (RTTI) to do that. Below is an example:
uses
{...}, TypInfo
procedure BrushStylesAsStrings(AList: TStrings);
var
a: integer;
pInfo: PTypeInfo;
pEnum: PTypeData;
begin
AList.Clear;
pInfo := PTypeInfo(TypeInfo(TBrushStyle));
pEnum := GetTypeData(pInfo);
with pEnum^ do
begin
for a := MinValue to MaxValue do
AList.Add(GetEnumName(pInfo, a));
end;
end;
Перечислить ключи реестра.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
This example demonstrates how to enumerate all registry keys from
a certain key (here: HKEY_CURRENT_USER)
}
{
Dieses Beispiel listet alle Eintrage eines bestimmten Schlussels
in der Registrierung auf. (hier: HKEY_CURRENT_USER)
}
uses
Registry;
procedure TForm1.Button1Click(Sender: TObject);
var
indent: Integer;
procedure EnumAllKeys(hkey: THandle);
var
l: TStringList;
n: Integer;
begin
Inc(indent, 2);
with TRegistry.Create do
try
RootKey := hkey;
OpenKey(EmptyStr, False);
l := TStringList.Create;
try
GetKeynames(l);
CloseKey;
for n := 0 to l.Count - 1 do
begin
memo1.Lines.Add(StringOfChar(' ', indent) + l[n]);
if OpenKey(l[n], False) then
begin
EnumAllKeys(CurrentKey);
CloseKey;
end;
end;
finally
l.Free
end;
finally
Free;
end;
Dec(indent, 2);
end;
begin
Memo1.Clear;
Memo1.Lines.Add('Keys under HKEY_CURRENT_USER');
indent := 0;
Memo1.Lines.BeginUpdate;
try
EnumAllKEys(HKEY_CURRENT_USER);
finally
Memo1.Lines.EndUpdate;
end;
end;
Перевести значения перечисляемого типа в строки.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
You obtain a string equivalent of an enumerated type value using the
GetEnumName function:
}
function GetEnumName(TypeInfo: PTypeInfo; Value: Integer): string;
{
The first argument is a pointer to the enumerated type’s RTTI information,
and the second argument is the ordinal position of the value within the enumerated type.
GetEnumName returns a string representing the corresponding enumerated type value.
You get the ordinal position of an enumerated type value based on a string using the
GetEnumValue function:
}
function GetEnumValue(TypeInfo: PTypeInfo; const Name: string): Integer;
{
Like GetEnumName, the first argument is a pointer to the RTTI information.
The second argument is a string that represents the enumerated type value.
This function returns the ordinal position of the corresponding value.
}
// Example:
// Declare a custom type:
type
TOurType = (otFirst, otSecond, otThird, otForth, otFifth, otLast);
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
TypInfo;
procedure TForm1.Button1Click(Sender: TObject);
var
OT: TOurType;
FT: TFontStyle;
begin
// Enumerate TFontStyle values
// Zeigt alle TFontStyle Werte an
Memo1.Lines.Add('The TFontStyle values:');
for FT := Low(TFontStyle) to High(TFontStyle) do
Memo1.Lines.Add(GetEnumName(TypeInfo(TFontStyle), Ord(FT)));
// Enumerate our custom TOurType values
// Werte vom Benutzerdefiniertern Typ TOurType auflisten.
Memo1.Lines.Add('The TOurType values:');
for OT := Low(TOurType) to High(TOurType) do
Memo1.Lines.Add(GetEnumName(TypeInfo(TOurType), Ord(OT)));
end;
Вывести все зарегистрированные библиотеки типов.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
Registry;
procedure EnumTypeLibs(TypeLibNames: TStrings);
var
f: TRegistry;
keyNames, keyVersions, keyInfos: TStringList;
keyName, keyVersion, keyInfo, tlName: string;
i, j, k: Integer;
begin
TypeLibNames.Clear;
keyNames := nil;
keyVersions := nil;
keyInfos := nil;
f := TRegistry.Create;
try
keyNames := TStringList.Create;
keyVersions := TStringList.Create;
keyInfos := TStringList.Create;
f.RootKey := HKEY_CLASSES_ROOT;
if not f.OpenKey('TypeLib', False) then raise
Exception.Create('TRegistry.Open');
f.GetKeyNames(keyNames);
f.CloseKey;
for i := 0 to keyNames.Count - 1 do
begin
keyName := keyNames.Strings[i];
if not f.OpenKey(Format('TypeLib\%s', [keyName]), False) then Continue;
f.GetKeyNames(keyVersions);
f.CloseKey;
for j := 0 to keyVersions.Count - 1 do
begin
keyVersion := keyVersions.Strings[j];
if not f.OpenKey(Format('TypeLib\%s\%s', [keyName, keyVersion]), False) then
Continue;
tlName := f.ReadString('');
f.GetKeyNames(keyInfos);
f.CloseKey;
{$B-}
for k := 0 to keyInfos.Count - 1 do
begin
keyInfo := keyInfos.Strings[k];
if (keyInfo = '') or (keyInfo[1] < '0') or (keyInfo[1] > '9') then Continue;
if not f.OpenKey(Format('TypeLib\%s\%s\%s\win32', [keyName, keyVersion,
keyInfo]), False) then Continue;
f.CloseKey;
TypeLibNames.Add(Format('%s ver.%s', [tlName, keyVersion]));
end;
{$B+}
end;
end;
finally
f.Free;
keyNames.Free;
keyVersions.Free;
keyInfos.Free;
end;
end;
// Example Call:
procedure TForm1.Button1Click(Sender: TObject);
begin
EnumTypeLibs(ListBox1.Items);
end;
Получить заголовки и названия классов форм активных процессов.
Автор: Xavier Pacheco
{
Copyright © 1999 by Delphi 5 Developer's Guide - Xavier Pacheco and Steve Teixeira
}
unit MainFrm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ComCtrls;
type
{ Define a record/class to hold the window name and class name for
each window. Instances of this class will get added to ListBox1 }
TWindowInfo = class
WindowName, // The window name
WindowClass: string; // The window's class name
end;
TMainForm = class(TForm)
lbWinInfo: TListBox;
btnGetWinInfo: TButton;
hdWinInfo: THeaderControl;
procedure btnGetWinInfoClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure lbWinInfoDrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure hdWinInfoSectionResize(HeaderControl: THeaderControl;
Section: THeaderSection);
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
function EnumWindowsProc(Hw: HWnd; AMainForm: TMainForm): Boolean; stdcall;
{ This procedure is called by the User32.DLL library as it enumerates
through windows active in the system. }
var
WinName, CName: array[0..144] of char;
WindowInfo: TWindowInfo;
begin
{ Return true by default which indicates not to stop enumerating
through the windows }
Result := True;
GetWindowText(Hw, WinName, 144); // Obtain the current window text
GetClassName(Hw, CName, 144); // Obtain the class name of the window
{ Create a TWindowInfo instance and set its fields with the values of
the window name and window class name. Then add this object to
ListBox1's Objects array. These values will be displayed later by
the listbox }
WindowInfo := TWindowInfo.Create;
with WindowInfo do
begin
SetLength(WindowName, strlen(WinName));
SetLength(WindowClass, StrLen(CName));
WindowName := StrPas(WinName);
WindowClass := StrPas(CName);
end;
MainForm.lbWinInfo.Items.AddObject('', WindowInfo); // Add to Objects array
end;
procedure TMainForm.btnGetWinInfoClick(Sender: TObject);
begin
{ Enumerate through all top-level windows being displayed. Pass in the
call back function EnumWindowsProc which will be called for each
window }
EnumWindows(@EnumWindowsProc, 0);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
var
i: integer;
begin
{ Free all instances of TWindowInfo }
for i := 0 to lbWinInfo.Items.Count - 1 do
TWindowInfo(lbWinInfo.Items.Objects[i]).Free
end;
procedure TMainForm.lbWinInfoDrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
begin
{ First, clear the rectangle to which drawing will be performed }
lbWinInfo.Canvas.FillRect(Rect);
{ Now draw the strings of the TWindowInfo record stored at the
Index'th position of the listbox. The sections of HeaderControl1
will give positions to which to draw each string }
with TWindowInfo(lbWinInfo.Items.Objects[Index]) do
begin
DrawText(lbWinInfo.Canvas.Handle, PChar(WindowName),
Length(WindowName), Rect, dt_Left or dt_VCenter);
{ Shift the drawing rectangle over by using the size
HeaderControl1's sections to determine where to draw the next
string }
Rect.Left := Rect.Left + hdWinInfo.Sections[0].Width;
DrawText(lbWinInfo.Canvas.Handle, PChar(WindowClass),
Length(WindowClass), Rect, dt_Left or dt_VCenter);
end;
end;
procedure TMainForm.hdWinInfoSectionResize(HeaderControl:
THeaderControl; Section: THeaderSection);
begin
lbWinInfo.Invalidate; // Force ListBox1 to redraw itself.
end;
end.
Как узнать переменные окружения.
| - Говорят, что русским было очень сложно опустить "Мир" обратно на землю... - Да чего уж там. Самой сложной частью всей операции была инсталляция Windows 95 на бортовые компьютеры "Мира". А упал он сам... |
procedure TForm1.Button5Click(Sender: TObject);
var
p: pChar;
begin
Memo1.Lines.Clear;
Memo1.WordWrap := false;
{$IFDEF WIN32}
p := GetEnvironmentStrings;
{$ELSE}
p := GetDOSEnvironment;
{$ENDIF}
while p^ <> #0 do begin
Memo1.Lines.Add(StrPas(p));
inc(p, lStrLen(p) + 1);
end;
{$IFDEF WIN32}
FreeEnvironmentStrings(p);
{$ENDIF}
end;
|
Получение переменных среды.

| Интересно, у владельцев компьютера, выражение "мать его..." это ругательное? |
procedure GetEnvironmentStrings(ss:TStrings);
{Переменные среды}
var
ptr: PChar;
s: string;
Done: boolean;
begin
ss.Clear;
s:='';
Done:=FALSE;
ptr:=windows.GetEnvironmentStrings;
while Done=false do begin
if ptr^=#0 then begin
inc(ptr);
if ptr^=#0 then Done:=TRUE
else ss.Add(s);
s:=ptr^;
end else s:=s+ptr^;
inc(ptr);
end;
end;
|
Активизировать клавишу возврата для TWebBrowser.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Problem:
Web forms that have multi-line text boxes and/or Submit buttons do not
respond to the Enter key when displayed on a TWebbrowser.
Also when browsing local folders, some keys don't respond.
How to solve it:
}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, OleCtrls, SHDocVw_TLB, ActiveX, StdCtrls;
type
TForm1 = class(TForm)
WebBrowser1: TWebBrowser;
Button1: TButton;
Button2: TButton;
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
FOleInPlaceActiveObject: IOleInPlaceActiveObject;
procedure MsgHandler(var Msg: TMsg; var Handled: Boolean);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormDestroy(Sender: TObject);
begin
FOleInPlaceActiveObject := nil;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnMessage := MsgHandler;
end;
procedure TForm1.MsgHandler(var Msg: TMsg; var Handled: Boolean);
const
DialogKeys: set of Byte = [VK_LEFT, VK_RIGHT, VK_BACK, VK_UP, VK_DOWN,
$30..$39, $41..42, $44..$55, $57, $59..$5A];
var
iOIPAO: IOleInPlaceActiveObject;
Dispatch: IDispatch;
begin
{ exit if we don't get back a webbrowser object }
if (WebBrowser1 = nil) then
begin
Handled := System.False;
Exit;
end;
Handled := (IsDialogMessage(WebBrowser1.Handle, Msg) = System.True);
if (Handled) and (not WebBrowser1.Busy) then
begin
if FOleInPlaceActiveObject = nil then
begin
Dispatch := WebBrowser1.Application;
if Dispatch <> nil then
begin
Dispatch.QueryInterface(IOleInPlaceActiveObject, iOIPAO);
if iOIPAO <> nil then
FOleInPlaceActiveObject := iOIPAO;
end;
end;
if FOleInPlaceActiveObject <> nil then
if ((Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP)) and
(Msg.wParam in DialogKeys) then
// nothing - do not pass on the DialogKeys
else
FOleInPlaceActiveObject.TranslateAccelerator(Msg);
end;
end;
initialization
OleInitialize(nil);
finalization
OleUninitialize;
Трансляция ошибок.
| Автор: Nomadic |
| - Исправил ли ты ошибку в программе? - В разумных пределах... |
Используй нормальную трансляцию ошибок в Application.OnException. Вроде это.
procedure DBExceptionTranslate(E: EDBEngineError); |
function OriginalMessage: string;
var
I: Integer;
DBErr: TDBError;
S: string;
begin
Result := '';
for I := 0 to E.ErrorCount - 1 do
begin
DBErr := E.Errors[I];
case DBErr.NativeError of
-836: { Intebase exception }
begin
S := DBErr.Message;
Result := #13#10 + Copy(S, Pos(#10, S) + 1, Length(S));
Exit;
end;
end;
S := Trim(DBErr.Message);
if S <> '' then
Result := Result + #13#10 + S;
end;
end;
begin
case E.Errors[0].ErrorCode of
$2204:
E.Message := LoadStr(SKeyDeleted);
$271E, $2734:
E.Message := LoadStr(SInvalidUserName);
$2815:
E.Message := LoadStr(SDeadlock);
$2601:
E.Message := LoadStr(SKeyViol);
$2604:
E.Message := LoadStr(SFKViolation) + OriginalMessage;
else
begin
E.Message := Format(LoadStr(SErrorCodeFmt), [E.Errors[0].ErrorCode]) +
OriginalMessage;
end;
end;
end;
|
Некорректность реализации свойства BorderWidth у ProgressBar.

Если есть желание, то некорректность можно исправить в ComCtrls.pas, переопределив BorderWidth.
TProgressBar = class(TWinControl)
private
FBorderWidth: TBorderWidth;
procedure SetBorderWidth(Value: TBorderWidth);
published
property BorderWidth: TBorderWidth read FBorderWidth write SetBorderWidth;
...
constructor TProgressBar.Create(AOwner: TComponent);
begin
FBorderWidth := inherited BorderWidth;
end;
procedure TProgressBar.SetBorderWidth(Value: TBorderWidth);
begin
if Value > (Height div 2) - 3 then
Exit
if Value <> inherited BorderWidth then
begin
inherited BorderWidth := Value;
FBorderWidth := inherited BorderWidth;
end;
end;
|
Аналогичный подход можно использовать в других компонентах. В процедуре SetBorderWidth вместо Exit можно создать исключение, но это уже на любителя.
Ошибка создания дескриптора курсора.
| Билл Гейтс, когда стал самым богатым человеком в мире, подумал: "На все воля Божья. Я никогда бы не разбогател, если бы Он этого не хотел. Надо как-то отблагодарить." Билли построил огромный храм, зажег в нем миллион свечей, вошел и молится: - Господи, спасибо Тебе за все. Не сочти за дерзость, Господи, но я хочу выразить Тебе свою признательность и приглашаю Тебя на игру в гольф в это воскресенье. Увидел Иисус это фейерверк, услышал молитву, понял, что уважают, и решил прийти. Играю они, значит, в гольф. Иисус бьет по мячу, мяч летит и зависает прямо над центром лунки. Иисус бьет по второму - тот летит и зависает прямо над центром лунки. Иисус бьет по третьему - тоже самое. Он оборачивается к Гейтсу и говорит: - Билли, а другого бета-тестера для своих глючных мячей ты подыскать не мог? |
Вы должны использовать ExecSql вместо Open. К примеру, если имя вашего запроса UpdateStudent, то при необходимости обновления STUDENT.DB вы должны использовать следующий код:
Begin ..... UpdateStudent.ExecSql; ..... End; |
Ваш запрос является Passtrough-запросом, который не может возвратить установленный результат, так что это не может быть открыто, а должно быть 'ВЫПОЛНЕНО'.
Получение констант с определением ошибки функцией LoadStr.
| В семье программиста несчастье. Жена прибегает в слезах: - У меня случился выкидыш! Программист задумчиво чешет репу: - Мда-а... Баг в бета-версии... |
Возбуждение исключения и передача строковой константы ошибки из CONSTS.PAS (как это делалось в Delphi 1 и Delphi 2) в Delphi3 невозможна. Например:
raise SomeException.Create(LoadStr(SInsertLineError)); |
В Delphi3 теперь не работает. Я предлагаю использовать выражение.
raise SomeException.Create(
{$IFNDEF VER100}LoadStr{$ENDIF}
(SInsertLineError));
|
Однако в Delphi 4 данная оплошность исправлена и вроде как документирована. Почитайте в хелпе на тему "resourcestrings", посмотрите сырцы и все сразу станет понятно и просто.
Номера ошибок.
| Странное какое-то у немцев отношение к компьютерам: на их языке слово "Gluck" означает счастье. |
| 1 | Неверный номер функции (Invalid function number) |
| 2 | Файл не найден (File not found) |
| 3 | Путь не найден (Path not found) |
| 4 | Слишком много открытых файлов (Too many open files) |
| 5 | Доступ к файлу закрыт (File access denied) |
| 6 | Неверный дескриптор файла (Invalid file handle) |
| 12 | Неверный код доступа к файлу (Invalid file access code) |
| 15 | Неверный номер устройства (Invalid drive number) |
| 16 | Невозможно удалить текущий каталог (Cannot remove current directory) |
| 17 | Невозможно переименовать устройство (Cannot rename across drives) |
| 100 | Ошибка чтения диска (Disk read error) |
| 101 | Ошибка записи на диск (Disk write error) |
| 102 | Файл не назначен (File not assigned) |
| 103 | Файл не открыт (File not open) |
| 104 | Файл не открыт для ввода (File not open for input) |
| 105 | Файл не открыт для вывода (File not open for output) |
| 106 | Неверный числовой формат (Invalid numeric format) |
| 200 | Деление на ноль (Division by zero) |
| 201 | Превышение допустимого диапазона (Range check error) |
| 202 | Переполнение стека (Stack overflow error) |
| 203 | Переполнение кучи (Heap overflow error) |
| 204 | Неверная операция с указателем (Invalid pointer operation) |
| 205 | Переполнение плавающей точки (Floating point overflow) |
| 206 | Потеря значимости плавающей точкой (Floating point underflow) |
| 207 | Неверная операция с плавающей точкой (Invalid floating point operation) |
| 210 | Объект не инициализирован (Object not initialized) |
| 211 | Вызов абстрактного метода (Call to abstract method) |
| 212 | Ошибка регистрации потока (Stream registration error) |
| 213 | Индекс коллекции вышел за пределы допустимого диапазона (Collection index out of range) |
| 214 | Ошибка переполнения коллекции (Collection overflow error) |
| 215 | Ошибка арифметического переполнения (Arithmetic overflow error) |
| 216 | Общая ошибка защиты (General protection fault) |
Error reading symbol file.
Время от времени при открытии проекта возникает сообщение: "Error reading symbol file". ТИПОВЫЕ РЕШЕНИЯУдалить файлы *.sym. Они могут остаться от другой версии Delphi, при том что формат файла с символьной информацией изменился.
Искусство управления ошибками.
Оформил: DeeCo
Автор: Даутов Ильдар.
Часть IIПродолжая тему "Управление ошибками в Delphi", поставим следующие задачи : программа-монитор ошибок должна работать как системный сервис Windows NT журнал ошибок должен сохраняться на диске и постоянно пополняться список текущих ошибок и полный журнал ошибок должны быть доступны для просмотра на любом компьютере локальной сети предприятия Реализуем следующую схему взаимодействия программ при возникновении ошибки : ошибка, возникшая в клиентской программе, передается по сети монитору-сервису Windows NT. Для передачи используем механизм каналов Mailslot монитор сохраняет текст ошибки на диске. Для хранения используем текстовый файл монитор пересылает по сети текст ошибки программе просмотра ошибок. Для передачи используем механизм каналов Mailslot программа просмотра принимает текст ошибки и отображает его на экране программа просмотра может запросить полный журнал ошибок. Для получения полного журнала используем механизм разделяемых сетевых файловых ресурсов В статье представлены 2 проекта : монитор ошибок и окно просмотра ошибок. Клиентская программа, имитирующая ошибку, была представлена в предыдущей статье, и здесь не рассматривается. Монитор ошибок Оформить программу как сервис Windows NT (Win32 service) не составляет большого труда : создаем новое приложение File | New... | New | Service Application. Создается приложение с глобальной переменной Application типа TServiceApplication и объектом типа TService, который и реализует всю функциональность сервиса устанавливаем требуемые свойства объекта TService имя сервиса параметры запуска сервиса имя и пароль пользователя, от имени которого стартует сервис переписываем событие OnExecute объекта TService, в котором реализуем требуемую функциональность сервиса компилируем проект регистрируем созданный сервис на сервере Windows NT и запускаем Регистрация сервиса выполняется из командной строки следующим образом : ErrorMonitorService.exe /install Удаление сервиса : ErrorMonitorService.exe /uninstall Запуск сервиса выполняется из командной строки следующим образом : net start ErrorMonitor Останов сервиса : net stop ErrorMonitor Оформив эту последовательность команд как BAT-файл, можно значительно облегчить себе жизнь при отладке сервиса. Достаточно подробную информацию о сервисах Windows NT можно найти в книге : А.В.Фролов, Г.В.Фролов 'Программирование для Windows NT (часть вторая)', Москва, ДИАЛОГ-МИФИ, 1997 Для сохранения протокола (журнала) пользовательских ошибок используем следующую схему : журнал ведется в текстовом файле в определенном каталоге Windows NT журнал имеет имя yyyy-mm-dd.log, соответствующее календарной дате запуска сервера при каждом запуске монитор проверяет наличие файла, имя которого соответствует текущей дате. При отсутствии - файл создается, иначе происходит дозапись в конец файла сохраняются только последние 7 файлов журнала Текст программы монитора ошибок приведен ниже :
unit uErrorMonitorService;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, ScktComp;
type
TErrorMonitor = class(TService)
procedure Service1Execute(Sender: TService);
procedure ServiceEMCreate(Sender: TObject);
private
public
function GetServiceController: PServiceController; override;
procedure SendError;
function InitLog: boolean;
end;
var
ErrorMonitor: TErrorMonitor;
implementation
uses Dialogs;
{$R *.DFM}
const
LogDir = 'C:\Log\'; // каталог, где сохраняются журналы
var
LogFile: TextFile; // файл текущего журнала
LogName: string; // имя файла текущего журнала
h: THandle; // handle канала Mailslot
str: string[250]; // буфер для передачи информации
MsgNumber, MsgNext, Read: DWORD;
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
ErrorMonitor.Controller(CtrlCode);
end;
function TErrorMonitor.GetServiceController: PServiceController;
begin
Result := @ServiceController;
end;
// Передача текста ошибки от сервиса программе просмотра
procedure TErrorMonitor.SendError;
var
h: THandle;
i: integer;
begin
// открытие MailSlot-канала, по которому будет передаваться протокол
// используется широковещательная передача в домене
h := CreateFile(PChar('\\*\mailslot\EMonMess'), GENERIC_WRITE,
FILE_SHARE_READ, nil,
OPEN_EXISTING, 0, 0);
if h <> INVALID_HANDLE_VALUE then
begin
// запись в канал и закрытие канала
WriteFile(h, str, Length(str) + 1, DWORD(i), nil);
CloseHandle(h);
end;
end;
// инициализация файла журнала
// журналы ведутся в отдельных файлах по каждой дате
function TErrorMonitor.InitLog: boolean;
var
sr: TSearchRec;
i: integer;
begin
Result := True;
// удаление старых файлов журнала
//(сохраняются только последние 7 журналов)
with TStringList.Create do
begin
Sorted := True;
i := FindFirst(LogDir + '*.log', faAnyFile, sr);
while i = 0 do
begin
Add(sr.Name);
i := FindNext(sr);
end;
FindClose(sr);
if Count > 7 then
for i := 0 to Count - 8 do
DeleteFile(LogDir + Strings[i]);
Free;
end;
// текущий файл журнала
LogName := LogDir + FormatDateTime('yyyy-mm-dd', Date) + '.log';
AssignFile(LogFile, LogName);
try
if FileExists(LogName) then
Append(LogFile)
else
Rewrite(LogFile);
except
str := 'Ошибка создания файла журнала : ' + LogName;
Status := csStopped;
LogMessage(str);
ShowMessage(str);
Result := False;
end;
end;
// основная логика сервиса
procedure TErrorMonitor.Service1Execute(Sender: TService);
begin
// создание MailSlot-канала с именем EMon - по этому имени к нему
// будут обращаться клиенты, у которых возникли ошибки
h := CreateMailSlot('\\.\mailslot\EMon', 0, MAILSLOT_WAIT_FOREVER, nil);
if h = INVALID_HANDLE_VALUE then
begin
Status := csStopped;
// запись в журнал событий NT
str := 'Ошибка создания канала EMon !';
LogMessage(str);
ShowMessage(str);
Exit;
end;
// создание файла журнала
if not InitLog then
Exit;
try
while not Terminated do
begin
// определение наличия сообщения в канале
if not GetMailSlotInfo(h, nil, DWORD(MsgNext), @MsgNumber, nil) then
begin
Status := csStopped;
str := 'Ошибка сбора информации канала EMon !';
LogMessage(str);
ShowMessage(str);
Break;
end;
if MsgNext <> MAILSLOT_NO_MESSAGE then
begin
beep;
// чтение сообщения из канала и добавление в текст протокола
if ReadFile(h, str, 200, DWORD(Read), nil) then
begin
// запись в журнал
Writeln(LogFile, str);
// посылка сообщения для показа
SendError;
end
else
begin
str := 'Ошибка чтения сообщения !';
Writeln(LogFile, str);
SendError;
end;
Flush(LogFile);
end;
sleep(500);
ServiceThread.ProcessRequests(False);
end;
finally
CloseHandle(h);
CloseFile(LogFile);
end;
end;
procedure TErrorMonitor.ServiceEMCreate(Sender: TObject);
begin
// под таким именем наш сервис будет виден в Service Control Manager
DisplayName := 'ErrorMonitor';
// необходимо при использовании ShowMessage
InterActive := True;
end;
end.
Окно просмотра ошибок
Текст программы окна просмотра ошибок приведен ниже :
unit fErrorMonitorMessage;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, ScktComp;
type
TfmErrorMonitorMessage = class(TForm)
// протокол текущих ошибок
meErrorTextNow: TMemo;
meJournals: TMemo;
// таймер для опроса канала
Timer: TTimer;
paJournals: TPanel;
buJournals: TButton;
lbJournals: TListBox;
laJournals: TLabel;
procedure FormCreate(Sender: TObject);
procedure TimerTimer(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure buJournalsClick(Sender: TObject);
private
public
end;
// сетевой разделяемый ресурс, где сохраняются журналы
// (укажите здесь имя своего ресурса и обеспечьте права для доступа)
const
LogDir = '\\MyServer\C$\Log\';
var
fmErrorMonitorMessage: TfmErrorMonitorMessage;
h: THandle; // handle Mailslot-канала
str: string[250]; // буфер обмена
MsgNumber, MsgNext, Read: DWORD;
implementation
{$R *.DFM}
procedure TfmErrorMonitorMessage.FormCreate(Sender: TObject);
var
sr: TSearchRec;
i: integer;
begin
// создание Mailslot-канала с именем EMonMess
// по этому каналу будем получать сообщения об ошибках от сервиса NT
h := CreateMailSlot('\\.\mailslot\EMonMess', 0, MAILSLOT_WAIT_FOREVER, nil);
if h = INVALID_HANDLE_VALUE then
begin
ShowMessage('Ошибка создания канала !');
Halt;
end;
// интервал опроса канала Mailslot - 3 секунды
Timer.Interval := 3000;
// таймер первоначально был выключен
Timer.Enabled := True;
// заполнение списка доступных журналов
i := FindFirst(LogDir + '*.log', faAnyFile, sr);
while i = 0 do
begin
lbJournals.Items.Add(sr.Name);
i := FindNext(sr);
end;
lbJournals.ItemIndex := lbJournals.Items.Count - 1;
FindClose(sr);
end;
procedure TfmErrorMonitorMessage.TimerTimer(Sender: TObject);
var
str: string[250];
begin
Timer.Enabled := False;
// определение наличия сообщения в канале
if not GetMailSlotInfo(h, nil, DWORD(MsgNext), @MsgNumber, nil) then
begin
ShowMessage('Ошибка сбора информации !');
Close;
end;
if MsgNext <> MAILSLOT_NO_MESSAGE then
begin
beep;
// чтение сообщения из канала и добавление в текст протокола
if ReadFile(h, str, 200, DWORD(Read), nil) then
meErrorTextNow.Lines.Add(str)
else
ShowMessage('Ошибка чтения сообщения !');
end;
Timer.Enabled := True;
end;
procedure TfmErrorMonitorMessage.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
CloseHandle(h);
end;
procedure TfmErrorMonitorMessage.buJournalsClick(Sender: TObject);
var
Journal: TFileStream;
s: string;
begin
// получение журнала ошибок за дату
meJournals.Lines.Clear;
meJournals.Lines.Add('Файл журнала ' +
lbJournals.Items[lbJournals.ItemIndex]);
Journal := TFileStream.Create(LogDir + lbJournals.Items[lbJournals.ItemIndex],
fmOpenRead or fmShareDenyNone);
SetLength(s, Journal.Size);
Journal.Read(PChar(s)^, Journal.Size);
meJournals.Lines.Add(s);
Journal.Free;
end;
end.
Искусство управления ошибками. Часть 2.
Оформил: DeeCo
Автор: Даутов Ильдар.
Введение.
Любая программа содержит ошибки и мастерство разработчика не только в минимизации числа этих ошибок, но и в умении управлять ими, если таковые возникли. Посмотрим, что же в плане управления программными ошибками предлагают такие системы как Delphi и Windows 95/Windows NT. Поставим задачу следующим образом : все ошибки в программах собственной разработки, независимо от места их возникновения на клиентских ПК предприятия, должны собираться и регистрироваться в одном единственном месте, а разработчик должен получать сообщение об этом событии, содержащее полный текст ошибки. Предложим следующую схему управления ошибками. На каком-либо ПК запускается программа-монитор и ждет сообщений об ошибках от клиентских программ , при приходе которых, регистрирует их. Клиентские программы при возникновении ошибки перехватывают ее, предварительно обрабатывают и отсылают монитору по локальной вычислительной сети. Решение задачи разобьем на 2 этапа - разработка сервера-монитора слежения за ошибками и подпрограммы-ловушки ошибок в прикладной клиентской программе. При разработке систем подобного класса неизбежно встает вопрос о протоколе межмашинного взаимодействия клиента и сервера. Из множества вариантов, предлагаемых Windows, мы остановим свой выбор на каналах передачи данных Mailslot, как наиболее простом, доступном и универсальном. Немного о Mailslot : каналы Mailslot позволяют выполнять одностороннюю передачу данных от одного или нескольких клиентов к одному или нескольким серверам, причем работают на уровне датаграмм и в широковещательном режиме.Монитор слежения за ошибками.
Монитор слежения за ошибками - это отдельное приложение, запускаемое либо на вашем ПК, либо на сервере (как Вам удобно). Его предназначение - ловить сообщения об ошибках, приходящие от клиентских программ и отображать текст ошибки в окне программы. Монитор должен запускаться до запуска любой клиентской программы.
Текст монитора приведен ниже :
unit fErrorMonitor;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TfmErrorMonitor = class(TForm)
// компонент для протоколирования ошибок
meErrorText: TMemo;
// компонент для активизации опроса канала
// интервал опроса устанавливается свойством Interval
Timer: TTimer;
procedure FormCreate(Sender: TObject);
procedure TimerTimer(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
public
end;
var
fmErrorMonitor: TfmErrorMonitor;
var
h: THandle;
str: string[250];
MsgNumber, MsgNext, Read: DWORD;
implementation
{$R *.DFM}
procedure TfmErrorMonitor.FormCreate(Sender: TObject);
begin
// создание канала с именем EMon - по этому имени к нему
// будут обращаться клиенты
h := CreateMailSlot('\\.\mailslot\EMon', 0, MAILSLOT_WAIT_FOREVER, nil);
if h = INVALID_HANDLE_VALUE then
begin
ShowMessage('Ошибка создания канала !');
Halt;
end;
// таймер первоначально был выключен
Timer.Enabled := True;
end;
procedure TfmErrorMonitor.TimerTimer(Sender: TObject);
begin
Timer.Enabled := False;
// определение наличия сообщения в канале
if not GetMailSlotInfo(h, nil, DWORD(MsgNext), @MsgNumber, nil) then
begin
ShowMessage('Ошибка сбора информации !');
Close;
end;
if MsgNext <> MAILSLOT_NO_MESSAGE then
begin
beep;
// чтение сообщения из канала и добавление в текст протокола
if ReadFile(h, str, 200, DWORD(Read), nil) then
meErrorText.Lines.Add(str)
else
ShowMessage('Ошибка чтения сообщения !');
end;
Timer.Enabled := True;
end;
procedure TfmErrorMonitor.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
// закрытие канала
CloseHandle(h);
end;
end.
Подпрограмма ловушки ошибок Подпрограмма ловушки ошибок встраивается в каждую прикладную программу. Ниже приведен примерный шаблон такой ловушки и пример ее использования. Вы вправе настроить шаблон под особенности своей предметной области.
unit fErrorClient;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TfmTest = class(TForm)
Button1: TButton;
Image1: TImage;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
public
end;
implementation
{$R *.DFM}
type
Error = class
class procedure ErrorCatch(Sender: TObject; Exc: Exception);
end;
// для простоты ловушка ошибок описана в этом модуле
// в реальных приложениях ее нужно вынести в отдельный unit
class procedure Error.ErrorCatch(Sender: TObject; Exc: Exception);
var
strMess: string[250];
UserName: array[0..99] of char;
h: THandle;
i: integer;
begin
// здесь можно проанализировать Exception, воспользовавшись его свойствами,
// и предпринять конкретные действия в зависимости от типа ошибки
beep;
// получение имени пользователя
i := SizeOf(UserName);
GetUserName(UserName, DWORD(i));
// формирование текста сообщения об ошибке
strMess := '/' + UserName + '/' + FormatDateTime('hh:mm', Time) + '/' +
Exc.Message;
// открытие канала : MyServer - имя сервера, на котором работает
// монитор ошибок (\\.\\mailslot\EMon - монитор работает на этом же ПК)
// EMon - имя канала
h := CreateFile(PChar('\\MyServer\mailslot\EMon'), GENERIC_WRITE,
FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
if h <> INVALID_HANDLE_VALUE then
begin
// передача текста ошибки (запись в канал и закрытие канала)
WriteFile(h, strMess, Length(strMess) + 1, DWORD(i), nil);
CloseHandle(h);
end;
// вывод сообщения об ошибке пользователю
ShowMessage('У Вас возникла ошибка (не волнуйтесь-все под контролем)' +
chr(13) + strMess);
end;
procedure TfmTest.FormCreate(Sender: TObject);
begin
// при создании главной формы приложения устанавливаем
// глобальный обработчик исключений
Application.OnException := Error.ErrorCatch;
end;
procedure TfmTest.Button1Click(Sender: TObject);
var
i: integer;
begin
// тестирование ловушки ошибок
i := 1 - 1;
i := 100 div i;
end;
end.
В следующей статье будет показано как превратить монитор ошибок в сервис Windows NT.
Получить исходные тексты (13 Кб)
Escape код PASSTHROUGH.
| Мой папа хакер и мама хакер, сестричка тоже хакер хоть куда. А кто не хакер? Ну кто не хакер? А тот кто кода и не видел никогда. |
(Postscript - зарегистрированная торговая марка Adobe Systems Incorporated).
unit Esc1;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{ добавляем модуль printers }
uses
Printers;
{$R *.DFM}
{ описываем структуру "PASSTHROUGH" }
type
TPrnBuffRec = record
BuffLength: word;
Buffer: array[0..255] of char;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Buff: TPrnBuffRec;
TestInt: integer;
s: string;
begin
{ Тестируем на предмет поддержки escape кода "PASSTHROUGH" }
TestInt := PASSTHROUGH;
if Escape(Printer.Handle,
QUERYESCSUPPORT,
sizeof(TestInt),
@TestInt,
nil) > 0 then
begin
{ Начинаем вывод на печать }
Printer.BeginDoc;
{ Создаем строку для транзитной пересылки }
s := ' Текстовая строка ';
{ Копируем строчку в буфер }
StrPCopy(Buff.Buffer, s);
{ Устанавливаем размер буфера }
Buff.BuffLength := StrLen(Buff.Buffer);
{ Даем команду на транзитную пересылку буфера }
Escape(Printer.Canvas.Handle,
PASSTHROUGH,
0,
@Buff,
nil);
{ Заканчиваем вывод на печать }
Printer.EndDoc;
end;
end;
end.
|
Возможность отмены вставки нового узла в TTreeView по нажатию кнопки Esc.
unit BetterTreeView;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, CommCtrl;
type
TTVNewEditCancelEvent = procedure( Sender: TObject; Node: TTreeNode; var Delete: Boolean) of object;
TBetterTreeView = class(TTreeView)
protected
FIsEditingNew: Boolean;
FOnEditCancel: TTVChangedEvent;
FOnNewEditCancel: TTVNewEditCancelEvent;
procedure Edit(const Item: TTVItem); override;
public
function NewChildAndEdit(Node: TTreeNode; const S: string): TTreeNode;
published
property IsEditingNew: Boolean read FIsEditingNew;
property OnEditCancel: TTVChangedEvent read FOnEditCancel write FOnEditCancel;
property OnNewEditCancel: TTVNewEditCancelEvent read FOnNewEditCancel write FOnNewEditCancel;
end;
implementation
procedure TBetterTreeView.Edit(const Item: TTVItem);
var
Node: TTreeNode;
Action: Boolean;
begin
with Item do
begin
{ Get the node }
if (state and TVIF_PARAM) <> 0 then
Node := Pointer(lParam)
else
Node := Items.GetNode(hItem);
if pszText = nil then
begin
if FIsEditingNew then
begin
Action := True;
if Assigned(FOnNewEditCancel) then
FOnNewEditCancel(Self, Node, Action);
if Action then
Node.Destroy
end
else
if Assigned(FOnEditCancel) then
FOnEditCancel(Self, Node);
end
else
inherited;
end;
FIsEditingNew := False;
end;
function TBetterTreeView.NewChildAndEdit(Node: TTreeNode; const S: string): TTreeNode;
begin
SetFocus;
Result := Items.AddChild(Node, S);
FIsEditingNew := True;
Node.Expand(False);
Result.EditText;
SetFocus;
end;
end.
|
Делегирование события.
Автор: Steve ...я вижу что событие делегировано, но почему вы считаете, что это отход от ООП? Тем не менее, Delphi это использует при каждом определении обработчика события. Вот как поступить в вашей ситуации: Определите тип процедуры, использующейся в качестве обработчика события. Допустим, ваш обработчик OnCalculate имеет один параметр типа Integer (присутствующий лишь для демонстрации идеи).
type TCalculateEvent = procedure(I: Integer) of object; |
type TSomeClass = class(TObject) private FOnCalculate: TCalculateEvent; procedure DoCalculate(I: Integer); public property OnCalculate: TCalculateEvent read FOnCalculate write FOnCalculate; end; |
procedure TSomeClass.DoCalculate(I: Integer);
begin
if Assigned(FOnCalculate) then
FOnCalculate(I);
end;
|
Теперь вы можете присваивать значение объекту TSomeClass, и назначать любую процедуру событию OnCalculate (естественно, при условии, что ее объявление соответствует объявлению TCalculateEvent).
Использовать в качестве обработчика сообщения обычную процедуру, а не метод объекта.
Автор: Alexey MahotkinУ этой процедуры должен быть еще один дополнительный параметр, например:
procedure MyEventHandler(PseudoSelf: TObject; Sender: TObject);
begin
{ ... }
end;
Синтаксис ссылки на событие.
Автор: Kurt Я хочу проверить значение события, чтобы убедиться, что оно указывает в настоящий момент на определенную функцию.По всей видимости вам придется использовать две временные переменные. Попытайтесь сделать следующее:
var ClickMethod: TNotifyEvent; B1Click: TNotifyEvent; ... ClickMethod := Button1.OnClick; B1Click := Button1Click; if @ClickMethod = @B1Click then messagebeep(0); |
Генерация еженедельных списков задач.
Автор: Mike Orriss Мне необходима программа, которая генерировала бы еженедельные списки задач. Программа должна просто показывать количество недель в списке задач и организовывать мероприятия, не совпадающие по времени. В моем текущем планировщике у меня имеется 12 групп и планы на 11 недель. Мне нужен простой алгоритм, чтобы решить эту проблему. Какие идеи?Вот рабочий код (но вы должны просто понять алгоритм работы):
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
const
maxTeams = 100;
var
Teams: array[1..maxTeams] of integer;
nTeams, ix, week, savix: integer;
function WriteBox(week: integer): string;
var
str: string;
ix: integer;
begin
Result := Format('Неделя=%d ', [week]);
for ix := 1 to nTeams do
begin
if odd(ix) then
Result := Result + ' '
else
Result := Result + 'v';
Result := Result + IntToStr(Teams[ix]);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
nTeams := StrToInt(Edit1.Text);
if Odd(nTeams) then
inc(nTeams); {должны иметь номера каждой группы}
ListBox1.Clear;
for ix := 1 to nTeams do
Teams[ix] := ix;
ListBox1.Items.Add(WriteBox(1));
for week := 2 to nTeams - 1 do
begin
Teams[1] := Teams[nTeams - 1];
{используем Teams[1] в качестве временного хранилища}
for ix := nTeams downto 2 do
if not Odd(ix) then
begin
savix := Teams[ix];
Teams[ix] := Teams[1];
Teams[1] := savix;
end;
for ix := 3 to nTeams - 1 do
if Odd(ix) then
begin
savix := Teams[ix];
Teams[ix] := Teams[1];
Teams[1] := savix;
end;
Teams[1] := 1; {восстанавливаем известное значение}
ListBox1.Items.Add(WriteBox(week));
end;
end;
end.
|
Высокоточный таймер.

Автор: John Mertus
{
Имя файла: HRTimer.PAS V1.00
Создан: Апр 17 1997, 06:40, автор John Mertus
Обновлен #6: Окт 12 1997, 10:56 John Mertus
Оболочка для таймера высокой точности при создании приложений под
Win95/WinNT
Var
HRT : THRTimer
HRT := THRTimer.Create;
HRT.StartTimer; Сброс таймера в ноль
HRT.ReadTimer; Возвращает отсчитанное время в миллисекундах
начиная со времени старта
HRT.Free;
Список изменений
Версия 1.00 - Первый выпуск
}
{-----------------Модуль HRTimer-------------------John Mertus Апрель 97---}
unit HRTimer;
{-------------------Объявления-------------------------------}
interface
uses Windows;
type
THRTimer = class(TObject)
constructor Create;
function StartTimer: Boolean;
function ReadTimer: Double;
private
StartTime: Double;
ClockRate: Double;
public
Exists: Boolean;
end;
{--------------------------Реализация-----------------------------------}
implementation
{------------------Create-------------------------John Mertus----Мар 97-}
constructor THRTimer.Create;
{ Получаем точное системное время и сохраняем его для дальнейшего }
{ использования. }
{ }
{***********************************************************************}
var
QW: TLargeInteger;
begin
inherited Create;
Exists := QueryPerformanceFrequency(QW);
ClockRate := QW.QuadPart;
end;
{------------------StartTimer---------------------John Mertus----Мар 97-}
function THRTimer.StartTimer: Boolean;
{ Получаем точное системное время и сохраняем его для дальнейшего }
{ использования. }
{ }
{***********************************************************************}
var
QW: TLargeInteger;
begin
Result := QueryPerformanceCounter(QW);
StartTime := QW.QuadPart;
end;
{-------------------ReadTimer---------------------John Mertus----Мар 97---}
function THRTimer.ReadTimer: Double;
{ Получаем точное системное время и сохраняем его для дальнейшего }
{ использования. }
{ }
{***********************************************************************}
var
ET: TLargeInteger;
begin
QueryPerformanceCounter(ET);
Result := 1000.0 * (ET.QuadPart - StartTime) / ClockRate;
end;
end.
|
Высокоточный таймер 2.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
You want to measure the elapsed time in a repeat, while or a for -Loop
to do some optimation ?
Sie mochten eine Routine mit einer repeat, while oder for i :=...hinsichtlich der
durchlaufenen Zeit messen, um z.B.Optimierungsaktivitaten durchzufuhren.
}
// Global Variables, Globale Variablen:
t1, t2, Res: TLargeInteger;
bOk: BOOL;
implementation
uses Windows, dialogs;
procedure StartTimer;
begin
bOK := QueryPerformanceFrequency(Res);
if bOK then
QueryPerformanceCounter(t1);
end;
procedure StopTimer;
begin
if bOK then
QueryPerformanceCounter(t2);
end;
procedure LoopRoutine;
var
iCounter: integer;
begin
StartTimer;
// Put here some kind of loop: For, While or Repeat
for i := 0 to iCounter do...
{ WHILE....
repeat...until}
{ After the Loop/
Nach Abschluss der Schleifenroutine:}
if bOK then
QueryPerformanceCounter(t2);
end;
procedure StartSchleife;
begin
LoopRoutine;
if bOK then
{$IFDEF VER80}
// Show the elapsed time:
MessageDlg('Der Schleifendurchlauf dauerte: ' + Format('%g Sekunden.',
[(t2.QuadPart - t1.Quadpart) / Res.QuadPart]));
$Else {$IFDEF VER120}
MessageDlg('Der Schleifendurchlauf dauerte: ' + Format('%g Sekunden.',
[(t2 - t1) / Res]));
{$ENDIF}
end;
Как отчитывать промежутки времени с точностью.
Автор: Nomadic.
Для начала описываешь процедуру, которая будет вызываться по сообщению от таймера :
procedure FNTimeCallBack(uTimerID, uMessage: UINT; dwUser, dw1, dw2: DWORD); stdcall; begin // Тело процедуры. end; |
А дальше в программе (например по нажатию кнопки) создаешь Таймер и вешаешь на него созданную процедуру.
uTimerID:=timeSetEvent(10,500,@FNTimeCallBack,100,TIME_PERIODIC); |
Подробности смотри в Help. Ну и в конце убиваешь таймер.
timeKillEvent(uTimerID); |
Обратите внимание на то, что все CALLBACK-функции, вызываемые Windows, должны использовать соглашение о вызовах stdcall.
Как работать с буфером обмена (Clipboard).
| Собираются программисты встречать новый год. Уже разлили пиво (по бокалам - а вы что подумали?), и тут один говорит: - Я хочу произнести тост. Давайте выпьем за моего злейшего врага. И пусть в новом году у него денег будет в десять раз больше, чем у меня! И, обведя взглядом недоуменные лица окружающих, добавляет: - За Билла Гейтса! |
Этот пример использует картинку, кнопку и компонент shape на форме. Когда пользователь кликает по кнопке, то изображение формы сохраняется в в переменной FormImage и копируется в буфер обмена (Clipboard). Затем изображение формы копируется обратно в компонент картинки, тем самым создавая интересный эффект, особенно, если кнопку понажимать несколько раз.
procedure TForm1.Button1Click(Sender: TObject);
var
FormImage: TBitmap;
begin
FormImage := GetFormImage;
try
Clipboard.Assign(FormImage);
Image1.Picture.Assign(Clipboard);
finally
FormImage.Free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Shape1.Shape := stEllipse;
Shape1.Brush.Color := clLime;
Image1.Stretch := True;
end;
|
Следующий пример копирует содержимое экрана в буфер обмена:
procedure CopyScreenToClipboard;
var
dx,dy: integer;
hSourcDC, hDestDC, hBM, hbmOld: THandle;
begin
dx := screen.width;
dy := screen.height;
hSourcDC := CreateDC('DISPLAY', nil, nil, nil);
hDestDC := CreateCompatibleDC(hSourcDC);
hBM := CreateCompatibleBitmap(hSourcDC, dx, dy);
hbmold:= SelectObject(hDestDC, hBM);
BitBlt(hDestDC, 0, 0, dx, dy, hSourcDC, 0, 0, SRCCopy);
OpenClipBoard(form1.handle);
EmptyClipBoard;
SetClipBoardData(CF_Bitmap, hBM);
CloseClipBoard;
SelectObject(hDestDC,hbmold);
DeleteObject(hbm);
DeleteDC(hDestDC);
DeleteDC(hSourcDC);
end;
|
Запуск и закрытие Excel, добавление и удаление книг или листов.
Автор: Lookin WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Запуск и закрытие Excel, добавление и удаление книг и листов
На данный момент работает:
- вызов и закрытие Excel
- добавление новых, открытие ранее созданных и удаление рабочих книг
- добавление и удаление листов в рабочие книги
Зависимости: ComObj, SysUtils,Dialogs,Controls;
Автор: lookin, lookin@mail.ru, Екатеринбург
Copyright: lookin
Дата: 04 мая 2002 г.
***************************************************** }
unit MSExcel;
interface
uses ComObj, SysUtils, Dialogs, Controls;
procedure CallExcel(Show: boolean);
procedure CloseExcel;
procedure AddWorkBook(WorkBookName: Ansistring);
procedure OpenWorkBook(WorkBookName: Ansistring);
procedure CloseWorkBook(WorkBookName: Ansistring);
procedure ActivateWorkBook(WorkBookName: Ansistring);
procedure ActivateWorkSheet(WorkBookName, WorkSheetName: Ansistring);
function WorkBookIndex(WorkBookName: Ansistring): integer;
function WorkSheetIndex(WorkBookName, WorkSheetName: Ansistring): integer;
procedure CheckExtension(Name: Ansistring);
procedure AddWorkSheet(WorkBookName, WorkSheetName: Ansistring);
procedure DeleteWorkSheet(WorkBookName, WorkSheetName: Ansistring);
var
Excel: Variant;
implementation
procedure CallExcel(Show: boolean);
begin
if VarIsEmpty(Excel) = true then
begin
Excel := CreateOleObject('Excel.Application');
if Show then
Excel.Visible := true;
end;
end;
procedure CloseExcel;
begin
if VarIsEmpty(Excel) = false then
begin
Excel.Quit;
Excel := 0;
end;
end;
procedure AddWorkBook(WorkBookName: Ansistring);
var
k: integer;
begin
CheckExtension(WorkBookName);
if VarIsEmpty(Excel) = true then
begin
Excel := CreateOleObject('Excel.Application');
Excel.Visible := true;
end;
k := WorkBookIndex(WorkBookName);
if k = 0 then
begin
Excel.Workbooks.Add;
Excel.ActiveWorkbook.SaveCopyAs(FileName := WorkBookName);
Excel.ActiveWorkbook.Close;
Excel.Workbooks.Open(WorkBookName);
end
else
MessageDlg('Книга с таким именем уже существует.', mtWarning, [mbOk], 0);
end;
procedure OpenWorkBook(WorkBookName: Ansistring);
var
k: integer;
begin
CheckExtension(WorkBookName);
if VarIsEmpty(Excel) = true then
begin
Excel := CreateOleObject('Excel.Application');
Excel.Visible := true;
end;
k := WorkBookIndex(WorkBookName);
if k = 0 then
Excel.Workbooks.Open(WorkBookName)
else
MessageDlg('Книга с таким именем уже открыта.', mtWarning, [mbOk], 0);
end;
procedure CloseWorkBook(WorkBookName: Ansistring);
var
k: integer;
begin
if VarIsEmpty(Excel) = false then
begin
k := WorkBookIndex(WorkBookName);
if k <> 0 then
Excel.ActiveWorkbook.Close(WorkBookName)
else
MessageDlg('Книга с таким именем отсутствует.', mtWarning, [mbOk], 0);
end;
end;
procedure ActivateWorkBook(WorkBookName: Ansistring);
var
k: integer;
begin
if VarIsEmpty(Excel) = false then
begin
k := WorkBookIndex(WorkBookName);
if k <> 0 then
Excel.WorkBooks[k].Activate;
end;
end;
procedure ActivateWorkSheet(WorkBookName, WorkSheetName: Ansistring);
var
k, j: integer;
begin
if VarIsEmpty(Excel) = false then
begin
k := WorkBookIndex(WorkBookName);
j := WorkSheetIndex(WorkBookName, WorkSheetName);
if j <> 0 then
Excel.WorkBooks[k].Sheets[j].Activate;
end;
end;
procedure AddWorkSheet(WorkBookName, WorkSheetName: Ansistring);
var
k, j: integer;
begin
if VarIsEmpty(Excel) = false then
begin
k := WorkBookIndex(WorkBookName);
if k <> 0 then
begin
Excel.DisplayAlerts := False;
Excel.Workbooks[k].Sheets.Add;
j := WorkSheetIndex(WorkBookName, WorkSheetName);
if j = 0 then
Excel.Workbooks[k].ActiveSheet.Name := WorkSheetName;
end;
end;
end;
procedure DeleteWorkSheet(WorkBookName, WorkSheetName: Ansistring);
var
k, j: integer;
begin
if VarIsEmpty(Excel) = false then
begin
k := WorkBookIndex(WorkBookName);
Excel.DisplayAlerts := false;
j := WorkSheetIndex(WorkBookName, WorkSheetName);
if j <> 0 then
Excel.Workbooks[k].Sheets[j].Delete
else
MessageDlg('Листа с таким именем в этой книге нет.', mtWarning, [mbOk],
0);
end;
end;
procedure CheckExtension(Name: Ansistring);
var
s: string;
begin
//проверка расширения
s := ExtractFileExt(Name);
if LowerCase(s) <> '.xls' then
if
MessageDlg('Вы задали имя книги с нестандартным расширением. Продолжить?',
mtWarning, [mbYes, mbCancel], 0) = mrCancel then
Abort;
end;
function WorkBookIndex(WorkBookName: Ansistring): integer;
var
i, n: integer;
begin
//проверка на наличие книги с этим именем
n := 0;
if VarIsEmpty(Excel) = false then
for i := 1 to Excel.WorkBooks.Count do
if Excel.WorkBooks[i].FullName = WorkBookName then
begin
n := i;
break;
end;
WorkBookIndex := n;
end;
function WorkSheetIndex(WorkBookName, WorkSheetName: Ansistring): integer;
var
i, k, n: integer;
begin
//проверка на наличие листа с этим именем в книге с этим именем
n := 0;
if VarIsEmpty(Excel) = false then
begin
k := WorkBookIndex(WorkBookName);
for i := 1 to Excel.WorkBooks[k].Sheets.Count do
if Excel.WorkBooks[k].Sheets[i].Name = WorkSheetName then
begin
n := i;
break;
end;
end;
WorkSheetIndex := n;
end;
end.
Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
begin
//вызов Excel, true - если хотите при вызове Excel отобразить окно Excel
CallExcel(true);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
//добавление новой рабочей книги с заданным именем
//ВАЖНО: используйте полное имя рабочей книги, т.е. включая путь
AddWorkBook('D:\qwerty.xls');
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
//добавление листа с именем ff в рабочую книгу D:\qwerty.xls
AddWorksheet('D:\qwerty.xls', 'ff');
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
//активация рабочей книги
ActivateWorkBook('D:\1234.xls');
end;
procedure TForm1.Button5Click(Sender: TObject);
begin
//активация листа в рабочей книге
ActivateWorkSheet('D:\qwerty.xls', 'ff');
end;
procedure TForm1.Button6Click(Sender: TObject);
begin
//открытие рабочей книги
OpenWorkBook('D:\qwerty.xls');
end;
procedure TForm1.Button7Click(Sender: TObject);
begin
//закрытие рабочей книги
CloseWorkBook('D:\qwerty.xls');
end;
procedure TForm1.Button8Click(Sender: TObject);
begin
//удаление листа из рабочей книги
DeleteWorkSheet('D:\qwerty.xls', 'ff');
end;
procedure TForm1.Button9Click(Sender: TObject);
begin
//закрытие Excel
CloseExcel;
end;
end.
Работа с ячейкой листа Microsoft Excel.
Автор: Lookin WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Работа с ячейкой листа Microsoft Excel
Получает и заносит число типа double в ячейку листа Microsoft Excel
Зависимости: ComObj
Автор: lookin, lookin@mail.ru, Екатеринбург
Copyright: lookin
Дата: 30 апреля 2002 г.
***************************************************** }
//ВНИМАНИЕ: ОБЯЗАТЕЛЬНОЕ условие работы - наличие запущенного Excel
//получение double из заданной ячейки первого листа в заданной рабочей книге
function DoubleValueFromExcelCell(ExcelWorkBook, ExcelCell: string): double;
var
i: integer;
Excel, v: Variant;
begin
Excel := GetActiveOleObject('Excel.Application');
for i := 1 to Excel.Application.Workbooks.Count do
if Excel.Application.Workbooks[i].FullName = ExcelWorkBook then
begin
v := Excel.Application.Workbooks[i].Sheets[1].Range[ExcelCell];
DoubleValueFromExcelCell := VarAsType(v, varDouble);
v := 0;
Excel := 0;
end;
end;
//занесение double в заданную ячейку первого листа в заданной рабочей книге
procedure DoubleValueToExcelCell(Value: double; ExcelWorkBook, ExcelCell:
string);
var
i: integer;
Excel, v: Variant;
begin
Excel := GetActiveOleObject('Excel.Application');
for i := 1 to Excel.Application.Workbooks.Count do
if Excel.Application.Workbooks[i].FullName = ExcelWorkBook then
begin
Excel.Application.EditDirectlyInCell := false;
v := Value;
Excel.Application.Workbooks[i].Sheets[1].Range[ExcelCell] := v;
end;
end;
Проход по ячейкам Excel макросом.
| Новости от Microsoft: "... новая операционная система имеет удобный дружественный интерфейс и множество функций. облегчающих жизнь пользователю. Сразу после запуска она форматирует диск, уничтожает содержимое BIOS и выводит из строя процессор, после чего на экране появляется надпись - "Теперь питание компьютера можно отключить". |
Private Sub CommandButton1_Click()
Dim i As Integer
Dim index As Integer
For Each cell_in_loop In Range("A2:A8")
For i = 3 To 21
index = 24 - i
If cell_in_loop.Value < Лист2.Cells(index, 1) Then
If cell_in_loop.Offset(0, 1).Value = "Да" Then
cell_in_loop.Offset(0, 2).Value = Лист2.Cells(index, 3)
Else
cell_in_loop.Offset(0, 2).Value = Лист2.Cells(index, 2)
End If
End If
Next
Next
End Sub
|
Пример объединения ячеек и выравнивания текста в Excel.
| Встречаются двое юзеров. Один говорит: - Я тут свежий антивирус достал, не хочешь себе установить? - Нет, мне это не нужно. - Почему? - Да мой комп так глючит, что на нем ни один вирус не запустится. |
Вызывать так:
MerCen('A4:J4');
|
Исходный код:
var xls: Variant; const xlCenter = -4108; procedure MerCen(ran: string); begin xls.Range[ran].Select; xls.Selection.HorizontalAlignment := xlCenter; xls.Selection.VerticalAlignment := xlCenter; xls.Selection.WrapText:= false; xls.Selection.Orientation := 0; xls.Selection.ShrinkToFit := False; xls.Selection.MergeCells := False; xls.Selection.Merge; end; |
Управление Microsoft Excel.
| Генеральный спонсор апокалипсиса: компания Microsoft. |
uses AciveX; // для Delphi 3 и 4
uses Ole2, OleAuto; // для Delphi 2
procedure TForm1.Button1Click(Sender: TObject);
var
MyExcel: Variant;
begin
MyExcel:=CreateOleObject('Excel.Application');
MyExcel.Visible:=true;
MyExcel.WorkBooks.Add;
MyExcel.Cells[1,1].value:='1';
MyExcel.ActiveWorkbook.SaveAs(PATHNAME);
MyExcel.Quit;
end;
|
Пример создания новой таблицы в Excel 2000.
Автор: http://www.sources.ru
uses
ComObj, ActiveX;
var
Row, Col: integer;
DestRange: OleVariant;
Excel: Variant;
begin
Excel := CreateOleObject('Excel.Application.9');
Excel.Visible := True;
Excel.WorkBooks.Add; //Создать новую таблицу
//Можно помещать текст и значения в диапазон ячеек
//Поместить слово тест в диапазон ячеек
Excel.ActiveSheet.Range['A2', 'B3'].Value := 'Тест';
//Или число
Excel.ActiveSheet.Range['A4', 'B5'].Value := 42;
//А вот так задаётся формула
Excel.ActiveSheet.Range['A10', 'A11'].Formula := '=RAND()';
//Можно задавать номера ячеек и столбцов
Excel.ActiveSheet.Cells.Item[1, 1].Value := 'Первая ячейка';
Row := 1;
Col := 3;
Excel.ActiveSheet.Cells.Item[Row, Col].Value := 'Другая ячейка';
//Можно скопировать данный из одного диапазона ячеек в другой
DestRange := Excel.Range['D6', 'F10'];
Excel.Range['A1', 'C5'].Copy(DestRange);
//Можно задавать параметры шрифта в определённой ячейке
Excel.Range['A2', 'A2'].Font.Size := 20;
Excel.Range['A2', 'A2'].Font.FontStyle := 'Bold';
Excel.Range['A2', 'A2'].Font.Color := clFuchsia;
Excel.Range['A2', 'A2'].Font.Name := 'Arial';
//Можно ещё и так изменить цвет диапазона ячеек
Excel.Range['B2', 'C6'].Interior.Color := RGB(223, 123, 123);
end;
Обмен данными с Excel.
| Самое часто произносимое в мире имя: - Билл Гейтс!? - Нет, его матушка! |
В Delphi 5, для обмена данными между Вашим приложением и Excel можно использовать компонент TExcelApplication, доступный на Servers Page в Component Palette.
На форме находится TStringGrid, заполненный некоторыми данными и две кнопки, с названиями To Excel и From Excel. Так же на форме находится компонент TExcelApplication со свойством Name, содержащим XLApp и свойством ConnectKind, содержащим ckNewInstance.
Когда нам необходимо работать с Excel, то обычно мы открываем ExcelApplication, затем открываем WorkBook и в конце используем WorkSheet.
Итак, несомненный интерес представляет для нас листы (WorkSheets) в книге (WorkBook). Давайте посмотрим как всё это работает.
Посылка данных в Excel
Это можно сделать с помощью следующей процедуры :
procedure TForm1.BitBtnToExcelOnClick(Sender: TObject);
var
WorkBk: _WorkBook; // определяем WorkBook
WorkSheet: _WorkSheet; // определяем WorkSheet
I, J, K, R, C: Integer;
IIndex: OleVariant;
TabGrid: Variant;
begin
if GenericStringGrid.Cells[0,1] <> '' then
begin
IIndex := 1;
R := GenericStringGrid.RowCount;
C := GenericStringGrid.ColCount;
// Создаём массив-матрицу
TabGrid := VarArrayCreate([0,(R - 1),0,(C - 1)],VarOleStr);
I := 0;
// Определяем цикл для заполнения массива-матрицы
repeat
for J := 0 to (C - 1) do
TabGrid[I,J] := GenericStringGrid.Cells[J,I];
Inc(I,1);
until
I > (R - 1);
// Соединяемся с сервером TExcelApplication
XLApp.Connect;
// Добавляем WorkBooks в ExcelApplication
XLApp.WorkBooks.Add(xlWBatWorkSheet,0);
// Выбираем первую WorkBook
WorkBk := XLApp.WorkBooks.Item[IIndex];
// Определяем первый WorkSheet
WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;
// Сопоставляем Delphi массив-матрицу с матрицей в WorkSheet
Worksheet.Range['A1',Worksheet.Cells.Item[R,C]].Value := TabGrid;
// Заполняем свойства WorkSheet
WorkSheet.name := 'Customers';
Worksheet.Columns.Font.Bold := True;
Worksheet.Columns.HorizontalAlignment := xlRight;
WorkSheet.Columns.ColumnWidth := 14;
// Заполняем всю первую колонку
WorkSheet.Range['A' + IntToStr(1),'A' + IntToStr(R)].Font.Color := clBlue;
WorkSheet.Range['A' + IntToStr(1),'A' + IntToStr(R)].HorizontalAlignment := xlHAlignLeft;
WorkSheet.Range['A' + IntToStr(1),'A' + IntToStr(R)].ColumnWidth := 31;
// Показываем Excel
XLApp.Visible[0] := True;
// Разрываем связь с сервером
XLApp.Disconnect;
// Unassign the Delphi Variant Matrix
TabGrid := Unassigned;
end;
end;
|
Получение данных из Excel
Это можно сделать с помощью следующей процедуры.
procedure TForm1.BitBtnFromExcelOnClick(Sender: TObject);
var
WorkBk: _WorkBook;
WorkSheet: _WorkSheet;
K, R, X, Y: Integer;
IIndex: OleVariant;
RangeMatrix: Variant;
NomFich: WideString;
begin
NomFich := 'C:\MyDirectory\NameOfFile.xls';
IIndex := 1;
XLApp.Connect;
// Открываем файл Excel
XLApp.WorkBooks.Open(NomFich, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,EmptyParam,0);
WorkBk := XLApp.WorkBooks.Item[IIndex];
WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;
// Чтобы знать размер листа (WorkSheet), т.е. количество строк и количество
// столбцов, мы активируем его последнюю непустую ячейку
WorkSheet.Cells.SpecialCells(xlCellTypeLastCell,EmptyParam).Activate;
// Получаем значение последней строки
X := XLApp.ActiveCell.Row;
// Получаем значение последней колонки
Y := XLApp.ActiveCell.Column;
// Определяем количество колонок в TStringGrid
GenericStringGrid.ColCount := Y;
// Сопоставляем матрицу WorkSheet с нашей Delphi матрицей
RangeMatrix := XLApp.Range['A1',XLApp.Cells.Item[X,Y]].Value;
// Выходим из Excel и отсоединяемся от сервера
XLApp.Quit;
XLApp.Disconnect;
// Определяем цикл для заполнения TStringGrid
K := 1;
repeat
for R := 1 to Y do
GenericStringGrid.Cells[(R - 1),(K - 1)] := RangeMatrix[K,R];
Inc(K,1);
GenericStringGrid.RowCount := K + 1;
until
K > X;
// Unassign the Delphi Variant Matrix
RangeMatrix := Unassigned;
end;
|
Содержание.
- DirectX для начинающих. Часть вторая.
- DirectX для начинающих. Считывание и запись.
- DirectX (Игровой SDK) 1-2.
- DirectX (Игровой SDK) 3-4.
- DirectX (Игровой SDK) 5-6.
- DirectX (Игровой SDK) 7.
- Direct Show и Delphi.
- Вывести список всех директорий, файлов и дисков.
- TreeView каталогов - как в левой части проводника.
- Итератор для поиска файлов в директории.
- Искать нужный файл в дереве каталогов.
- Отключить редактор системного реестра.
- Сделать кнопку ПУСК неактивной.
- Как отключить комбинацию Alt+Tab.
- Блокировка меню без закрашивания серым.
- Всплывающие подсказки у выключенных кнопок.
- Отключить кнопку закрытия чужого окна.
- Отключить кнопку закрытия чужого окна 2.
- Отключить Ctrl+Alt+Delete.
- Как показать DbGrid в режиме disabled.
- Эмуляция нажатия клавиши даже в неактивном окне.
- Можно ли отключить определённый элемент в TRadioGroup.
- Как отключить Range Checking для участка программы, а затем вновь включить.
- Рассуждения о потоках.
- Защита по ключевой дискете - защита от копирования.
- Определение размера диска и свободного пространства на нём.
- Получить объем диска и его свободного места.
- Как получить снимок экрана.
- Как получить снимок экрана 2.
- Избавиться от мерцаний в DrawCell.
- Как снять выделение в StringGrid.
- Растворение экрана.
- Различить правый и левый Ctrl.
- Различать звуковые CD.
- Заблокировать перемещение формы.
- Создать неактивные пиктограммы для TSpeedButton и TBitBtn во время выполнения.
- Разбиение шестнадцатиричной величины.
- Автоматически закрыть TCommonDialog.
- Задать кнопку по умолчанию для диалога.
- Динамически подключаемые библиотеки.
- DLL и ресурсы.
- Отслеживание из DLL ее присоединение к процессу и отсоединение.
- Пример обратного вызова DLL.
- Пример обратного вызова DLL 2.
- Сегменты данных DLL.
- Разработка DLL в среде Borland Delphi.
- Автоматизация кодирования импорта функций из DLL.
- Использование DLL в качестве Plug-in.
- Можно ли определить полный путь и имя файла запущенной DLL из самой DLL.
- Модуль-заготовка для динамического подключения библиотек.
- DLL - убийственная утилита.
- Получить специфическую для DLL информацию о версии.
- Как создать DLL только с ресурсами.
- Как DLL правильно заполнить строковыми ресурсами, и потом достать их.
- Отладка DLL в Windows XP.
- И снова баннеры - Download Accelerator 5.3.0.0.
- Реализация Drag-and-Drop из внешнего приложения в ваше.
- Описание протокола DNS (Domain Name System).
- Как преобразовать DOC в RTF при помощи OLE.
- Пример шифрования данных.
- Как избежать использования неактуальных указателей.
- Как получить дескриптор окна Windows, содержащего DOS программу.
- Выключение питания ATX корпуса из-под DOS.
- DOS команды.
- При использовании DOS DBF файлов - перекодировка между форматами.
- Получение переменных окружения DOS.
- Получение переменных окружения DOS 2.
- Считывать строки из текстового DOS файла.
- Получить выходные данные DOS приложения.
- Конвертирует строку из кодировки DOS в кодировку Windows и обратно.
- Как сделать имитацию ввода с клавиатуры для программы выполняющейся в DOS-окне.
- Как скачать любой URL используя стандартные настройки сети.
- Закачать файл из Интернета.
- Закачать файл из Интернета 2.
- Закачать файл из Интернета 3.
- Качаем с докачкой.
- Как скачать файл через Proxy.
- Автоматизация создание BackUp-ов проектов.
- Интерфейс присоединения Drag-and-Dock.
- Примеры использования Drag and Drop для различных визуальных компонентов.
- Drag and Drop из TScrollBox.
- Интерфейс переноса Drag-and-Drop.
- Drag and Drop для TListBox на примере двойного списка.
- Drag and Drop из RichEdit.
- Drag and Drop TImage.
- Drag and Drop несколько элементов в TListView.
- Пример Drag and Drop между двумя DBGrid.
- Drag and Drop - как использовать ItemAtPos для получения элемента DirListBox.
- Drag and Drop со списками.
- Как получить список файлов из Проводника.
- Перемещение из DbGrid.
- Drag and Drop c Win95 Explorer.
- Drag and Drop c Win95 Explorer 2.
- Как перетаскивать (Drag and Drop) выделенный текст между компонентами Memo.
- Drag and Drop между двумя компонентами ListBox.
- Перетаскивание элементов управления c рамкой контура.
- Drag and Drop с минимизированным приложением.
- Рисование кривых по заданным точкам.
- Рисование без мерцания.
- Как вывести на Canvas надпись под углом.
- Отрисовка стрелки с заданными параметрами.
- Как нарисовать кривую Безье.
- Отрисовка битового образца.
- Программа рисует на форме календарь на 2002 год.
- Выводим цветной текст на форме под любым углом.
- Как вывести цветную надпись на StatusBare.
- Нарисовать подсвеченный прямоугольник вокруг контрола под мышкой.
- Рисование КРИВЫХ в Delphi.
- Рисование КРИВЫХ в Delphi 2.
- Как вывести текст с красивым обрезанием если не помещается.
- Рисовать неактивный текст.
- Хочу прорисовать границу ячейки в Excel.
- Вывод шрифтов в списке в виде самих шрифтов.
- Как нарисовать фрактал.
- Как нарисовать график функции.
- Как нарисовать график функции 2.
- Рисование графов.
- Вывести полупрозрачный текст.
- Рисование иконки в TImage.
- Как рисовать картинки в пунктах меню (через OwnerDraw).
- Компонент для отрисовки линий.
- Нарисовать линию, не используя функции LineTo.
- Как элемент списка нарисовать своим цветом.
- Рисование квадрата мышкой.
- Как рисовать линии (или ещё что-нибудь) на экране (TDesktopCanvas).
- Можно ли рисовать на рамке формы.
- Рисование на минимизированной иконке.
- Можно ли из Delphi рисовать в любой части экрана или в чужом окне.
- Как рисовать на органе управления, например, на TPanel.
- Вывод текста на родительском элементе управления.
- Как рисовать на экране.
- Как нарисовать что-нибудь на TMemo.
- Рисование прямоугольника на изображении.
- Как отобразить выбранную строку DBGrid различными цветами.
- Как быстро нарисовать тень в заданном регионе.
- Изобразить эффект разбрызгивания.
- Отображение текста с тегами форматирования.
- Ввыводим текст и графику, используя DelphiX.
- Рисование текста в DBGrid.
- Рисование текста в DBGrid 2.
- Вывод текста на канве картинки.
- Как нарисовать Bitmap с прозрачностью.
- Как с помощью функции Textout вывести на канве прозрачный текст.
- Драйвера на Delphi.
- Получить список дисков и сведения о выбранном диске.
- Свой формат для TDateTimePicker.
- Автоматически открыть TDateTimePicker.
- Очистить дату в TDateTimePicker.
- Удалить отображение текущей даты из TDateTimePicker.
- Показывать номера недели в TDateTimePicker.
- Получить или установить позицию пиктограмм рабочего стола.
- Выделить родительский компонент, когда он невидим, в Design Time.
- Сохранить значение TDateTime в реестре.
- Преобразовать TDateTime в Unix TimeStamp.
- Получение координат области Рабочего стола, не скрытой Панелью задач.
- 9 live by DuoMark SETUP.
- Дубликат Paradox или dBase записи.
- Ошибка дублирования идентификатора ресурса.
- Создание новой таблицы на основе структуры другой таблицы.
- Дублируем TTable со всеми индексами.
- Экономия памяти с дублирующими значениями в таблице.
- DirectX для начинающих. Часть пятая.
- Как заставить работать DelphiX и UnDelphiX под Delphi6.
- Динамическое добавление пунктов меню.
- Динамическое добавление пунктов меню 2.
- Динамическое добавление пунктов меню 3.
- Динамически создаваемые псевдонимы.
- Динамическая и статическая загрузка DLL.
- Динамические и виртуальные методы.
- Динамические массивы.
- Динамические массивы 2.
- Динамические массивы 3.
- Динамические массивы 4.
- Динамические массивы 5.
- Решение для динамически создаваемых компонентов.
- Динамическое создание формы.
- Динамическое распределение памяти.
- Динамическое распределение памяти 2.
- Динамическое OLE.
- Динамические PageControl и TabSheet.
- Динамические PageControl и TabSheet 2.
- Как удобнее работать с динамически формируемыми запросами.
- Динамическое использование DLL.
- Удаление и добавление элементов в динамический массив.
- Использование DynArrayFromVariant.
- Динамическая загрузка DLL.
- Динамический список.
- Динамический список 2.
- Динамический список 3.
- Простое использование TParser.
- Расчет расстояния между двумя точками на земной поверхности.
- Как сэкономить память в программах.
- Обработка исключений EDBEngineError.
- Создать редактируемый TListBox.
- Массив Edit-компонентов.
- Матрица на основе TEdit.
- Числовая маска компонента Edit.
- Преобразовать первую букву каждого слова к верхнему регистру в TEdit.
- Ограничение ввода в текстовое поле.
- Проблема потери фокуса для TEdit.
- Исключить звуковой сигнал в поле ввода.
- Как заставить TEdit не пикать при нажатии недопустимых клавиш.
- Элемент управления Edit, реагирующий на событие OnTimer.
- Замена текста в поле TEdit.
- Правое выравнивание текста в компоненте Edit.
- Edit с возможностью автоматического выбора.
- Пасхальные яйца в Delphi и CBuilder.
- Эллипсовидное окно.
- Ещё одна защита ваших shareware программ и как её снять.
- Извлечь Email адреса из DBX файлов Outlook.
- Отправить Email скрытно от пользователя.
- Как отправить письмо скрытно от пользователя по протоколу SMTP.
- Как отправить письмо и указать тему, текст сообщения.
- Вложенные свойства.
- Использовать фильтр объема для изображения.
- Как создать неактивное изображение из обычного (рельеф).
- Удаление содержимого каталога.
- Очистить очередь сообщений клавиатуры и мышки.
- Очистить корзину.
- Очистка таблицы (с проверкой).
- Очистить TImage.
- Как позволить или запретить выдачу звукового сигнала в динамик (Beep).
- Эмуляция нажатия клавиши в активном окне.
- 64-битное кодирование 4.
- Кодирование и раскодирование паролей.
- Зашифровать и расшифровать строку.
- Шифрование исполняемого файла.
- Генерация 96-битного ключа для шифрования с помощью Encrypt.
- Шифрование текстовых полей таблицы Interbase.
- Шифрование текста по введенному паролю.
- Простое симметричное шифрование строк.
- Шифрование и дешифрование текстов по принципу S-Coder со скрытым ключом.
- Алгоритм 128-битного шифрования (TEA).
- Шифрование текста.
- Шифрование текста 2.
- Шифрование строки InString, с возможностью корректного сохранения результата шифрования в TEXT-FILE.
- URL кодирование строки.
- Кодировка полиалфавитным шифром Вигенера - xor кодировка.
- Шифрование Bitmap.
- Шифрование файлов, строк и памяти.
- Шифрование текста по ключевым числам.
- Способ шифрования и дешифрования пароля.
- Определяем номер последнего дня в месяце.
- Увеличить процессорное время, выделяемое программе.
- Как осуществить ввод текста в компоненте Label.
- Использование Enter как Tab в TDBGrid.
- Перейти по Enter на следующее поле в TDBGrid.
- Запрос пароля при инициализации приложения.
- Как подменить нажатия ENTER на нажатия TAB.
- Как подменить нажатия ENTER на нажатия TAB 2.
- Как подменить нажатия ENTER на нажатия TAB 3.
- Как подменить нажатия ENTER на нажатия TAB 4.
- Как подменить нажатия ENTER на нажатия TAB 5.
- Работа клавиши Enter как Tab.
- Получить список шрифтов и их размеров для текущего принтера.
- Пример EnumWindows.
- Использование перечислимых констант.
- Перечислить диски.
- Использовать EnumFontFamilies.
- Перечислить формы и дочерние формы.
- Получить текст элемента перечисляемого типа.
- Перечислить сетевые соединения.
- Перечислить процессы и завершить их.
- Как сделать текстовый список всех доступных свойств перечисляемого типа.
- Перечислить ключи реестра.
- Перевести значения перечисляемого типа в строки.
- Вывести все зарегистрированные библиотеки типов.
- Получить заголовки и названия классов форм активных процессов.
- Как узнать переменные окружения.
- Получение переменных среды.
- Активизировать клавишу возврата для TWebBrowser.
- Трансляция ошибок.
- Некорректность реализации свойства BorderWidth у ProgressBar.
- Ошибка создания дескриптора курсора.
- Получение констант с определением ошибки функцией LoadStr.
- Номера ошибок.
- Error reading symbol file.
- Искусство управления ошибками.
- Искусство управления ошибками. Часть 2.
- Escape код PASSTHROUGH.
- Возможность отмены вставки нового узла в TTreeView по нажатию кнопки Esc.
- Делегирование события.
- Использовать в качестве обработчика сообщения обычную процедуру, а не метод объекта.
- Синтаксис ссылки на событие.
- Генерация еженедельных списков задач.
- Высокоточный таймер.
- Высокоточный таймер 2.
- Как отчитывать промежутки времени с точностью.
- Как работать с буфером обмена (Clipboard).
- Запуск и закрытие Excel, добавление и удаление книг или листов.
- Работа с ячейкой листа Microsoft Excel.
- Проход по ячейкам Excel макросом.
- Пример объединения ячеек и выравнивания текста в Excel.
- Управление Microsoft Excel.
- Пример создания новой таблицы в Excel 2000.
- Обмен данными с Excel.