C WinAPI — это основной набор программных интерфейсов (API) Microsoft, доступных в операционных системах Ранняя версия носила название Win32 API.

Введение

C WinAPI — это интерфейс прикладного программирования, который используется для создания приложений Windows. Для начала работы начинающий пользователь должен загрузить SDK Windows, ранее известный как Platform SDK.

Содержит файлы заголовков, библиотеки, образцы, документацию и инструменты, которые используются для разработки приложений. API для языков программирования C и C ++. Это самый прямой способ создания приложений в операционной системе от компании.

C WinAPI можно разделить на несколько областей:

    базовые услуги;

    безопасность;

  • пользовательский интерфейс;

    мультимедиа;

    оболочка Windows;

    сетевые службы.

Базовые службы обеспечивают доступ к основным ресурсам. К ним относятся функции C WinAPI, файловые системы, устройства, процессы, потоки, реестр и обработка ошибок. Область безопасности предоставляет интерфейсы, объекты и другие элементы программирования для аутентификации, авторизации, криптографии и других связанных с безопасностью задач. Подсистема графики обеспечивает функциональность вывода графического содержимого на мониторы, принтеры и другие устройства вывода. Пользовательский интерфейс обеспечивает функциональность для создания окон и элементов управления.

Компонент мультимедиа предоставляет инструменты для работы с видео, звуковыми и входными устройствами. Функции интерфейса оболочки позволяют приложениям получать доступ к функциям, предоставляемым оболочкой операционной системы. Сетевые службы предоставляют доступ к сетевым возможностям ОС Windows.

Компоненты

При создании WinAPI C следует учитывать базовые возможности, предоставляемые Windows API, которые можно упорядочить в семи категориях. Рассмотрим каждую из них подробнее.

Основные услуги предоставляют доступ к базовым системным ресурсам, доступным в Windows. Примеры: файловая система, периферийные устройства, процессы, доступ к системному реестру и система управления исключениями. Эти функции хранятся в файлах kernel.exe, krnl286.exe или krnl386.exe для 16-разрядных систем и kernel32.dll и advapi32.dll для 32-разрядных систем.

Графический интерфейс обеспечивает доступ к ресурсам для отображения на мониторах, принтерах и другом периферийном оборудовании. Хранится в файле gdi.exe на 16-разрядных системах и gdi32.dll в 32-разрядных системах.

Пользовательский интерфейс отвечает за просмотр и управление основными элементами, такими как кнопки и полосы прокрутки, получение информации о клавиатуре и мыши, а также связанные с ними функции. Эти функции хранятся в файле user.exe в 16-разрядных системах и user32.dll comctl32.dll в 32-разрядных системах. Начиная с версии XP элементы управления были сгруппированы в comctl32.dll.

Общие диалоги — отображают данные для открытия и сохранения файлов, выбора цвета и шрифта. Находятся в файле comdlg.dll на 16-разрядных системах и comdlg32.dll в 32-разрядных системах.

Windows Shell — компонент WinAPI, который позволяет приложениям получать доступ к функциям, предоставляемым оболочкой операционной системы.

Сетевые службы обеспечивает доступ к различным сетевым возможностям операционной системы. Его подкомпоненты включают NetBIOS, Winsock, RPC. В старых версиях — NetDDE.

Версии

Win16, Win32 и Win32s являются стандартными наборами компонентов, которые позволяют прикладному программному обеспечению использовать функции различных операционных систем семейства Windows.

Win32, преемник Win16, был представлен в 1993 году в 32-разрядных продуктах семейства Windows, таких как Windows NT, 2000, 95. Этот программный интерфейс реализован тремя программными библиотеками: Kernel32.dll, User32.dll и GDI32.dll2. Те же функции Win32 доступны во всех продуктах Windows, и, в зависимости от продукта, использование определенных функций может привести к ошибке обслуживания.

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

Спецификация

C WinAPI — это абстрактная спецификация интерфейса программирования для операционной системы Windows. Состоит из объявлений функций, объединений, структур, типов данных, макросов, констант и других элементов программирования. WinAPI описывается главным и находится в заголовках Windows C. Официальная реализация функций WinAPI находится в динамических библиотеках (DLL): например, kernel32.dll, user32.dll, gdi32.dll или shell32.dll в системном каталоге. Существуют сторонние реализации Windows API: в первую очередь проект Wine и проект ReactOS.

Windows API — динамический объект. Количество функций постоянно растет с каждой новой версией ОС и новыми пакетами обновлений. Существуют также важные различия между версиями сервера и настольными версиями операционной системы. Некоторые функции официально не документированы.

Pelles C

Pelles C — бесплатная программа и лучший компилятор C и интегрированная среда разработки (IDE) для языка программирования C. Поддерживает 32-разрядную Windows (x86) и 64-разрядную Windows (x64). Реализует как стандарты C99, так и C11. Pelles C имеет встроенный редактор ресурсов, растровое изображение, значок и редактор курсоров и редактор шестнадцатеричных дампов. Он разработан шведским разработчиком Пелле Ориниусом. Название компилятора носит имя своего автора. Поставляется с SDK, поэтому программист сразу может приступить к созданию приложений без дальнейшей установки.

Ошибка целевой архитектуры

Чтобы создавать программы Windows API, необходимо включить расширения Microsoft. По умолчанию они выключены, в связи с чем компилятор выдает следующее сообщение об ошибке, которое служит примером C WinAPI с нарушенной структурой: fatal error #1014: #error: "No target architecture" («Нет целевой архитектуры»). Чтобы включить расширения Microsoft, переходим к параметрам проекта и выбираем вкладку «Компилятор». На этой вкладке активируем флажок «Включить расширения Microsoft».

MSDN

Является центральным порталом для разработки Windows. Это огромная коллекция материалов, связанных с разработкой приложений с использованием инструментов Microsoft. Это самая полная база наряду с документацией по разработке настольных приложений и список API Windows.

Применение DLL в WinAPI C

Библиотека общих элементов управления обеспечивает доступ к расширенным функциям операционной системы, таким как строки состояния, индикаторы выполнения, панели инструментов и вкладки. Эти команды находятся в библиотеке commctrl.dll в 16-разрядных системах и comctl32.dll и сгруппированы с пользовательским интерфейсом.

DLL — это формат файла динамической библиотеки ссылок, используемый для хранения нескольких кодов и процедур для программ Windows. Файлы DLL были созданы таким образом, что несколько программ могли использовать их информацию одновременно, помогая сохранить память. Позволяет пользователю редактировать кодирование сразу нескольких приложений без их изменения. Библиотеки DLL можно преобразовать в статические, используя MSIL Disassembler или DLL для Lib 3.00.

WinAPI как интерфейс прикладного программирования для Windows предлагает множество мощных функций, которые позволяют создавать свои программы, начиная с простой обработки файлов и заканчивая построением графического интерфейса для программирования низкоуровневых драйверов устройств.

Прежде чем начать программирование в WinAPI, необходимо настроить среду для кода в ОС Windows. Поскольку это не дистрибутив Linux, у него нет встроенного компилятора для создания приложений. Рассмотрим следующие варианты для компиляции кода:


Для Windows доступен комплект разработчика, который предоставляет документацию и инструменты, позволяющие разработчикам создавать программное обеспечение с использованием API и связанных с ним технологий.

Интерфейс прикладного программирования WindowsAPI (applicationprogramminginterface) является интерфейсом системного программирования в пользовательском режиме для семейства операционных систем Windows. До выхода 64-разрядных версий Windows программный интерфейс для 32-разрядных версий операционных систем Windows назывался Win32 API, чтобы его можно было отличить от исходной 16-разрядной версии Windows API (которая служила интерфейсом программирования для начальных 16-разрядных версий Windows).

Windows API состоит из нескольких тысяч вызываемых функций, которые разбиты на следующие основные категории:

  • Базовыеслужбы (Base Services).
  • Службыкомпонентов (Component Services).
  • Службы пользовательского интерфейса (User Interface Services).
  • Графические и мультимедийные службы (Graphics and Multimedia Services).
  • Обмен сообщениями и совместная работа (Messaging and Collaboration).
  • Сеть (Networking).
  • Веб-службы (Web Services).

Описание WindowsAPI можно найти в документации по набору инструментальных средств разработки программного обеспечения - WindowsSoftwareDevelopmentKit (SDK). Эта документация доступна на веб-сайте www.msdn.microsoft.com. Она также включена со всеми уровнями подписки в сеть MicrosoftDeveloperNetwork (MSDN), предназначенную для разработчиков.

Microsoft .NET Framework состоит из библиотеки классов под названием Framework Class Library (FCL) и управляемой среды выполнения кода -Common Language Runtime (CLR). CLR обладает функциями своевременной компиляции, проверки типов, сборки мусора и обеспечения безопасности доступа к коду. Предлагая эти функции, CLR предоставляет среду разработки, повышающую производительность работы программистов и сокращающую количество наиболее распространенных ошибок программирования.

Среда CLR реализована, как классический COM-сервер, код которого находится в стандартной Windows DLL-библиотеке, предназначенной для работы в пользовательском режиме. Фактически все компоненты.NET Framework реализованы как стандартные Windows DLL-библиотеки пользовательского режима, наложенные поверх неуправляемых функций Windows API. (Ни один из компонентов.NET Framework не работает в режиме ядра.) Взаимоотношения между этими компонентами показаны на рисунке.

Службы, функции и стандартные программы.

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

  • Функции Windows API . Документированные, вызываемые подпрограммы в WindowsAPI. Например, CreateProcess, CreateFile и GetMessage.
  • Собственные системные службы (или системные вызовы) . Недокументированные базовые службы в операционной системе, вызываемые при работе в пользовательском режиме. Например, NtCreateUserProcess является внутренней службой, которую функция Windows CreateProcess вызывает для создания нового процесса.
  • Функции поддержки ядра (или подпрограммы) . Подпрограммы внутри операционной системы Windows, которые могут быть вызваны только из режима ядра. Например, ExAllocatePoolWithTag является подпрограммой, вызываемой драйверами устройств для выделения памяти из системных динамически распределяемых областей Windows (называемых пулами).
  • Службы Windows. Процессы, запускаемые Диспетчером управления службами (Windowsservicecontrolmanager). Например, служба Диспетчер задач запускается в виде процесса, работающего в пользовательском режиме, в котором поддерживается команда at (аналогичная UNIX-командам at или cron).
  • Библиотеки DLL (dynamic-link libraries - динамически подключаемые библиотеки) . Набор вызываемых подпрограмм, связанных вместе в виде двоичного файла, который может быть загружен в динамическом режиме приложениями, которые используют эти подпрограммы. В качестве примера можно привести Msvcrt.dll (библиотеку времени выполнения для приложений, написанных на языке C) и Kernel32.dll (одну из библиотек подсистемы Windows API). DLL-библиотеки широко используются компонентами и приложениями Windows, которые работают в пользовательском режиме. Преимущество, предоставляемое DLL-библиотеками по сравнению со статическими библиотеками, заключается в том, что они могут использоваться сразу несколькими приложениями, и Windows обеспечивает наличие в памяти только одной копии кода DLL-библиотеки для тех приложений, в которых имеются ссылки на эту библиотеку. Следует заметить, что неисполняемые.NET-сборки компилируются как DLL-библиотеки, но без каких-либо экспортируемых подпрограмм. CLR анализирует скомпилированные метаданные для доступа к соответствующим типам и элементам классов.

История Win32 API.

Интересно, что Win32 не планировался в качестве исходного интерфейса программирования для той системы, которая в ту пору называлась Windows NT. Поскольку проект Windows NT запускался в качестве замены для OS/2 версии 2, первоначальным интерфейсом программирования был 32-разрядный OS/2 PresentationManagerAPI. Но через год после запуска проекта произошел взлет поступившей в продажу Microsoft Windows 3.0. В результате этого Microsoft сменила направление и сделала Windows NT будущей заменой семейства продуктов Windows, а не заменой OS/2. В связи с этим и возникла необходимость выработать спецификацию Windows API - до этого, в Windows 3.0, API существовал только в виде 16-разрядного интерфейса.

Хотя в Windows API намечалось введение множества новых функций, недоступных в Windows 3.1, Microsoft решила сделать новый API, по возможности, максимально совместимым по именам, семантике и используемым типам данных с 16-разрядным Windows API, чтобы облегчить бремя переноса существующих 16-разрядных Windows-приложений в Windows NT. Этим, собственно, и объясняется тот факт, что многие имена функций и интерфейсов могут показаться непоследовательными: так нужно было для обеспечения совместимости нового Windows API со старым 16-разрядным Windows API.

Disclaimer

Казалось бы, что WinAPI уходит в прошлое. Давно уже существует огромное количество кросс-платформенных фреймфорков, Windows не только на десктопах, да и сами Microsoft в свой магазин не жалуют приложения, которые используют этого монстра. Помимо этого статей о том, как создать окошки на WinAPI, не только здесь, но и по всему интернету, исчисляется тысячами по уровню от дошколят и выше. Весь этот процесс разобран уже даже не по атомам, а по субатомным частицам. Что может быть проще и понятнее? А тут я еще…

Но не все так просто, как кажется.

Почему о WinAPI сейчас?

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

О чем это я? А вот кусочке кода:

Case WM_KEYDOWN: MessageBox(hwndDlg,"Die!","I"m dead!",MB_YESNO|MB_ICONINFORMATION); break;
Таким образом, авторы хотели добавить поддержку клавиатуры, но суровая реальность недр архитектуры диалоговых окон в Windows жестко пресекла такую самодеятельность. Те, кто пользовался эмулятором и отладчиком в нем, хоть раз видели это сообщение?
В чем же проблема?

Ответ такой: так делать нельзя!

И, возвращаясь, к изначальному вопросу о WinAPI: очень много популярных, и не очень, проектов продолжают его использовать и в настоящее время, т.к. лучше, чем на чистом API многие вещи не сделать (тут можно бесконечно приводить аналогии вроде сравнения высокоуровневых языков и ассемблера, но сейчас не об этом). Да и мало ли почему? Просто используют и все тут.

О проблеме

Диалоговые окна упрощают работу с GUI, одновременно лишая нас возможности сделать что-то самостоятельно. Например, сообщения WM_KEYDOWN/WM_KEYUP, приходящие в оконную процедуру, «съедаются» в недрах DefDlgProc, беря на себя такие вещи, как: Навигация по Tab, обработка клавиш Esc, Enter, и т.д. Кроме того, диалоги не нужно создавать вручную: проще, ведь, набросать кнопок, списков, в редакторе ресурсов, вызвать в WinMain CreateDialog/DialogBox и все готово.

Обойти такие мелкие неприятности просто. Есть, как минимум, два вполне легальных способа:

  1. Создать свой собственный класс через RegisterClassEx и в процедуре обработки класса схватывать WM_KEYDOWN, перенаправлять в процедуру обработки самого диалога. Да-да! Можно создавать диалоги со своим собственным классом, и встроенный в VS редактор даже позволяет задавать имя класса для диалога. Вот только кто об этом знает и этим пользуется?
    Минус очевиден: Нужно регистрировать еще один класс, иметь на 1 CALLBACK процедуру больше, суть которой будет всего-навсего в трансляции пары сообщений. Кроме того, мы не будем знать куда их транслировать, и придется городить костыли.
  2. Использовать встроенный механизм акселераторов. И нам даже не придется менять код диалоговой процедуры! Ну, разве что, добавить одну строчку в switch/case, но об этом ниже.

Tutorials?

Не побоюсь сказать, что все туториалы по созданию окон через WinAPI начинаются с такого незамысловатого кода, обозначая его, как «цикл обработки сообщений» (опущу детали по подготовке класса окна и прочую обвязку):

While (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
Здесь действительно все просто:

  1. GetMessage() выхватывает очередное сообщение из очереди, и ключевой момент : блокирует поток, если в очереди пусто.
  2. TranslateMessage() из WM_KEYDOWN/WM_KEYUP формирует сообщения WM_CHAR/WM_SYSCHAR (они нужны, если кто-то хочет сделать свой редактор текста).
  3. DispatchMessage() отправляет сообщение в оконную процедуру (если таковая существует).
Начнем с того, что этот код использовать опасно, и вот почему . Обратите внимание на сноску:
Because the return value can be nonzero, zero, or -1, avoid code like this:
while (GetMessage(lpMsg, hWnd, 0, 0)) ...
И ниже приводится пример правильного цикла.

Стоит сказать, что в шаблонах VS для Win32 приложений, написан именно такой неправильный цикл. И это очень печально. Ведь мало кто будет вникать в то, что сделали сами авторы, ведь это априори правильно. И неправильный код множится вместе с багами, которые очень сложно отловить.

После этого фрагмента кода, как правило, следует рассказ про акселераторы, и добавляется пара новых строчек (учитывая замечание в MSDN, предлагаю сразу писать правильный цикл):

HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); BOOL bRet = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) { if (-1 == bRet) break; if (!TranslateAccelerator(msg.hwnd, hAccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
Этот вариант я видел чаще всего. И он (та-дам ) снова неправильный!

Сперва о том, что изменилось (потом о проблемах этого кода):

В первой строчке из ресурсов загружается таблица клавиш, при нажатии на которые, будет формироваться сообщение WM_COMMAND с соответствующим id команды.

Собственно TranslateAccelerator этим и занимается: если видит WM_KEYDOWN и код клавиши, которые есть в этом списке, то (опять же ключевой момент) будет формировать сообщение WM_COMMAND (MAKEWPARAM(id, 1)) и отправлять в соответствующую для дескриптора окна, указанного в первом аргументе, процедуру обработки.

Из последней фразы, думаю, стало понятно, в чем проблема предыдущего кода.
Поясню: GetMessage выхватывает сообщения для ВСЕХ объектов типа «окно» (в число которых входят и дочерние: кнопки, списки и прочее), а TranslateAccelerator будет отправлять сформированную WM_COMMAND куда? Правильно: обратно в кнопку/список и т.д. Но мы обрабатываем WM_COMMAND в своей процедуре, а значит нам интересно ее получать в ней же.

Ясно, что TranslateAccelerator надо вызывать для нашего созданного окна:

HWND hMainWnd = CreateWindow(...); HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); BOOL bRet = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) { if (-1 == bRet) break; if (!TranslateAccelerator(hMainWnd, hAccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
И вроде все хорошо и замечательно теперь: мы разобрали все детально и все должно работать идеально.

И снова нет. :-) Это будет работать правильно, пока у нас ровно одно окно - наше. Как только появится немодальное новое окно (диалог), все клавиши, которые будут в нем нажаты оттранслируются в WM_COMMAND и отправляться куда? И опять же правильно: в наше главное окно.

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

IsDialogMessage

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

На самом деле, делает она чуть больше, чем следует из названия. А именно:

  • Осуществляет навигацию по дочерним контролам кнопками Tab/Shift+Tab/вверх/вниз/вправо/влево. Плюс еще кое-что, но этого нам достаточно
  • По нажатии на ESC формирует WM_COMMAND(IDCANCEL)
  • По нажатии на Enter формирует WM_COMMAND(IDOK) или нажатие на текущую кнопку по умолчанию
  • Переключает кнопки по умолчанию (рамочка у таких кнопок чуть ярче остальных)
  • Ну и еще разные штуки, которые облегчают пользователю работу с диалогом
Что она нам дает? Во-первых, нам не надо думать о навигации внутри окна. Нам и так все сделают. Кстати, навигацию по Tab можно сделать, добавив стиль WS_EX_CONTROLPARENT нашему основному окну, но это топорно и не так функционально .

Во-вторых, она нам облегчит жизнь по всем остальным пунктам, перечисленным в списке (и даже немного больше).

Вообще, она используется где-то в недрах Windows для обеспечения работы модальных диалоговых окон , а программистам дана, чтобы вызывать ее для немодальных диалогов. Однако мы ее можем использовать где угодно :

Although the IsDialogMessage function is intended for modeless dialog boxes, you can use it with any window that contains controls, enabling the windows to provide the same keyboard selection as is used in a dialog box.
Т.е. теперь, если мы оформим цикл так:

HWND hMainWnd = CreateWindow(...); HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); BOOL bRet = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) { if (-1 == bRet) break; if (!TranslateAccelerator(hMainWnd, hAccel, &msg)) { if (!IsDialogMessage(hMainWnd, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } }
То наше окошко будет иметь навигацию, как в родном диалоге Windows. Но теперь мы получили два недостатка:

  1. Этот код также будет хорошо работать только с одним (немодальным) окном ;
  2. Получив все достоинства диалоговой навигации, мы лишаемся прелестей в виде сообщений WM_KEYDOWN/WM_KEYUP (только для самого окна, а не для дочерних контролов);
И вот на этом этапе вообще все туториалы заканчиваются и начинаются вопросы: How to handle keyboard events in a winapi standard dialog?
Это первая ссылка в гугле, но поверьте: тысячи их. Про предлагаемые решений (лучшее из которых - это создать свой класс диалогов, о чем я писал выше, до subclassing и RegisterHotKey. Где-то я даже видел «лучший» из способов: использовать Windows Hooks).

Пора поговорить о том, чего нет в туториалах и ответах.

Как правило (как правило! Если кому-то захочется большего, то можно регистрировать свой класс для диалогов и работать так. И, если же, кому-то это интересно, я могу дополнить этим статью) WM_KEYDOWN хотят тогда, когда хотят обработать нажатие на клавишу, которая выполнит функцию в независимости от выбранного контрола в окне - т.е. некая общая функция для всего данного конкретного диалога. А раз так, то почему бы не воспользоваться богатыми возможностями, которые нам сама WinAPI и предлагает: TranslateAccelerator .

Везде используют ровно одну таблицу акселераторов, и только для главного окна. Ну действительно: цикл GetMessage-loop один, значит и таблица одна. Куда еще их девать?

На самом деле, циклы GetMessage-loop могут быть вложенными . Давайте еще раз посмотрим описание PostQuitMessage :

The PostQuitMessage function posts a WM_QUIT message to the thread"s message queue and returns immediately; the function simply indicates to the system that the thread is requesting to quit at some time in the future.
И GetMessage:
If the function retrieves the WM_QUIT message, the return value is zero.
Таким образом, выход из GetMessage-loop осуществится, если мы вызовем PostQuitMessage в процедуре окна. Что это значит?

Мы можем для каждого немодального окна в нашей программе создавать свой собственный подобный цикл. В данном случае DialogBoxParam нам не подходит, т.к. оно крутит свой собственный цикл и повлиять мы на него не можем. Однако если создадим диалог через CreateDialogBoxParam или окно через CreateWindow, то можно закрутить еще один цикл. При этом в каждом таком окне и диалоге мы должны вызывать PostQuitMessage:

HWND hMainWnd = CreateWindow(...); HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); BOOL bRet = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) { if (-1 == bRet) break; if (!TranslateAccelerator(hMainWnd, hAccel, &msg)) { if (!IsDialogMessage(hMainWnd, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } // .... LRESULT CALLBACK WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) { switch(umsg) { case WM_MYMESSAGE: { HWND hDlg = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MYDIALOG), hwnd, MyDialogBoxProc); HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR_FOR_MY_DIALOG)); BOOL bRet = 0, fSavedEnabledState = IsWindowEnabled(hwnd); EnableWindow(hwnd, FALSE); // disable parent window, as dialog window is modal while (bRet = GetMessage(&msg, nullptr, 0, 0)) { if (-1 == bRet) break; if (!TranslateAccelerator(hDlg, hAccel, &msg)) { if (!IsDialogMessage(hDlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } EnableWindow(hwnd, fSavedEnabledState); // enable parent window. Dialog was closed break; } } } INT_PTR CALLBACK MyDlgProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) { switch(umsg) { case WM_CLOSE: { // EndDialog(hwnd, 0); -- DONT DO THAT! // EndDialog is valid ONLY for Modal Dialogs, created with DialogBox(Param) DestroyWindow(hwnd); break; } case WM_DESTROY: { PostQuitMessage(0); break; } // .... } return 0; }
Обратите внимание: теперь для каждого нового окна в нашей программе мы можем добавить в обработку собственную таблицу акселераторов. WM_QUIT будет выхватывать GetMessage из цикла для диалога, а внешний цикл его даже не увидит. Почему так происходит?

Дело в том, что внешний цикл «встал» на вызове DispatchMessage, который вызвал нашу процедуру, которая крутит свой собственный внутренний цикл GetMessage с таким же DispatchMessage. Классический вложенный вызов (в данном случае DispatchMessage). Посему внешний цикл не получит WM_QUIT и не завершится на этом этапе. Все будет работать стройно.

Но и тут есть свои недостатки:
Каждый такой цикл будет обрабатывать сообщения только для «своего» окна . Про другие-то мы здесь не знаем. А значит, если где-то объявится еще один цикл, то все остальные окна не будут получать нужной обработки своих сообщений парой TranslateAccelerator/IsDialogMessage.

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

Делаем красиво

Т.к. правильная постановка задачи является половиной решения, то сперва надо эту самую задачу правильно же и поставить.

Во-первых, было бы логично, что только активное окно принимает сообщения. Т.е. для неактивного окна мы не будем транслировать акселераторы и передавать сообщения в IsDialogMessage.

Во-вторых, если для окна не задана таблица акселераторов, то транслировать нечего, будем просто отдавать сообщение в IsDialogMessage.

Создадим простой std::map, который будет мапить дескриптор окна в дескриптор таблицы акселераторов. Вот так:

Std::map l_mAccelTable;
И по мере создания окон будем в него добавлять новые окна с дескриптором на свою любимую таблицу (или нуль, если такая обработка не требуется).

BOOL AddAccelerators(HWND hWnd, HACCEL hAccel) { if (IsWindow(hWnd)) { l_mAccelTable[ hWnd ] = hAccel; return TRUE; } return FALSE; } BOOL AddAccelerators(HWND hWnd, LPCTSTR accel) { return AddAccelerators(hWnd, LoadAccelerators(hInstance, accel)); } BOOL AddAccelerators(HWND hWnd, int accel) { return AddAccelerators(hWnd, MAKEINTRESOURCE(accel)); } BOOL AddAccelerators(HWND hWnd) { return AddAccelerators(hWnd, HACCEL(NULL)); }
Ну и после закрытия окна удалять. Вот так:

Void DelAccel(HWND hWnd) { std::map::iterator me = l_mAccelTable.find(hWnd); if (me != l_mAccelTable.end()) { if (me->second) { DestroyAcceleratorTable(me->second); } l_mAccelTable.erase(me); } }
Теперь, как создаем новый диалог/окно, вызываем AddAccelerators(hNewDialog, IDR_MY_ACCEL_TABLE). Как закрываем: DelAccel(hNewDialog).

Список окон с нужными дескрипторами у нас есть. Немного модифицируем наш основной цикл обработки сообщений:

// ... HWND hMainWnd = CreateWindow(...); AddAccelerators(hMainWnd, IDR_ACCELERATOR); BOOL bRet = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) { if (-1 == bRet) break; if (!HandleAccelArray(GetActiveWindow(), msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } // ...
Значительно лучше! Что же там в HandleAccelArray и зачем там GetActiveWindow()?

Немного теории:

Есть две функции, возвращающих дескриптор активного окна GetForegroundWindow и GetActiveWindow . Отличие первой от второй вполне доходчиво описано в описании второй:

The return value is the handle to the active window attached to the calling thread"s message queue. Otherwise, the return value is NULL.
Если первая будет возвращать дескриптор любого окна в системе, то последняя только того, которое использует очередь сообщений нашего потока . Т.к. нас интересуют окна только нашего потока (а значит те, которые будут попадать в нашу очередь сообщений), то и возьмем последнюю.

Так вот HandleAccelArray, руководствуясь переданным ей дескриптором на активное окно, ищет это самое окно в нашей мапе, и если оно там есть, отдает это сообщение на трансляцию в TranslateAccelerator, а затем (если первый не увидел нужного) в IsDialogMessage. Если и последняя не обработала сообщение, то возвращаем FALSE, чтобы пройти по стандартной процедуре TranslateMessage/DispatchMessage.

Выглядит так:

BOOL HandleAccelWindow(std::map::const_iterator mh, MSG & msg) { const HWND & hWnd = mh->first; const HACCEL & hAccel = mh->second; if (!TranslateAccelerator(hWnd, hAccel, &msg)) { // message not for TranslateAccelerator. Try it with IsDialogMessage if (!IsDialogMessage(hWnd, &msg)) { // so, do default stuff return FALSE; } } // ok, message translated. Say to message-loop, to get next message return TRUE; } BOOL HandleAccelArray(HWND hActive, MSG & msg) { if (!hActive) return FALSE; // no active window. Nothing to do std::map::const_iterator mh = l_mAccelTable.find(hActive); if (mh != l_mAccelTable.end()) { // Got it! Try to translate this message for the active window return HandleAccelWindow(mh, msg); } return FALSE; }
Теперь каждое дочернее окно вправе добавить себе любимую таблицу акселераторов и спокойно ловить и обрабатывать WM_COMMAND с нужным кодом.

А что там еще об одной строчке в коде обработчика WM_COMMAND?

Описание в TranslateAccelerator гласит:
To differentiate the message that this function sends from messages sent by menus or controls, the high-order word of the wParam parameter of the WM_COMMAND or WM_SYSCOMMAND message contains the value 1.
Обычно код обработки WM_COMMAND выглядит так:

Switch(HIWORD(wParam)) { case BN_CLICKED: // command from buttons/menus { switch(LOWORD(wParam)) { case IDC_BUTTON1: DoButton1Stuff(); break; case IDC_BUTTON2: DoButton2Stuff(); break; // ... } break; } }
Теперь можно написать так:

Switch(HIWORD(wParam)) { case 1: // accelerator case BN_CLICKED: // command from buttons/menus { switch(LOWORD(wParam)) { case IDC_BUTTON1: DoButton1Stuff(); break; case IDC_BUTTON2: DoButton2Stuff(); break; // ... } break; } }
И теперь, возвращаясь к тому же fceux, добавив всего одну строчку в код обработки команд от кнопок, мы получим желаемое: управлять дебагером с клавиатуры. Достаточно добавить небольшую обертку вокруг главного цикла сообщений и новую таблицу акселераторов с нужными соответствиями VK_KEY => IDC_DEBUGGER_BUTTON.

P.S.: Мало кто знает, но можно создавать свою собственную таблицу акселераторов , а теперь и применять ее прямо налету.

P.P.S.: Т.к. DialogBox/DialogBoxParam крутит собственный цикл, то от при вызове диалога через них акселераторы работать не будут и наш цикл (или циклы) будет «простаивать».

P.P.P.S.: После вызова HandleAccelWindow мап l_mAccelTable может измениться, т.к. TranslateAccelerator или IsDialogMessage вызывают DispatchMessage, а там может встретиться AddAccelerators или DelAccel в наших обработчиках! Поэтому лучше его после этой функции не трогать.

Пощупать код можно . За основу был взят код, генерируемый из стандартного шаблона MS VS 2017.

Теги: Добавить метки

FindWindow
GetWindow
GetWindowText
SetWindowText
IsWindow
MoveWindow
IsWindowVisible
EnableWindow
IsWindowEnabled
WindowFromPoint
ShowWindow
CloseWindow
SetWindowPos
GetClassLong
SetClassLong
GetWindowLong
SetWindowLong
GetDesktopWindow
GetParent

Функция FindWindow

function FindWindow(className,WindowName: PChar) : HWND;
Функция возвращает описатель окна, удовлетворяющий запросу (0 -если такого окна не найдено).

ClassName Имя класса, по которому призводится поиск среди ВСЕХ окон системы. WindowName Заголовок окна

Один из параметров может быть равен nil, тогда поиск ведется по другому параметру.
Пример:

Функция GetWindow

function GetWindow(Wnd: HWND; Param) : HWND
Функция возвращает описатель окна удовлетворяющий запросу.

Wnd Описатель какого-либо начального окна Param Принимает одно из следующих значений-констант: gw_Owner Возвращается описатель окна-предка (0 - если нет предка). gwHWNDFirst Возвращает описатель первого окна (относительно Wnd). gw_HWNDNext Возвращает описатель следующего окна (окна перебираются без повторений, т.е. если вы не меняли параметр Wnd функции, повторно описатели не возвращаются) gw_Child Возвращает описатель первого дочернего окна.

Пример:

Функция GetWindowText

function GetWindowText(hWnd: HWND; lpString: PChar; nMaxCount: Integer): Integer;
Функция возвращает текст окна. Для формы это будет заголовок, для кнопки - надпись на кнопке.

hWnd Описатель того окна, текст которого нужно получить. lpString Переменная, в которую будет помещен результат nMaxCount

Максимальная длина текста, если текст длиннее, то он обрезается.


Пример:

Функция IsWindow

function IsWindow(hWnd: HWND): BOOL; Возвращает True, если окно с заданным описателем существует и False в противном случае.

Hwnd Описатель нужного окна

Функция MoveWindow

MoveWindow(hWnd: HWND; X, Y, nWidth, nHeight: Integer; bRepaint: BOOL): BOOL; Перемещает окно в новую позицию.

hWnd Описатель перемещаемого окна. X, Y, nWidth, nHeight Соответственно: новые координаты X,Y; новая ширина, высота. bRepaint Булево значение, показывающее будет ли окно перерисовано заново.

Функция IsWindowVisible

function IsWindowVisible(hWnd: HWND): BOOL;
Возвращает True если данное окно видимо.

hWnd Описатель окна.

Функция EnableWindow

function EnableWindow(hWnd: HWND; bEnable: BOOL): BOOL;
Устанавливает доступность окна(окно недоступно, если оно не отвечает на события мыши, клавиатуры и т.д.). Аналог в Delphi свойство Enabled компонентов. EnableWindow возвращает True, если всё прошло успешно и False в противром случае.

hWnd Описатель окна. bEnable Булево значение, определяющее доступность окна.


Пример:

Функция IsWindowEnabled

function IsWindowEnabled(hWnd: HWND): BOOL;
Возвращает для заданного окна: True, если окно доступно и False в противном случае.

hWnd Описатель окна.

Функция WindowFromPoint

WindowFromPoint(Point: TPoint): HWND;
Возвращает описатель окна, находящегося в данной точке экрана.

Point Координата точки экрана типа TPoint (определение типа смотри ниже)

Функция

type TPoint = Record x: Longint; y: Longint; end ;

Функция ShowWindow

function ShowWindow(hWnd: HWND; nCmdShow: Integer): BOOL; Показывает или прячет окно.

hWnd Описатель нужного окна nCmdShow Константа, определяющая, что будет сделано с окном: SW_HIDE SW_SHOWNORMAL SW_NORMAL SW_SHOWMINIMIZED SW_SHOWMAXIMIZED SW_MAXIMIZE SW_SHOWNOACTIVATE SW_SHOW SW_MINIMIZE SW_SHOWMINNOACTIVE SW_SHOWNA SW_RESTORE SW_SHOWDEFAULT SW_MAX


Пример:

Функция CloseWindow

function CloseWindow(hWnd: HWND): BOOL; stdcall;
Закрывает окно.

hWnd Описатель закрываемого окна.

SetWindowPos

function SetWindowPos(hWnd: HWND; hWndInsertAfter: HWND; X, Y, cx, cy: Integer; uFlags: UINT): BOOL; stdcall;
Устанавливает окно в новую позицию

hWnd Оптсатель окна hWndInsertAfter Описатель окна, перед которым в списке Z-Order будет вставлено окно hWnd , или одна из следующих констант: HWND_BOTTOM Поместить окно на дно списка Z-Order HWND_TOP Поместить окно на верх списка Z-Order X, Y, cx, cy

Соответственно - новые горизонт. , верт. позиции окна (X, Y ), а также новая ширина
и высота (cx, cy )

uFlags Одна или несколько (разделенных OR ) следующих констант: SWP_NOSIZE Не изменять размер окна после перемещения (cx, cy игнорируются) SWP_NOZORDER Не изменять положение окна в списке Z-Order SWP_SHOWWINDOW Сделать окно видимым после перемещения SWP_HIDEWINDOW Спрятать окно после перемещения SWP_NOACTIVATE Не передавать фокус окну после перемещения SWP_NOMOVE Не перемещать окно (игнорируется X, Y )

Функция GetClassLong

function GetClassLong(hWnd: HWND; nIndex: Integer): Integer;
Эта функция возвращает 32-разрядное целое, взятое из определенного поля записи TWndClassEx указанного окна.

hWnd Описатель окна nIndex Константа, определяющая что будет возвращено. Должна быть одна из следующих: GCL_MENUNAME Возвращает указатель на строку, содержащую имя меню класса, определенного в файле ресурсов связанного с некоторой программой. GCL_HBRBACKGROUND Возвращает описатель (HBRUSH) кисти фона, ассоциированной с классом GCL_HCURSOR Возвращает описатель (HCURSOR) курсора, фссоциированного с классом GCL_HICON Возвращает описатель (HICON) пиктограммы, ассоциированной с классом GCL_HMODULE Возвращает описатель процесса (HMODULE), зарегистртровавшего класс. GCL_CBWNDEXTRA Возвращает размер памяти (в байтах), выделенной под хранение дополнительных данных ДАННОГО ОКНА. Описание как использвать эту память, смотри в описании функции GCL_CBCLSEXTRA Возвращает размер памяти (в байтах), выделенной под хранение дополнительных ДАННОГ КЛАССА GCL_WNDPROC Возвращает адрес оконной процедуры, связанной с классом. GCL_STYLE Возвращает стиль класса (наличие того или иного стиля проверяется побитовой операцией And с помощью констант типа cs_XXX ) GCL_HICONSM

Обратите внимание : в случае, когда функция возвращает указатель, необходимо приведение типов (Integer ->

Функция SetClassLong

function SetClassLong(hWnd: HWND; nIndex: Integer; dwNewLong: Longint): Integer; Парная функция функции . Устанавливает в нужное поле соответствующее значение.
Функция возвращает старое значение поля, чтобы его потом можно было исправить или же возврашается ноль, если что-то пошло не так как надо.

hWnd Описатель окна nIndex Одна из констант GCL_XXX из функции . В зависимости от значения этого поля будет изменнено нужное поле.

Обратите внимание Pointer к типу Integer .

Функция GetWindowLong

function GetWindowLong(hWnd: HWND; nIndex: Integer): Longint; Возвращает информацию о некотором окне в виде 32-битного целого.

hWnd Описатель окна nIndex Константа,определяющая, что будет возвращено. Должна быть одна их следующих:
GWL_WNDPROC Возвращает адрес оконной процедуры, связанной с данным окном. Полученный адрес (после соответсвующих приведений типов) может использоваться в функции CallWindowProc . Данное значение обычно используют, если хотят заменить существующую оконную процедуру на свою собственную, при этом, чтобы не потерять работоспособности окна, обычно и используют CallWindowProc . GWL_HINSTANCE Возвращает описатель приложения, заданный при создании окна функцией CreateWindowEx . GWL_HWNDPARENT Возвращает описатель (HWND) родительского окна GWL_STYLE Возвращает стиль окна. Конкркетные значения стилей узнаются при помощи побитовой операции And и констант WS_XXX GWL_EXSTYLE Возвращает расширенный стиль окна. Конкркетные значения стилей узнаются при помощи побитовой операции And и констант WS_EX_XXX GWL_USERDATA Возвращает 32-битное целое, ассоциированное с окном (это последний параметр в вызове CreateWindow или CreateWindowEx) GWL_ID Возвращает идентификатор окна (он не имеет ничего общего с описателем окна!), задаваемый параметром hMenu для дочерних окон при вызове CreateWindow или CreateWindowEx

Обратите внимание : в случае, когда функция возвращает указатель, необходимо приведение типов (Integer -> Pointer). Делать это можно так:

Функция SetWindowLong

function SetWindowLong(hWnd: HWND; nIndex: Integer; dwNewLong: Longint): Longint;
Парная к функции . Изменяет аттрибуты определенного окна.
Функция возвращает старое значение свойства, если вызов прошел удачно или нуль в противном случае.

hWnd Описатель окна nIndex Константа, определяющая, какое свойство будет изменено. Должна быть одной из констант GWL_XXX из описания функции dwNewLong Новое значение свойства, определяемого константой nIndex

Обратите внимание :при установке полей-указателей необходимо приведение типа Pointer к типу Integer .

Функция GetDesktopWindow

function GetDesktopWindow: HWND
Функция возвращает описатель окна Рабочего Стола (Desktop). Без параметров.

GetParent

function GetParent(hWnd: HWND): HWND;
Возвращает описатель родительского окна для окна hWnd .

hWnd Описатель окна

API (Application Programming Interface) - это интерфейс программирования приложений, термин, часто упоминаемый разработчиками программного обеспечения. Если разрабатываемое вами приложение имеет функцию, позволяющую обращаться к нему из других приложений, то это - API вашего приложения. Параметры, которые принимает ваша функция, образуют её API, так как они являются средством, при помощи которого другие приложения взаимодействуют с данной функцией.

Операционная система Windows предоставляет большой набор функций, позволяющих различным приложениям, в том числе и приложениям Visual FoxPro, обмениваться информацией с Windows на достаточно низком уровне. Эти функции принято называть Windows API. Использование Windows API в приложениях Visual FoxPro позволяет реализовать возможности, недостижимые стандартными средствами языка.

Объявление Windows API функций в Visual FoxPro

Функции Windows API скомпонованы в динамически связанные библиотеки (Dynamic Link Library, DLL ). Как правило, файлы таких библиотек имеют расширение dll . Перед тем, как использовать Windows API функцию в вашем приложении, вы должны её объявить . Для объявления функции применяется команда DECLARE..DLL:

DECLARE [cFunctionType ] FunctionName IN LibraryName ; [cParamType1 [@] ParamName1 , cParamType2 [@] ParamName2 , ...]

Параметры команды:

cFunctionType
необязательный параметр, указывает тип данных, возвращаемых функцией:

cFunctionType Размер,
байт
Описание
Short 16-ти разрядное целое число
Integer, Long 4 32-х разрядное целое число
Single 4 32-х разрядное вещественное число
Double 8 64-х разрядное вещественное число
String - Строка символов

FunctionName
имя функции в DLL-библиотеке. Имя функции чувствительно к регистру символов, то есть GetDC и GETDC - это имена совершенно разных функций.

LibraryName
наименование DLL-библиотеки, в которой находится функция. Для библиотек Kernel32.dll, Gdi32.dll, User32.dll, Mpr.dll и Advapi32.dll можно использовать синоним WIN32API.

AliasName
необязательный параметр, позволяет вместо имени функции использовать придуманный вами псевдоним. Написание псевдонима, в отличие от имени функции, не чувствительно к регистру символов. Как правило, псевдоним используется, когда имя API функции совпадает с именем встроенной (или вашей) функции Visual FoxPro.

cParamType
указывает тип данных передаваемого функции значения:

Параметр может передаваться как по значению, так и по ссылке. Для указания того, что параметр передаётся по ссылке, используется символ "@".

С точки зрения Visual FoxPro не существует никакой разницы между типами данных Long и Integer. Обычно тип Integer применяется для обозначения целых чисел со знаком, а тип Long - целых чисел без знака.

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

Все функции Windows API, как, впрочем, и сама Windows, написаны на языке программирования Си. Поэтому для того, чтобы понять, как правильно использовать API функции в Visual FoxPro (который, кстати, так же написан на Си, по крайней мере, его ядро), познакомимся, какие типы данных применяются в Си и Windows, и, что не менее важно, разберёмся с такими типами данных, как перечисления, структуры и указатели. Кроме того, вы узнаете, что такое прототипы функций в Си, и как, основываясь на описании прототипа функции в MSDN, правильно объявить её в команде DECLARE..DLL.

Базовые типы данных Си

Если вы знакомы с языком программирования Си, то знаете, как легко в нём можно создавать различные типы данных. Достаточно написать следующий код:

Typedef int INT32;

и вот у вас новый тип INT32, который полностью соответствует типу int. Но, с точки зрения Си, это совершенно разные типы, и попытка присвоить переменной типа INT32 значение переменной типа int приведёт к ошибке!

Изобилие типов данных заставляет многих разработчиков думать, что программирование с использованием API является трудным. Но это не так! В Си в основном используются следующие типы данных:

    тип char - символ в формате ANSI. Имеет длину 8 разрядов (один байт).

    тип wchar - символ в формате Unicode. Имеет длину 16 разрядов (два байта).

    тип int - целые числа. Они делятся в Си на три типа: int , short int и long int . Последние обычно сокращаются до short и long . Тип short - это 16-ти разрядное, а типы int и long - 32-х разрядные целые числа.

    тип float - вещественные числа, имеющие дробную часть. Имеют длину 32 разряда (4 байта).

    тип double - вещественные числа двойной точности. Имеют длину 64 разряда (8 байт).

    тип enum - перечисляемый тип данных.

    тип void используется для обозначения величин, имеющих нулевую длину и не имеющих значения.

    тип pointer - указатель; он не содержит информацию в общепринятом смысле - как другие типы Си; вместо этого, в каждом указателе находится адрес ячейки памяти, где хранятся реальные данные. Имеет длину 32 разряда (4 байта).

Как ни странно, строковый тип в Си отсутствует. На самом деле все строки представлены в Си как массивы символов.

Некоторые типы могут объявляться как беззнаковые. Модификатор unsigned (без знака) используется со следующими типами данных: char , short , int и long .

Например, следующее объявление переменной в Си:

Usigned int имя_переменной ;

означает, что эта переменная - целое 32-х разрядное целое без знака.

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

Перечисляемый тип enum связывает с переменной набор именованных констант, называемых перечисляемыми константами. Объявление перечисляемого типа выглядит так:

Enum поле_тега { const1 , const2 , ... } переменная ;

Если поле_тега опускается, то после закрывающей фигурной скобки необходимо указать переменную . Если поле_тега указано, то не указывается переменная .

Исторически сложилось так, что тип enum равнозначен типу int - то есть переменная перечисляемого типа занимает в памяти 4 байта. Каждая перечисляемая константа имеет значение, определяемое её порядковым номером в списке; нумерация начинается с нуля. Рассмотрим перечисление CombineMode:

Enum CombineMode{ CombineModeReplace, CombineModeIntersect, CombineModeUnion, CombineModeXor, CombineModeExclude, CombineModeComplement };

В этом перечислении константа CombineModeReplace имеет значение 0, константа CombineModeIntersect имеет значение 1, и так далее; константа CombineModeComplement имеет значение 5.

Значения перечисляемых констант могут быть указаны явно, как, например, в следующем примере:

Enum DashCap{ DashCapFlat = 0, DashCapRound = 2, DashCapTriangle = 3 };

Перечисленные типы данных покрывают 99% всех типов данных, используемых в программировании Windows API. Это звучит слишком просто, не так ли? Почему же описания API функций содержат все эти типы - HWND, HINSTANCE, POINT и им подобные?

Причиной тому является то, что Cи имеет особенность, называемую strict-typing . Однажды появившись, переменная одного типа может принимать только те значения, которые соответствуют ее типу. Вы не можете сначала сохранить в переменной строку, а затем присвоить ей число. В Visual FoxPro мы обычно стараемся симулировать подобное путем соглашения о наименованиях. Например, cName представляет собой переменную символьного типа, тогда как nCount - числовую. Strict-typing позволяет создать новый тип данных, присвоив существующему типу данных новое имя. Каждый новый тип представляется отличным от других типов, несмотря на то, что внутренне они хранят одно и то же.

Windows усложняет использование этой концепции. К примеру, тип LONG в действительности представляет собой long int, а тип UINT - unsigned int. Оба типа являются 32-разрядными целыми числами. Производные типы данных определяются в различных include-файлах (файлы с расширением.h). Если вы приобрели Visual Studio.NET, то можете найти эти файлы в папке ..\VC7\PlatformSDK\Include\ .

Типы данных Windows

Определение того, какой из базовых типов Си действительно представляет тип данных, используемый в API функции, является одной из тяжелейших задач в программировании API. Используйте следующее правило: если вы не можете найти слово float, double, char или str где-либо в имени функции или параметра, тогда это обычно 32-разрядное целое. Потребуется некоторое время для понимания и выработки навыков, но потом вы будете запросто преобразовывать типы данных. В следующей таблице приведены основные типы данных Windows и соответствующие им типы, используемые при объявлении функции в Visual FoxPro:

Тип данных
Windows
Тип в объявлении
функции
Описание
BOOL Long 32-х разрядное целое число. 0 означает false, все остальное означает true.
BOOLEAN Long тоже самое, что и BOOL.
BYTE String 8-ми разрядное целое число
CHAR String 8-ми разрядное целое число
CLSID String
COLORREF Long 32-х разрядное целое число
DWORD Long 32-х разрядное целое число
DOUBLE Double 64-х разрядное вещественное число
FLOAT Single 32-х разрядное вещественное число
GUID String 128-разрядное число (16 байт)
HANDLE Long
HBITMAP Long 32-х разрядное целое число без знака
HDC Long 32-х разрядное целое число без знака
HICON Long 32-х разрядное целое число без знака
HGLOBAL Long 32-х разрядное целое число без знака
HKL Long 32-х разрядное целое число без знака
HLOCAL Long 32-х разрядное целое число без знака
HINSTANCE Long 32-х разрядное целое число без знака
HRESULT Long 32-х разрядное целое число без знака
HWND Long 32-х разрядное целое число без знака
LONG Long 32-х разрядное целое число
LPARAM Long 32-х разрядное целое число без знака
SHORT Integer 16-ти разрядное целое число
SIZE_T Long 32-х разрядное целое число без знака
TCHAR String Соответствует типу CHAR для строк формата ANSI и WCHAR для строк формата Unicode
UCHAR String Символ в ANSI кодировке
UINT Long 32-х разрядное целое число без знака
ULONG Long 32-х разрядное целое число без знака
USHORT Integer
UUID String 128-разрядное число (16 байт)
VOID нет Не имеет значения
WCHAR String UNICODE character
WNDPROC Long 32-х разрядное целое число без знака
WORD Integer 16-ти разрядное целое число без знака
WPARAM Long 32-х разрядное целое число без знака

Указатели

Другой концепцией, широко используемой в Си, являются указатели (pointers). Указатель представляет собой переменную, которая содержит адрес области памяти, по которому хранятся данные. Тип указателя всегда определяется типом данных, на которые он указывает; его размер всегда равен четырём байтам. Например, указатель на переменную типа SHORT представляет собой 32-разрядное целое число, как и указатель на любой другой тип данных. Описание указателя, принятое в программировании Windows API, начинается с символов "LP", что означет Long Pointer, или "длинный указатель", работающий с 32-х разрядной моделью памяти. Затем может следовать символ "C" (const), указывающий, что данные не должны изменяться. Далее следует описание типа данных переменной, адрес которой хранится в указателе. Например, LPDWORD - указатель на переменную типа DWORD.

Указатели на числовые данные при объявлении Windows API функции передаются по ссылке. Как пример рассмотрим функцию GetFileSize. Вот её прототип (подробнее о прототипах функций буде рассказано ниже):

DWORD GetFileSize(HANDLE hFile, // дескриптор файла LPDWORD lpFileSizeHigh // указатель);

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

Объявление этой функции в Visual FoxPro:

DECLARE GetFileSize IN WIN32API Long hFile, Long @ FileSizeHight

Как видите, параметр FileSizeHight передаётся функции по ссылке , потому что передача по ссылке - это и есть передача указателя.

Сложнее обстоит дело со строками. Как уже говорилось, символьные строки в Cи - это массивы, поэтому в программировании API функций применяется тип str , определяющий массив символов типа CHAR (соответственно, тип wstr определяет массив символов типа WCHAR). В следующей таблице показаны типы указателей на символьные строки:

Тип указателя
на строку
Описание
LPSTR Указатель на модифицируемую нуль-терминированную строку ANSI-формата. Передаётся по ссылке
LPCSTR Указатель на немодифицируемую нуль-терминированную строку ANSI-формата. Передаётся по значению
LPTSTR Соответствует типу LPSTR для строк формата ANSI и типу LPWSTR для строк формата UNICODE. Передаётся по ссылке.
LPCTSTR Соответствует типу LPSTR для строк формата ANSI и типу LPWSTR для строк формата UNICODE. Передаётся по значению.
LPWSTR Указатель на модифицируемую нуль-терминированную строку UNICODE. Передаётся по ссылке
LPCWSTR Указатель на немодифицируемую нуль-терминированную строку UNICODE. Передаётся по значению

Указатели на символьные данные могут передаваться как по ссылке, так и по значению - тип String в объявлении функции в Visual FoxPro всегда передаёт указатель на переменную, содержащую символьные данные. Используйте передачу символьных данных по ссылке только тогда, когда API функция должна изменить значение параметра.

Структуры

Структуру можно рассматривать как набор переменных различных типов, образующих единое целое. В Си структура создаётся при помощи ключевого слова struct , за которым следует необязательное поле тега (tag) и список элементов структуры:

Struct поле_тега { тип_элемента элемент1 ; тип_элемента элемент2 ; ..... тип_элемента элементN ; };

Возможно и такое объявление структуры:

Struct { тип_элемента элемент1 ; тип_элемента элемент2 ; ..... тип_элемента элементN ; } переменная ;

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

Struct { WORD wYear ; WORD wMonth ; WORD wDayOfWeek ; WORD wDay ; WORD wHour ; WORD wMinute ; WORD wSecond ; WORD wMilliseconds ; } SYSTEMTIME, *PSYSTEMTIME;

Структуры очень похожи на записи таблиц Visual FoxPro. Так, если запись таблицы personal содержит поля fio , address , tlfnumber и email , то для обращения к полю tlfnumber используется следующий синтаксис:

Personal.tlfnumber

Так же выглядит и обращение к полю структуры:

SYSTEMTIME.wMinute

Для формирования структур в Visual FoxPro используются переменные, содержащие строки символов. Например, для рассмотренной выше структуры SYSTEMTIME вам понадобится переменная длиной 16 байт. В первые два байта этой переменной заносится значение поля wYear , в следующие два байта - значение поля wMonth , в следующие два байта - значение поля wDayOfWeek , и так далее, пока структура не будет полностью сформирована. А при объявлении API функции в Visual FoxPro тип параметра, в котором передаётся переменная, содержащая структуру, должен быть типа String. Как записать в строковую переменную числовые данные, вы узнаете чуть позже.

При программировании Windows API на Си описание указателя на структуру начинается с символов LP (Long Pointer), за которыми следует наименование структуры. Так, указатель нас структуру SYSTEMTIME будет иметь тип LPSYSTEMTIME, указатель на структуру POINT будет иметь тип LPPOINT, и так далее. Как видите, ничего сложного, но, благодаря этой концепции, существует чрезвычайно большое количество типов указателей на структуры.

Если данные в передаваемой структуре не должны изменяться, то указатель на такую структуру объявляется так:

CONST имя_стуктуры *

Здесь модификатор CONST означает, что данные в структуре не должны меняться, а символ (*) после имени структуры означает, что вся эта строка есть описание указателя на структуру. В следующем примере показан прототип функции CopyRect, которая копирует одну структуру в другую:

BOOL CopyRect(LPRECT lprcDst , CONST RECT * lprcSrc );

Описание прототипов функций в MSDN

Теперь, когда с типами данных всё стало более-менее понятно, познакомимся подробнее с таким понятием Си, как прототипы функций.

Согласно стандарта ANSI, все функции в Си должны иметь прототипы. Прототип функции достаточно прост:

возвращаемый_тип имя_функции(тип_параметра(ов) имя_параметра(ов) );

Если в прототипе указан тип VOID как возвращаемый_тип , то это означает, что функция не возвращает никаких значений. Если тип VOID указан как тип_параметра , то это означает, что функция не имеет параметров.

Информацию о прототипах Windows API функций, включенных в библиотеки Kernel32.dll, Gdi32.dll, User32.dll, Mpr.dll и Advapi32.dll, в MSDN для Visual Studio.NET вы можете найти, последовательно открывая следующие разделы оглавления (Contents) справки:

MSDN Library

Windows Development Win32 API SDK Documentacion Reference

В разделе Reference вы можете посмотреть описания функций, открыв один из следующих подразделов:

Вот ещё один адрес в MSDN, по которому так же имеется информация об API функциях:

MSDN Library

User Interface Design and Development SDK Documentacion Windows Shell Shell Reference Shell Functions

На следующем рисунке показан фрагмент окна справочной системы MSDN:

Вот как, например, описана в MSDN функция CopyRect:

CopyRect

The CopyRect function copies the coordinates of one rectangle to another.

BOOL CopyRect(
LPRECT lprcDst , // destination rectangle
CONST RECT* lprcSrc // source rectangle
);

Parameters

lprcDst
Pointer to the RECT structure that receives the logical coordinates of the source rectangle.
lprcSrc
Pointer to the RECT structure whose coordinates are to be copied in logical units.

Return Values

If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero.
Windows NT/2000/XP: To get extended error information, call GetLastError.

Remarks

Because applications can use rectangles for different purposes, the rectangle functions do not use an explicit unit of measure. Instead, all rectangle coordinates and dimensions are given in signed, logical values. The mapping mode and the function in which the rectangle is used determine the units of measure.

Example Code

For an example, see Using Rectangles .

Requirements

Windows NT/2000/XP: Included in Windows NT 3.1 and later.
Windows 95/98/Me: Included in Windows 95 and later.
Header: Declared in Winuser.h; include Windows.h.
Library: Use User32.lib.

Как видите, информация достаточно исчерпывающая. Функция возвращает значение типа BOOL, ей передаются два параметра: типа LPRECT и CONST RECT* - указатели на структуры типа RECT. При объявлении этой функции в Visual FoxPro вы должны указать, что первый параметр передаётся по ссылке, а второй - по значению:

DECLARE Long CopyRect IN User32.dll String @ Dst , String Src

А как я определил, что эта функция находится в библиотеке User32.dll? Очень просто. В разделе рекомендаций (Requirements ) пункт Library гласит: Use User32.lib. Подставьте вместо расширения lib расширение dll - и всё! Кстати, там же, в пункте Header, сообщается, в каком include-файле содержится описание прототипа функции.

Но это ещё не всё! Так как функция работает со структурами, то в её описании присутствует гиперссылка на структуру RECT. Щёлкните мышью по этой гиперссылке, и на экране появится подробное описание структуры.

Формирование структур в Visual FoxPro

В девятой версии Visual FoxPro существенно расширены возможности встроенных функций BINTOC и CTOBIN. Теперь эти функции можно применять для преобразования числовых данных в формат, пригодный для использования в структурах. Напомню, что функция BINTOC выполняет преобразование числа в строку, а функция CTOBIN - строки в число.

Синтаксис функции BINTOC:

BINTOC(nExpression , eFlag )

Из всех возможных значений, которые может принимать параметр eFlag , нас интересуют следующие:

Синтаксис функции CTOBIN:

CTOBIN(cExpression , eFlag )

Возможные значения параметра eFlag :

Ниже показаны примеры использования этих функций:

CTOBIN(BINTOC(1000.55,"4RS"), "4RS") && Результат: 1000 ? CTOBIN(BINTOC(-1000.55,"4RS"), "4RS") && Результат: -1000 ? CTOBIN(BINTOC(1000.55,"F"), "4N") && Результат: 1000.549987929 ? CTOBIN(BINTOC(-1000.55,"F"), "4N") && Результат: -1000.549987929 ? CTOBIN(BINTOC(1000.55,"B"), "8N") && Результат: 1000.55 ? CTOBIN(BINTOC(-1000.55,"B"), "8N") && Результат: -1000.55

В качестве примера запишем в переменную Visual FoxPro структуру RECT. Эта структура используется в рассмотренной ранее функции CopyRect для описания координат прямоугольной области. Вот как эта структура описывается в MSDN:

Typedef struct _RECT { LONG left; LONG top; LONG right; LONG bottom; } RECT, *PRECT;

Как видите, структура RECT содержит четыре поля, в каждом из которых хранится значение типа LONG. Для её формирования в Visual FoxPro понадобится строка длиной 16 байт.

Ниже показан код, в котором объявляется функция CopyRect, формируются структуры Dst и Src для передачи их как параметров функции, и затем выполняется копирование одной структуры в другую. В примере используется функция BINTOC для преобразования числа в строку:

DECLARE Long CopyRect IN WIN32API String @ Dst, String Src * Формируем структуру Src cSrc = BINTOC(nLeft,"4RS") + BINTOC(nTop,"4RS") + ; BINTOC(nRight,"4RS") + BINTOC(nBottom,"4RS") * Подготавливаем место для структуры Dst cDst = REPLICATE(CHR(0),16) nResult = CopyRect(@cDst, cSrc) && Копирование

В следующем примере показано, как, используя функцию CTOBIN, можно "разобрать" структуру, получив числовые значения её полей:

NLeft = CTOBIN(SUBSTR(cDst,1,4), "4RS") && RECT.left nTtop = CTOBIN(SUBSTR(cDst,5,4), "4RS") && RECT.top nRight = CTOBIN(SUBSTR(cDst,9,4), "4RS") && RECT.right nBottom = CTOBIN(SUBSTR(cDst,13,4), "4RS") && RECT.bottom

Структуры, содержащие указатели

Достаточно часто встречается ситуация, когда передаваемая Windows API функции структура содержит указатели. В качестве примера рассмотрим функцию StartDoc, создающую документ для печати на принтере. Вот её прототип:

Int StartDoc(HDC hdc , // handle to DC CONST DOCINFO* lpdi // contains file names);

Как видите, второй передаваемый функции параметр - это указатель на структуру DOCINFO. Вот эта структура:

Typedef struct { int cbSize ; LPCTSTR lpszDocName ; LPCTSTR lpszOutput ; LPCTSTR lpszDatatype ; DWORD fwType ; } DOCINFO, *LPDOCINFO;

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

В Visual FoxPro достаточно сложно сформировать структуру, содержащую указатели. Во-первых, нужно выделить блок памяти и получить указатель на него. Во-вторых, необходимо переписать в эту память значение переменной Visual FoxPro - таким образом, у нас будет полностью реализован механизм указателей. Последнее, что остаётся сделать - это поместить значение указателя в структуру. При этом нужно выполнить одно существенное требование: выделенная память не должна быть перемещаемой - иначе может оказаться, что наш указатель в какой-то момент будет показывать на область, к которой наши данные не имеют никакого отношения!

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

Функция GlobalAlloc получает у Windows блок памяти указанного размера и возвращает указатель на него. Вот прототип этой функции:

HGLOBAL GlobalAlloc(UINT uFlags , // атрибуты распределения памяти SIZE_T dwBytes // размер в байтах);

Параметр uFlags определяет, как будет распределяться память. В MSDN написано, что он может принимать одно из следующих значений:

Из таблицы следует, что для параметра uFlags следует использовать значение GPTR. Но как узнать, какое это значение? Найдите в MSDN описание функции GlobalAlloc и в разделе Requirements посмотрите, в каком include-файле находится её прототип. Это файл Winbase.h. Именно в нём и следует искать описание значений констант. Вот фрагмент этого файла, в котором определяются перечисленные в таблице константы:

/* Global Memory Flags */ #define GMEM_FIXED 0x0000 #define GMEM_MOVEABLE 0x0002 #define GMEM_NOCOMPACT 0x0010 #define GMEM_NODISCARD 0x0020 #define GMEM_ZEROINIT 0x0040 #define GMEM_MODIFY 0x0080 #define GMEM_DISCARDABLE 0x0100 #define GMEM_NOT_BANKED 0x1000 #define GMEM_SHARE 0x2000 #define GMEM_DDESHARE 0x2000 #define GMEM_NOTIFY 0x4000 #define GMEM_LOWER GMEM_NOT_BANKED #define GMEM_VALID_FLAGS 0x7F72 #define GMEM_INVALID_HANDLE 0x8000 #define GHND (GMEM_MOVEABLE | GMEM_ZEROINIT) #define GPTR (GMEM_FIXED | GMEM_ZEROINIT)

Следовательно, GPTR = GMEM_FIXED + GMEM_ZEROINIT = 0x0000 + 0x0040 = 0x0040.

Какой размер должен иметь выделяемый блок памяти? Конечно, равный длине строки, в которой хранится наименование документа. В следующем примере показаны действия, начиная с объявления API функции и заканчивая выделением блока памяти:

uFlags , Long dwBytes cDocumentName = "Имя печатаемого документа" && Имя документа nLenDocumentName = LEN(cDocumentName) && Длина строки hGlobal = GlobalAlloc(GPTR, nLenDocumentName)

Следующая задача - переписать содержимое переменной cDocumentName в полученный блок памяти. Воспользуемся для этого встроенной функцией Visual FoxPro SYS(2600). Вот её синтаксис:

SYS(2600, dwAddress , nLenght [, cNewString ])

Функция ведёт себя по разному в зависимости от того, указан параметр cNewString или нет.

Если параметр cNewString указан , то функция копирует nLenght байт из переменной cNewString в память по адресу, указанному в dwAddress .

Если параметр cNewString не указан , то функция возвращает nLenght байт из памяти по адресу, указанному в dwAddress .

Как видите, параметр dwAddress - это указатель .

Запишем содержимое строки cDocumentName в выделенную функцией GlobalAlloc память:

SYS(2600, hGlobal, nLenDocumentName, cDocumentName)

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

#DEFINE GPTR 0x0040 DECLARE Long GlobalAlloc IN WIN32API Long uFlags , Long dwBytes cDocumentName = "Имя печатаемого документа" nLenDocumentName = LEN(cDocumentName) hGlobal = GlobalAlloc(GPTR, nLenDocumentName) SYS(2600, dwAddress , nLenght [, cNewString ]) * Начинаем формировать структуру DOCINFO * cDocInfo - переменная, в которой формируется структура cDocInfo = BINTOC(20,"4RS") && DOCINFO.cbSize cDocInfo = cDocInfo + BINTOC(hGlobal,"4RS") && DOCINFO.lpszDocName cDocInfo = cDocInfo + REPLICATE(CHR(0),12) && Остальные поля структуры * Конец формирования структуры

Структура формируется в переменной cDocInfo . В первые четыре байта записываем число 20 - это размер структуры (поле cbSize ). Следующие четыре байта, добавляемые в переменную, - это указатель на область памяти, в которую переписано содержимое переменной cDocumentName - наименование документа. Затем к переменной добавляются ещё двенадцать нулевых байтов - это поля структуры lpszOutput , lpszDatatype и fwType , которые, согласно документации, могут игнорироваться; это означает, что поля должны иметь нулевые значения. Таким образом, получилась строка длиной 20 байтов - что и требовалось.

Особенности использования памяти

Применяя в ваших приложениях функции Windows API, вы должны помнить о том, Visual FoxPro не может управлять памятью, резервируемой этими функциями. Поэтому, если вы распределяете память, например, при помощи функции GlobalAlloc, то вы обязательно должны после использования вернуть эту память Windows, вызвав функцию GlobalFree. Для каждой API функции, резервирующей память, имеется функция - антипод, освобождающая зарезервированную память.

Вот прототип функции GlobalFree:

HGLOBAL GlobalFree(HGLOBAL hMem // указатель на блок памяти);

Функция получает только один параметр - указатель на блок памяти. Вот её объявление и использование в Visual FoxPro:

DECLARE Long GlobalFree IN WIN32API Long hGlobal GlobalFree(hGlobal)

здесь hGlobal - указатель на блок памяти, возвращаемый функцией GlobalAlloc.

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

Передача в функцию массивов

Массив с точки зрения Си - это переменная, содержащая несколько элементов одного типа. Доступ к каждому отдельному элементу массива осуществляется при помощи индекса. Все элементы массива имеют одинаковый размер, нельзя описывать массивы со смешанными типами данных. Все элементы массива хранятся в памяти последовательно, один за другим, при этом минимальное значение индекса соответствует первому элементу, а максимальное - последнему.

Массив Visual FoxPro имеет совершенно другую организацию, которая позволяет хранить в его элементах совершенно разные типы данных. Поэтому невозможно передать массив из Visual FoxPro в Windows API функцию, просто указав его имя как параметр. Более того, в команде DECLARE..DLL нет такого типа данных, как массивы.

Тем не менее выход из этого положения есть. Как и для структур, для передачи массивов используются символьные переменные. Числовые данные из массива должны быть преобразованы в строку, которую вы и передаёте как параметр в Windows API функцию. В следующем примере показан код функции ArrayToString, формирующей строку из массива. Функция получает получает массив taArray (по ссылке) и флаг tlTypeOfValue , указывающий, как нужно преобразовать значения элементов массива - как целые (tlTypeOfValue= .F.) или вещественные (tlTypeOfValue= .T.) числа:

FUNCTION ArrayToString PARAMETERS taArray, tlTypeOfValue EXTERNAL ARRAY taArray LOCAL lnLenArray, lnIndex, lcStruct, lcType lcType = IIF(tlTypeOfValue = .t., "F", "4RS") lnLenArray = ALEN(taArray) && Количество элементов в массиве lcStruct = "" FOR lnIndex = 1 TO lnLenArray lcStruct = lcStruct + BINTOC(taArray, lcType) ENDFOR RETURN lcStruct ENDFUNC

Функция работает как с одномерными, так и с двумерными массивами.

Кодировка символьных данных: форматы ANSI и UNICODE

В кодировке ANSI (применяемой в Visual FoxPro) каждый символ определяется одним байтом, то есть максимальное количество символов равно 256, что, конечно, очень мало. Например, при русификации часть стандартных символов ANSI заменяется на символы кириллицы. А в некоторых алфавитах, например, японской кане, столько символов, что одного байта для их кодировки просто недостаточно. Для поддержки таких языков, и, что более важно, для облегчения "перевода" программ на другие языки, была разработана кодировка Unicode. Каждый символ в Unicode состоит из двух байтов, что позволило расширить набор допустимых символов до 65536.

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

Встроенная функция Visual FoxPro STRCONV выполняет конвертирование строк как из формата ANSI в UNICODE, так и обратно. Вот её синтаксис:

STRCONV(cExpression , nConversionSetting [, nRegionalIdentifier [, nRegionalIDType ]])

Параметр cExpression - это строка, которую необходимо конвертировать. Параметр nConversionSetting указывает характер конвертирования. Из всех его возможных значений нас интересуют только два:

  • 5 - конвертирование из ANSI в UNICODE
  • 6 - конвертирование из UNICODE в ANSI

Необязательные параметры nRegionalIdentifier и nRegionalIDType определяют дополнительные региональные настройки и могут быть безболезненно проигнорированы.

Ниже показаны примеры использования функции STRCONV:

CUnicodeString = STRCONV(cANSIString, 5) && Конвертирование в Unicode cANSIString = STRCONV(cUnicodeString, 6) && Конвертирование в ANSI

Возможно, при прочтении этого раздела у вас сложилось впечатление, что работать с Windows API функциями в Visual FoxPro достаточно просто. И да, и нет. Например, некоторые API функции используют типы данных, не поддерживаемые в команде DECLARE..DLL, например, 64-х разрядные целые числа. Так же есть ряд функций, называемых функциями обратного вызова. В прототипе такой функции перед описанием возвращаемого типа присутствует модификатор CALLBACK. К сожалению, вы не можете использовать такие функции, по крайней мере, напрямую. Так же бывает, что структура содержит указатель на функцию - такие API в Visual FoxPro так же нельзя использовать.