Программирование на Delphi. Часть 2. Разработка Android-приложения
Продолжаем нашу серию статей по Delphi. В предыдущем материале мы скачали и установили бесплатную версию Delphi и запустили простейшую программу под Windows, сегодня наша цель: разработать первое приложение на платформе Android и запустить его на своём телефоне.
1. Подготовка среды разработки Delphi для создания Android-приложений
Установим все необходимые инструменты для разработки. Нажмите в главном меню Tools — Manage Platforms
Выберите вкладку Additional Options и там отметьте галочками Java Development Kit 1.8 и Android SDK 25.2.5 — NDK r17b. Нажмите Apply — это запустит процесс скачивания и установки компонентов.
Процесс довольно длительный, может потребоваться перезапуск среды разработки. Старайтесь не нагружать компьютер другими операциями. После успешного завершения вы увидеть надпись Operation Completed.
Нажмите Start working — среда разработки готова к созданию Android-приложений.
ЗАМЕЧАНИЕ. Я протестировал разные варианты установки компонентов Android на нескольких компьютерах и операционных системах. В том числе: когда SDK уже установлено (с Android Studio), когда компоненты ставились вручную с серверов Google, когда использовалась виртуальная машина AdoptOpenJDK. Если вы хотите пойти таким же сложным путём (ручная установка), вот некоторые параметры работающих версий: AdoptOpenJDK (Windows 64-bit OpenJDK 8 (LTS) with HotSpot JVM), Android SDK (sdkmanager «build-tools;29.0.0» «extras;google;usb_driver» «platforms;android-26» «tools» «platform-tools» ), Android NDK r17b.
Если автоматическая установка не сработает, пишите вопросы в комментариях — я постараюсь ответить и дополнить статью.
2. Подготовка проекта Android-приложения
Нажимаем на главном экране (Welcome Page) Create a new Multi-Device Application — Delphi.
Выбираем Header/Footer (шаблон приложения с шапкой и подвалом) и жмём ОК.
Создаем папку проекта, я рекомендую поместить её в каталог C:\work и назвать HelloAndroid
Внесем небольшие изменения в наше приложение. В шапке формы напишем Привет Android. Для этого нажмём на метку HeaderLabel на форме и слева внизу найдём параметр Text.
Изменим его значение на Привет Android — и увидим, что изменился текст на форме.
Запустите эту программу под Windows (нажмите F9) — вы увидите обычную форму. Программа работает.
Переключим целевую платформу для компиляции приложения на Android в правом верхнем углу экрана (дважды щёлкните названии платформы).
Без телефона запустить приложение не получится. Давайте его настроим.
3. Настройка телефона для разработки Android
К сожалению, каждый телефон настраивается по-своему. Я расскажу общую схему на примере моего Xiaomi Redmi Note 4.
Для начала нужно включить режим разработчика. Необходимо зайти в Настройки — О телефоне и семь раз тапнуть пункт — версия MIUI (для телефонов Huawei, например, нужно сделать следующее: окне «О телефоне» необходимо не менее 7 раз нажать по строчке «Номер сборки»).
После этой процедуры зайдите в пункт меню Расширенные настройки, там нажмите Для разработчиков.
В меню разработчиков нужно включить кнопку Отладка по USB и затем Установка через USB. (Нюанс: для Redmi Note 4 в этот момент должна быть вставлена SIM-карта. После включения этих опций симку можно вынуть).
На других телефона процедура выглядит примерно также. Для вашей модели телефона инструкцию включения режим разработчика ищите в Google по запросу: «как включить режим разработчика для *название телефона*» или «как включить отладку по USB для *название телефона*».
После включения этих опций берёте хороший USB-кабель (например, которым заряжаете ваш телефон) и подключаете к компьютеру. На Windows 10 драйвера не потребовались, телефоны Android система определяла сама. Для Windows 8 потребовалось скачать ADB-драйвера для конкретной модели телефона. Они также гуглятся. Скачать подошедший мне adb-драйвер можно с нашего сайта.
При подключении устройства телефон может попросить разрешить отладку через USB для конкретного компьютера. Ставим галку и соглашаемся, нажав ОК. Если не успели, вытаскиваем кабель и вставляем снова.
Но может и не попросить, зависит от модели. Теперь важно, чтобы телефон увидела Delphi.
Скорее всего, после успешного подключения среда разработки подхватит устройство автоматически. Если этого не произойдёт, попробуйте нажать кнопку Refresh devices (обновление устройств). Справа от надписи Android вы увидите название вашего смартфона.
Всё готово к запуску приложения.
4. Запуск приложения на Android
Нажмите F9 (зелёный треугольник в панели управления) для запуска приложения.
Пока программа будет компилироваться, внимательно следите за вашим устройством Android. После создания файла apk (установочного файла Android-приложения) на компьютере он будет передан на ваш смартфон. Например, мой Xiaomi Redmi Note 4 даёт только 10 секунд на подтверждение установки. Если вы не уложитесь в это время (не успеете нажать Установить), установка будет отклонена и её нужно будет запускать заново, нажав F9 в Delphi.
К счастью, можно запомнить этот выбор для конкретного приложения. Ставьте галку (на скриншоте я ещё это не сделал) и нажимайте Установить, когда увидите подобный экран:
Подождите ещё немного — и приложение запустится! Ура!
Значок приложения появится также на рабочем столе вашего смартфона — вы можете запустить его снова или удалить. Для продолжения работы удалять его не нужно, при запуске новых версий из среды разработки Delphi будет заменять его на более свежую версию.
НЮАНС. Если один раз приложение установилось, а потом появляется ошибка, попробуйте удалить его с устройства. Это может быть связано с изменениями настроек телефона, подключением телефона к другому компьютеру и т.д.
Пока приложение совсем простое. Усовершенствуем его — добавим ввод данных.
5. Ввод данных в Android-приложении
Добавьте на форму текстовое поле, для этого найдите компонент TEdit в палитре компонентов (воспользуйтесь поиском, как показано на скриншоте) и перетащите его в верхнюю часть формы.
Под ним добавьте компонент кнопки TButton, как в предыдущем уроке. На самой кнопке напишите слово Приветствие, использовав его поле Text в инспекторе объектов. Ваш экран будет выглядеть примерно так:
И для текстового поля, и для кнопки измените в инспекторе объектов следующие свойства:
Элементы прижмутся к верхней части формы и станут в 2 раза выше, это сделает работу с программой удобнее (особенно на Android). Выглядеть в среде разработки это будет вот так:
6. Нажатие кнопки
Теперь напишем обработчик события — нажатия кнопки. Для этого дважды щёлкните на кнопке с надписью Приветствие — откроется редактор кода. Впишите (или скопируйте с этой страницы) туда следующий текст:
Работа с Bluetooth в Delphi. Часть 3.
Итак, в предыдущих частях мы научились получать список локальных радиомодулей Bluetooth и удаленных устройств Bluetooth.
Нам осталось научиться получать список сервисов, предоставляемых удаленным устройством и управлять локальными радиомодулями.
Также необходимо разобраться, как же все-таки передаются данные между различными устройствами Bluetooth.
В этой части (а она будет самой длинной и информативной) мы создадим программу, которая поможет нам обобщить полученную информацию и покажет, как использовать новые функции, которые здесь будут описаны.
К сожалению, документация Microsoft по Bluetooth API и работе с Bluetooth настолько скудна (у меня получилось 50 страниц в Word с оформлением), а примеров они не предоставляют, что из нее очень трудно понять, как все-таки работает эта технология.
Когда я только начинал изучать этот предмет, я перерыл весь Internet, но так ничего вразумительного не нашел.
Поэтому, здесь мне хочется дать наиболее полную и подробную информацию об этом вопросе, чтобы вы не столкнулись с той же проблемой отсутствия информации.
Итак, приступим.Создание проекта
Главную и пока единственную форму, назовем fmMain. Заголовок BTWork.
Теперь нам понадобятся файл JwaBluetoothAPI.pas, JwaBtHDef.pas и JwaBthSdpDef.pas. Их можно найти в примерах из предыдущих частей или в библиотеке BTClasses.
Чтобы не тянуть с собой все остальные файлы из JWA, давайте, немного исправим эти. Найдите в них строку
и замените JwaWindows на Windows.
Далее удалить из них строки
И в файле JwaBluetoothAPI удалите все, что находится между и вместе с этими DEF. И в конце этого файле удалите .
Далее, в JwaBluetoothAPI.pas после
implementation uses JwaWinDLLNames;
const btapi = ‘irprops.cpl’;
Да простят нас ребята, которые эту библиотеку писали!
Все эти действия я делал для того, чтобы уменьшить архив примера. Да и не нужно тянуть за собой много лишнего. Хотя сама библиотека весьма полезна. Один модуль JwaWindows чего стоит. Там есть очень много интересного.
После того, как мы подправили эти модули, давайте добавим их в наш проект. Готово?
В этом приложении мы будем получать список локальных радиомодулей, устройств, к ним присоединенных, список сервисов, предоставляемых устройствами. Также мы должны управлять радиомодулями и научиться проходить авторизацию.
Приступаем.Оформление главной формы
На главную форму поместите компонент TPanel и установите следующие свойства:
Свойство | Значение |
Align | alTop |
Caption | |
Name | Panel |
Далее поместите компонент TTreeView и установите свойства как в таблице:
Свойство | Значение |
Align | alLeft |
Cursor | crHandPoint |
HideSelection | False |
HotTrack | True |
Name | TreeView |
ReadOnly | True |
Правее TTreeView поместим TSplitter и установим следующие его свойства:
Свойство | Значение |
Name | Splitter |
Width | 5 |
И, наконец, помещаем компонент TListView еще правее TSplitter. Устанавливаем его свойства как в таблице:
Свойство | Значение |
Align | alClient |
ColumnClick | False |
Cursor | crHandPoint |
GridLines | True |
HideSelection | False |
HotTrack | True |
Name | ListView |
ReadOnly | True |
RowSelect | True |
ShowWorkAreas | True |
ViewStyle | vsReport |
На TPanel поместим кнопку TButton.
Свойство | Значение |
Caption | Refresh |
Name | btRefresh |
Теперь мы готовы писать программу.Пишем код
При старте нашей программы желательно, чтобы сразу заполнялся TreeView. В нем будут показаны модули Bluetooth и устройства, которые к ним подключены.
Для этого в обработчике OnCreate формы fmMain напишем такой код: procedure TfmMain.FormCreate(Sender: TObject); begin btRefresh.Click; end;
В uses нашего модуля, который относится к главной форме, допишем: implementation // Уже написано. uses // Дописать! JwaBluetoothAPIs, Windows, SysUtils, Dialogs;
Ниже добавим функцию: // Преобразует адрес из внутреннего формата (dword) в строку, // принятую для представления адресов устройств Bluetooth. function BTAdrToStr(const Adr: BLUETOOTH_ADDRESS): string; var Loop: byte; begin Result := IntToHex(Adr.rgBytes[0], 2); for Loop := 1 to 5 do Result := IntToHex(Adr.rgBytes[Loop], 2) + ‘:’ + Result; end;
Здесь хочу привести описание используемых структур, так как ранее я их не описывал:
BLUETOOTH_FIND_RADIO_PARAMS
BLUETOOTH_FIND_RADIO_PARAMS = record
dwSize : DWORD;
end;
BLUETOOTH_DEVICE_SEARCH_PARAMS
BLUETOOTH_DEVICE_SEARCH_PARAMS = record
dwSize : DWORD;
fReturnAuthenticated : BOOL;
fReturnRemembered : BOOL;
fReturnUnknown : BOOL;
fReturnConnected : BOOL;
fIssueInquiry : BOOL;
cTimeoutMultiplier : UCHAR;
hRadio : THandle;
end;
BLUETOOTH_DEVICE_INFO
BLUETOOTH_RADIO_INFO
В этом коде появилось три новые функции, которые выделены комментариями. Вот они:
BluetoothIsConnectable
Эта функция определяет, возможно ли подключение к указанному радиомодулю.
Объявление функции:
function | BluetoothIsConnectable( |
const | hRadio : THandle): BOOL; stdcall; |
BluetoothIsDiscoverable
Функция определяет, будет ли виден указанный радиомодуль другим при поиске. Если просматриваются все радиомодули, то вернет TRUE, если хотя бы один разрешает обнаружение.
Объявление функции:
function | BluetoothIsDiscoverable( |
const | hRadio : THandle): BOOL; stdcall; |
BluetoothEnumerateInstalledServices
Получает список GUID сервисов, предоставляемых устройством. Если параметр hRadio=0, то просматривает все радиомодули.
Объявление функции:
function | BluetoothEnumerateInstalledServices( hRadio : THandle; pbtdi : __PBLUETOOTH_DEVICE_INFO; |
var | pcServices : dword; pGuidServices : PGUID): dword; stdcall; |
Посмотрите на код получения списка сервисов: // Получаем размер массива сервисов ServiceCount := 0; Services := nil; hRadio := ASelected.Parent.ImageIndex; Info := ASelected.Data; // NEW FUNCTION BluetoothEnumerateInstalledServices(hRadio, Info, ServiceCount, nil); // Выделяем память. SetLength(Services, ServiceCount); // Получаем список сервисов BluetoothEnumerateInstalledServices(hRadio, Info, ServiceCount, PGUID(Services));
Сначала мы вызываем функцию с pcServices=0 и pGuidServices=nil для того, чтобы получить количество сервисов, предоставляемых устройством.
Потом выделяем память (SetLength()) и только затем вызываем функцию с реальными параметрами и получаем список сервисов.
Еще важное замечание. В файле JwaBluetoothAPIs.pas параметр pbtdi имеет тип PBLUETOOTH_DEVICE_INFO, который раскрывается в BLUETOOTH_DEVICE_INFO. Заметьте, что это НЕ УКАЗАТЕЛЬ. Это неверно, так как в исходном виде функция требует именно указатель. Поэтому, я ввел тип type __PBLUETOOTH_DEVICE_INFO = ^PBLUETOOTH_DEVICE_INFO
Так что ИСПОЛЬЗУЙТЕ файл из примера, а не из исходной библиотеки. Иначе получите нарушение доступа к памяти.
Комментарий к коду: мы перечитываем информацию об устройстве, так как за время, пока мы любуемся программой, могли произойти различные события: устройство отключили, отменили авторизацию и т.п. А мы хотим иметь самую свежую информацию об устройстве.
В принципе то, что описано выше, мы уже знали, кроме двух указанных функций.
Давайте расширим возможности нашего приложения. Добавим функции запрета/разрешения обнаружения радиомодуля и запрета/разрешения подключения к нему.
BluetoothEnableIncomingConnections и BluetoothEnableDiscoverable
Поместим на форму компонент TactionList и изменим его свойства как показано в таблице.
Свойство | Значение |
Name | ActionList |
Теперь два раза щелкнем по ActionList и в появившемся окне редактора свойств добавим две TAction со следующими свойствами:
Свойство | Значение |
Caption | Connectable |
Name | acConnectable |
Свойство | Значение |
Caption | Discoverable |
Name | acDiscoverable |
На панель Panel добавим две TButton и установим свойства:
Свойство | Значение |
Action | acConnectable |
Name | btConnectable |
Свойство | Значение |
Action | acDiscoverable |
Name | btDiscoverable |
Теперь обработчик события OnExecute для acConnectable: procedure TfmMain.acConnectableExecute(Sender: TObject); var SelectedItem: TListItem; begin SelectedItem := ListView.Selected; if Assigned(SelectedItem) then if not BluetoothEnableIncomingConnections(Integer(SelectedItem.Data), TAction(Sender).Caption = ‘Not conn.’) then MessageDlg(‘Unable to change Radio state’, mtError, [mbOK], 0) else TreeViewChange(TreeView, TreeView.Selected); end;
Такой же обработчик напишем и для OnExecute acDiscoverable. procedure TfmMain.acConnectableExecute(Sender: TObject); var SelectedItem: TListItem; begin SelectedItem := ListView.Selected; if Assigned(SelectedItem) then if not BluetoothEnableDiscovery(Integer(SelectedItem.Data), TAction(Sender).Caption = ‘Not disc.’) then MessageDlg(‘Unable to change Radio state’, mtError, [mbOK], 0) else TreeViewChange(TreeView, TreeView.Selected); end;
Важно: если Windows сам использует радиомодуль, то он не даст поменять статус, хотя и функция выполнится без ошибок!
Здесь мы ввели две новые функции:
BluetoothEnableInfomingConnection
Функция разрешает/запрещает подключения к локальному радиомодулю Bluetooth.
Объявление функции:
function | BluetoothEnableIncomingConnections( hRadio : THandle; fEnabled : BOOL): BOOL; stdcall; |
BluetoothEnableDiscovery
Функция разрешает/запрещает обнаружение локального радиомодуля Bluetooth.
Объявление функции:
function | BluetoothEnableDiscovery( hRadio : THandle; fEnabled : BOOL): BOOL; stdcall; |
Вывод окна свойств устройства
Теперь давайте научимся выводить системное окно свойств устройства Bluetooth. Для этого добавим к ActionList еще один TAction с такими свойствами:
Свойство | Значение |
Caption | Property |
Name | acProperty |
Добавим на Panel кнопку TButton с такими свойствами:
Свойство | Значение |
Action | acProperty |
Name | btProperty |
Теперь напишем такой обработчик событий OnUpdate у acProperty: procedure TfmMain.acPropertyUpdate(Sender: TObject); var SelectedNode: TTreeNode; SelectedItem: TListItem; begin SelectedNode := TreeView.Selected; SelectedItem := ListView.Selected; TAction(Sender).Enabled := Assigned(SelectedNode) and Assigned(SelectedItem) and (SelectedNode.ImageIndex > 0); end;
И обработчик OnExecute для нее же: procedure TfmMain.acPropertyExecute(Sender: TObject); var Info: BLUETOOTH_DEVICE_INFO; begin Info := BLUETOOTH_DEVICE_INFO(ListView.Selected.Data^); BluetoothDisplayDeviceProperties(Handle, Info); end;
Важно: в исходном виде в файле JwaBluetoothAPIs функция BluetoothDisplayDeviceProperties объявлена неверно. Второй параметр должен быть указателем, а там он передается как структура. Я исправил функцию так, чтобы он передавался как var-параметр (по ссылке). Используйте модуль JwaBluetoothAPIs из этого примера, чтобы не возникало ошибок доступа к памяти.
Важно: ни в этой процедуре, ни ранее, ни далее я не провожу проверку ошибок, чтобы не загромождать код лишними подробностями. В реальном приложении НЕОБХОДИМО проверять возвращаемые функциями значения и указатели.
Итак, в этом коде есть новая функция:
BluetoothDisplayDeviceProperty
Функция выводит стандартное окно свойств устройства Bluetooth.
Объявление функции:
function | BluetoothEnableDiscovery( hwndParent : HWND; |
var | pbtdi : PBLUETOOTH_DEVICE_INFO): BOOL; stdcall; |
Важно: в оригинале (см. примечание выше) функция выглядит так: function BluetoothEnableDiscovery( hwndParent : HWND; pbtdi : PBLUETOOTH_DEVICE_INFO): BOOL; stdcall;
Это неверно, так как в документации Microsoft указано, что параметр pbtdi должен передаваться как указатель (что подразумевает запись PBLUETOOTH_DEVICE_INFO), но как я писал выше, этот тип ошибочен. Он не является указателем. Я изменил функцию так, как показано выше (так она и должна быть, если не менять определение типа).
Выбор устройства
Рассмотрим, как вызвать окно диалога выбора устройства.
Добавим в наш проект на Panel еще одну кнопку TButton, и установим ее свойства как в таблице:
Свойство | Значение |
Caption | Select |
Name | btSelect |
Напишем такой обработчик события OnClick у этой кнопки: procedure TfmMain.btSelectClick(Sender: TObject); var ASelParams: BLUETOOTH_SELECT_DEVICE_PARAMS; ASelParamsSize: dword; begin ASelParamsSize := SizeOf(BLUETOOTH_SELECT_DEVICE_PARAMS); FillChar(ASelParams, ASelParamsSize, 0); with ASelParams do begin dwSize := ASelParamsSize; hwndParent := Handle; fShowRemembered := True; fAddNewDeviceWizard := True; end; BluetoothSelectDevices(@ASelParams); BluetoothSelectDevicesFree(@ASelParams); end;
В этой части кода две новые функции.
BluetoothSelectDevices
Функция разрешает/запрещает обнаружение локального радиомодуля Bluetooth.
Объявление функции:
function | BluetoothSelectDevices( pbtsdp : PBLUETOOTH_SELECT_DEVICE_PARAMS): BOOL; stdcall; |
Если функция вернула TRUE, то пользователь выбрал устройства. Pbtsdp^.pDevices будет указывать на корректные данные. После вызова необходимо проверить флаги fAuthenticated and fRemembered, чтобы удостовериться в корректности данных. Для освобождения памяти используйте функцию BluetoothSelectDevicesFree, только если функция вернет TRUE.
BLUETOOTH_SELECT_DEVICE_PARAMS
Объявление функции: BLUETOOTH_SELECT_DEVICE_PARAMS = record
dwSize : DWORD;
cNumOfClasses : ULONG;
prgClassOfDevices : PBlueToothCodPairs;
pszInfo : LPWSTR;
hwndParent : HWND;
fForceAuthentication : BOOL;
fShowAuthenticated : BOOL;
fShowRemembered : BOOL;
fShowUnknown : BOOL;
fAddNewDeviceWizard : BOOL;
fSkipServicesPage : BOOL;
pfnDeviceCallback : PFN_DEVICE_CALLBACK;
pvParam : Pointer;
cNumDevices : DWORD;
pDevices : __PBLUETOOTH_DEVICE_INFO;
end;
BluetoothSelectDevicesFree
Эта функция должна вызываться, только если вызов BluetoothSelectDevices был успешен. Эта функция освобождает память и ресурсы, задействованные функцией BluetoothSelectDevices в структуре BLUETOOTH_SELECT_DEVICE_PARAMS.
Объявление функции:
function | BluetoothSelectDevices( pbtsdp : PBLUETOOTH_SELECT_DEVICE_PARAMS): BOOL; stdcall; |
Управление сервисами
Для управления сервисами Microsoft Bluetooth API предоставляет функцию:
BluetoothSetServiceState
Объявление функции:
function | BluetoothSetServiceState( hRadio : Thandle; |
var | pbtdi : PBLUETOOTH_DEVICE_INFO; |
const | pGuidService : TGUID; dwServiceFlags : DWORD): DWORD; stdcall; |
Важно: в оригинале (см. примечание выше) функция выглядит так: function BluetoothSetServiceState( hRadio : Thandle; pbtdi : PBLUETOOTH_DEVICE_INFO; const pGuidService : TGUID; dwServiceFlags : DWORD): DWORD; stdcall;
Это неверно, так как в документации Microsoft указано, что параметр pbtdi должен передаваться как указатель (что подразумевает запись PBLUETOOTH_DEVICE_INFO), но как я писал выше, этот тип ошибочен. Он не является указателем. Я изменил функцию так, как показано выше (так она и должна быть, если не менять определение типа).
Как использовать функцию? Давайте добавим к ActionList еще одну TAction с такими свойствами:
Свойство | Значение |
Caption | Disable |
Name | acEnable |
И добавим на Panel еще одну кнопку TButton, установив у нее следующие свойства:
Свойство | Значение |
Action | acEnable |
Name | btEnable |
А в обработчике OnExecute для acEnable такой код: procedure TfmMain.acEnableExecute(Sender: TObject); var GUID: TGUID; begin GUID := StringToGUID(ListView.Selected.Caption); BluetoothSetServiceState(TreeView.Selected.Parent.ImageIndex, BLUETOOTH_DEVICE_INFO(TreeView.Selected.Data^), GUID, BLUETOOTH_SERVICE_DISABLE); end;
Важно: после нажатия на кнопку btEnable сервис будет удален из системы. Включить его можно будет через окно свойств устройства Bluetooth.
Как определять отключенные сервисы рассмотрим в серии про передачу данных через Bluetooth.
Удаление устройств
Для удаления устройств используется функция:
BluetoothRemoveDevice
Функция удаляет авторизацию между компьютером и устройством Bluetooth. Также очищает кэш-записи об этом устройстве.
Объявление функции:
function | BluetoothRemoveDevice( |
var | pAddress : BLUETOOTH_ADDRESS): DWORD; stdcall; |
Давайте попробуем. Добавим в ActionList TAction со следующими свойствами:
Свойство | Значение |
Caption | Remove |
Name | acRemove |
И на Panel кнопку TButton со свойствами:
Свойство | Значение |
Action | acRemove |
Name | btRemove |
В обработчике OnUpdate для acRemove напишем следующий код: procedure TfmMain.acRemoveUpdate(Sender: TObject); begin TAction(Sender).Enabled := acProperty.Enabled; end;
А для события OnExecute такой код: procedure TfmMain.acRemoveExecute(Sender: TObject); var Info: BLUETOOTH_DEVICE_INFO; Res: dword; begin Info := BLUETOOTH_DEVICE_INFO(ListView.Selected.Data^); Res := BluetoothRemoveDevice(Info.Address); if Res <> ERROR_SUCCESS then MessageDlg(‘Device not found’, mtError, [mbOK], 0); TreeViewChange(TreeView, TreeView.Selected); end;
Процедура выполняется достаточно долго, так что не думайте, что программа зависла.
Важно: устройство удаляется из списка. Однако если уже иметь адрес устройства, то можно получить о нем информацию.
Есть еще одна функция, которая связана с BluetoothRemoveDevice. Это:
BluetoothUpdateDeviceRecord
Функция обновляет данные об устройстве в кэше.
Объявление функции:
function | BluetoothUpdateDeviceRecord( |
var | pbtdi : BLUETOOTH_DEVICE_INFO): DWORD; stdcall; |
Попробуем использовать и ее. Схема стандартная: TAction к ActionList, TButton на Panel:
Свойство | Значение |
Caption | Update |
Name | acUpdate |
Свойство | Значение |
Action | acUpdate |
Name | btUpdate |
Код: procedure TfmMain.acUpdateUpdate(Sender: TObject); begin TAction(Sender).Enabled := acProperty.Enabled; end;
procedure TfmMain.acUpdateExecute(Sender: TObject); var Info: BLUETOOTH_DEVICE_INFO; Res: dword; NewName: string; begin if InputQuery(‘Имя устройства’, ‘Новое имя’, NewName) then begin lstrcpyW(Info.szName, PWideChar(WideString(NewName))); Res := BluetoothUpdateDeviceRecord(Info); if Res <> ERROR_SUCCESS then RaiseLastOsError; TreeViewChange(TreeView, TreeView.Selected); end; end;
Как видите, все просто.
Итак, удалять устройства мы умеем. Давайте теперь научимся их добавлять. Для этого Bluetooth API предоставляет две функции:
BluetoothAuthenticateDevice
Функция отправляет запрос на авторизацию удаленному устройству Bluetooth. Есть два режима авторизации: «Wizrd mode» и «Blind Mode«.
«Wizard Mode» запускается, когда параметр pszPasskey = nil. В этом случае открывается окно «Мастера подключения». У пользователя будет запрошен пароль, который будет отправлен в запросе на авторизацию удаленному устройству. Пользователь будет оповещен системой об успешном или не успешном выполнении авторизации и получит возможность попытаться авторизировать устройства еще раз.
«Blind Mode» вызывается, когда pszPasskey <> nil. В этом случае пользователь не увидит никакого мастера. Вам необходимо программно запросить код авторизации (pszPasskey) и уведомить пользователя о результате.
Объявление функции:
function | BluetoothAuthenticateDevice( hwndParent : HWND; hRadio : THandle; pbtdi : BLUETOOTH_DEVICE_INFO; pszPasskey : PWideChar; ulPasskeyLength : ULONG): DWORD; stdcall; |
Для «Blind Mode» соответствие кодов ошибок Bluetooth кодам ошибок Win32 приведено в таблице:
Bluetooth | Win32 |
BTH_ERROR_SUCCESS | ERROR_SUCCESS |
BTH_ERROR_NO_CONNECTION | ERROR_DEVICE_NOT_CONNECTED |
BTH_ERROR_PAGE_TIMEOUT | WAIT_TIMEOUT |
BTH_ERROR_HARDWARE_FAILURE | ERROR_GEN_FAILURE |
BTH_ERROR_AUTHENTICATION_FAILURE | ERROR_NOT_AUTHENTICATED |
BTH_ERROR_MEMORY_FULL | ERROR_NOT_ENOUGH_MEMORY |
BTH_ERROR_CONNECTION_TIMEOUT | WAIT_TIMEOUT |
BTH_ERROR_LMP_RESPONSE_TIMEOUT | WAIT_TIMEOUT |
BTH_ERROR_MAX_NUMBER_OF_CONNECTIONS | ERROR_REQ_NOT_ACCEP |
BTH_ERROR_PAIRING_NOT_ALLOWED | ERROR_ACCESS_DENIED |
BTH_ERROR_UNSPECIFIED_ERROR | ERROR_NOT_READY |
BTH_ERROR_LOCAL_HOST_TERMINATED_CONNECTION | ERROR_VC_DISCONNECTED |
BluetoothAuthenticateMultipleDevices
Позволяет авторизироваться сразу на нескольких устройствах при помощи одной копии «Мастера авторизации».
Объявление функции:
function | BluetoothAuthenticateMultipleDevices( hwndParent : HWND; hRadio : THandle; cDevices : DWORD; rgpbtdi : __PBLUETOOTH_DEVICE_INFO): DWORD; stdcall; |
Важно: в оригинале функция выглядит вот так: function BluetoothAuthenticateMultipleDevices( hwndParent : HWND; hRadio : THandle; cDevices : DWORD; pbtdi : PBLUETOOTH_DEVICE_INFO): DWORD; stdcall;
Это неверно, так как в документации Microsoft указано, что параметр rgpbtdi должен передаваться как указатель (что подразумевает запись PBLUETOOTH_DEVICE_INFO), но как я писал выше, этот тип ошибочен. Он не является указателем. Я изменил функцию так, как показано выше. По поводу типа __PBLUETOOTH_DEVICE_INFO я писал выше.
Описывать с примером, как использовать эти функции, не буду, так как они тривиальны (если вы прочитали все вышеизложенное). Остались последние три функции, которые мы не рассмотрели:
BluetoothRegisterForAuthentication
Регистрирует функцию обратного вызова, которая будет вызываться на запрос устройства об авторизации. Если несколько приложений зарегистрировало такую функцию, то будет вызвана функция в последнем приложении.
Объявление функции:
function | BluetoothRegisterForAuthentication( |
var | pbtdi : PBLUETOOTH_DEVICE_INFO; |
var | phRegHandle : HBLUETOOTH_AUTHENTICATION_REGISTRATION; pfnCallback : PFN_AUTHENTICATION_CALLBACK; pvParam : Pointer): DWORD; stdcall; |
BluetoothUnregisterAuthentication
Удаляет функцию обратного вызова, зарегистрированную функцией BluetoothRegisterForAuthentication и закрывает Handle.
Объявление функции:
function | BluetoothUnregisterAuthentication( hRegHandle : HBLUETOOTH_AUTHENTICATION_REGISTRATION): BOOL; stdcall; |
BluetoothSendAuthenticationResponse
Эта функция должна вызываться из функции обратного вызова при запросе авторизации удаленным устройством для передачи PIN.
Объявление функции:
function | BluetoothSendAuthenticationResponse( hRadio : THandle; pbtdi : PBLUETOOTH_DEVICE_INFO; pszPasskey : LPWSTR): DWORD; stdcall; |
И, наконец, функция обратного вызова:
PFN_AUTHENTICATION_CALLBACK
Описание этой функции дано выше. Здесь приведу лишь определеннее.
Объявление функции:
PFN_AUTHENTICATION_CALLBACK = | |
function | (pvParam : Pointer; pDevice : PBLUETOOTH_DEVICE_INFO): BOOL; stdcall; |
На этот раз все. Мы рассмотрели все функции Bluetooth API от Microsoft. Также мы научились управлять устройствами и радиомодулями Bluetooth, проводить авторизацию и получать информацию об этих устройствах.
Но актуальным остается вопрос, который мне многие задают. Как же все-таки передавать данные между устройствами Bluetooth?
Ответ на этот вопрос читайте в следующей серии статей «Передача данных через Bluetooth«.
Конечно, можно было всю эту информацию уместить в эти статьи, но объем ее не сравним с предоставленным здесь. Так что наберитесь терпения. Я постараюсь надолго не задерживать с выходом новой серии.
Полностью рабочий пример, рассмотренный в этой статье, вы можете скачать здесь.
Я буду рад любым замечаниям и пожеланиям по данной теме.