delphi приложение для android bluetooth

Программирование на Delphi. Часть 2. Разработка Android-приложения

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

1. Подготовка среды разработки Delphi для создания Android-приложений

Установим все необходимые инструменты для разработки. Нажмите в главном меню Tools — Manage Platforms

img 2020 01 13 16 29 08

Выберите вкладку Additional Options и там отметьте галочками Java Development Kit 1.8 и Android SDK 25.2.5 — NDK r17b. Нажмите Apply — это запустит процесс скачивания и установки компонентов.

img 2020 01 13 16 29 47

Процесс довольно длительный, может потребоваться перезапуск среды разработки. Старайтесь не нагружать компьютер другими операциями. После успешного завершения вы увидеть надпись Operation Completed.

img 2020 01 13 17 02 19Нажмите 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.img 2019 12 16 18 19 28

Выбираем Header/Footer (шаблон приложения с шапкой и подвалом) и жмём ОК.

img 2019 12 16 18 20 18

Создаем папку проекта, я рекомендую поместить её в каталог C:\work и назвать HelloAndroidimg 2019 12 16 18 21 08

Внесем небольшие изменения в наше приложение. В шапке формы напишем Привет Android. Для этого нажмём на метку HeaderLabel на форме и слева внизу найдём параметр Text.

Изменим его значение на Привет Android — и увидим, что изменился текст на форме.

img 2019 12 16 18 24 39

Запустите эту программу под Windows (нажмите F9) — вы увидите обычную форму. Программа работает.

Переключим целевую платформу для компиляции приложения на Android в правом верхнем углу экрана (дважды щёлкните названии платформы).

img 2019 12 16 18 25 12

Без телефона запустить приложение не получится. Давайте его настроим.

3. Настройка телефона для разработки Android

К сожалению, каждый телефон настраивается по-своему. Я расскажу общую схему на примере моего Xiaomi Redmi Note 4.

Для начала нужно включить режим разработчика. Необходимо зайти в Настройки — О телефоне и семь раз тапнуть пункт — версия MIUI (для телефонов Huawei, например, нужно сделать следующее: окне «О телефоне» необходимо не менее 7 раз нажать по строчке «Номер сборки»).

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

screenshot 2020 01 17 11 35 53 275 com.android.settings

В меню разработчиков нужно включить кнопку Отладка по USB и затем Установка через USB. (Нюанс: для Redmi Note 4 в этот момент должна быть вставлена SIM-карта. После включения этих опций симку можно вынуть).

screenshot 2020 01 17 11 36 00 816 com.android.settings

На других телефона процедура выглядит примерно также. Для вашей модели телефона инструкцию включения режим разработчика ищите в Google по запросу: «как включить режим разработчика для *название телефона*» или «как включить отладку по USB для *название телефона*».

После включения этих опций берёте хороший USB-кабель (например, которым заряжаете ваш телефон) и подключаете к компьютеру. На Windows 10 драйвера не потребовались, телефоны Android система определяла сама. Для Windows 8 потребовалось скачать ADB-драйвера для конкретной модели телефона. Они также гуглятся. Скачать подошедший мне adb-драйвер можно с нашего сайта.

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

screenshot 2020 01 13 13 17 40 899 com.android.systemuiНо может и не попросить, зависит от модели. Теперь важно, чтобы телефон увидела Delphi.
Скорее всего, после успешного подключения среда разработки подхватит устройство автоматически. Если этого не произойдёт, попробуйте нажать кнопку Refresh devices (обновление устройств). Справа от надписи Android вы увидите название вашего смартфона.

img 2020 01 17 12 37 05

Всё готово к запуску приложения.

4. Запуск приложения на Android

Нажмите F9 (зелёный треугольник в панели управления) для запуска приложения.

Пока программа будет компилироваться, внимательно следите за вашим устройством Android. После создания файла apk (установочного файла Android-приложения) на компьютере он будет передан на ваш смартфон. Например, мой Xiaomi Redmi Note 4 даёт только 10 секунд на подтверждение установки. Если вы не уложитесь в это время (не успеете нажать Установить), установка будет отклонена и её нужно будет запускать заново, нажав F9 в Delphi.

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

screenshot 2020 01 13 13 18 53 565 com.miui .securitycenter

Подождите ещё немного — и приложение запустится! Ура!

screenshot 2020 01 13 17 13 35 332 com.embarcadero.headerfooterapplicationЗначок приложения появится также на рабочем столе вашего смартфона — вы можете запустить его снова или удалить. Для продолжения работы удалять его не нужно, при запуске новых версий из среды разработки Delphi будет заменять его на более свежую версию.

НЮАНС. Если один раз приложение установилось, а потом появляется ошибка, попробуйте удалить его с устройства. Это может быть связано с изменениями настроек телефона, подключением телефона к другому компьютеру и т.д.

screenshot 2020 01 13 17 08 05 755 com.miui .homeПока приложение совсем простое. Усовершенствуем его — добавим ввод данных.

5. Ввод данных в Android-приложении

Добавьте на форму текстовое поле, для этого найдите компонент TEdit в палитре компонентов (воспользуйтесь поиском, как показано на скриншоте) и перетащите его в верхнюю часть формы.

img 2020 01 13 17 22 05

Под ним добавьте компонент кнопки TButton, как в предыдущем уроке. На самой кнопке напишите слово Приветствие, использовав его поле Text в инспекторе объектов. Ваш экран будет выглядеть примерно так: img 2020 01 13 17 27 12

И для текстового поля, и для кнопки измените в инспекторе объектов следующие свойства:

Элементы прижмутся к верхней части формы и станут в 2 раза выше, это сделает работу с программой удобнее (особенно на Android). Выглядеть в среде разработки это будет вот так:

img 2020 01 17 13 31 59

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«.

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

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

Я буду рад любым замечаниям и пожеланиям по данной теме.

Источник

Понравилась статья? Поделить с друзьями:
Добавить комментарий
  • Как сделать успешный бизнес на ритуальных услугах
  • Выездной кейтеринг в России
  • Риски бизнеса: без чего не обойтись на пути к успеху
  • delphi при закрытии формы
  • delphi поменять иконку приложения