Работа с Bluetooth в Delphi

Часть 3

© 2006 Петриченко Михаил,
Soft Service Company

Вступление

И так, в предыдущих частях мы научились получать список локальных радиомодулей Bluetooth и удаленных устройств Bluetooth. Нам осталось научиться получать список сервисов, предоставляемых удаленным устройством и управлять локальными радиомодулями. Так же, необходимо разобраться, как же все-таки передаются данные между различными устройствами Bluetooth.

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

Прежде, чем мы приступим, давайте определимся в терминах. Microsoft в своей документации вводит два термина: Radio и Device. Radio – это локальный радиомодуль Bluetooth (USB-брелок, интегрированное решение, в общем то, что установлено на вашем компьютере). Device – это то устройство Bluetooth с которым вы хотите обмениваться информацией. Будь то телефон, КПК, гарнитура или еще что-то. Важно понимать, что если мы пишем программу для PDA, то когда она работает на PDA - его модуль Bluetooth будет Radio, а компьютер - Device. Если же она работает на компьютере, то компьютерный модуль – Radio, а PDA – Device.

Что мы знаем

К сожалению, документация Microsoft по Bluetooth API и работе с Bluetooth настолько скудна (у меня получилось 50 страниц в Word с оформлением), а примеров они вообще не предоставляют, что из нее очень трудно понять, как же все-таки работает эта технология.

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

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

И так, приступим.

Создание проекта

Давайте создадим в Delphi новый проект и сохраним его под именем BTWork, а модуль – под именем Main.

Главную и пока единственную форму, назовем fmMain. Заголовок BTWork.

Теперь нам понадобятся файл JwaBluetoothAPI.pas, JwaBtHDef.pas и JwaBthSdpDef.pas. Их можно найти в примерах из предыдущих частей или в библиотеке BTClasses.

Для того, чтобы не тянуть с собой все остальные файлы из JWA, давайте эти чуть-чуть исправим. Найдите в них строку

uses
  JwaWindows
и замените JwaWindows на Windows.

Далее удалить из них строки

{$WEAKPACKAGEUNIT}

{$HPPEMIT ''}
{$HPPEMIT '#include "bluetoothapis.h"'}
{$HPPEMIT ''}

{$I jediapilib.inc}

И в файле JwaBluetoothAPI удалите все, что находится между {$IFDEF DYNAMIC_LINK} и {$ELSE} вместе с этими DEF. И в конце этого файле удалите {$ENDIF}.

Далее, в JwaBluetoothAPI.pas после

implementation

uses
  JwaWinDLLNames;
Напишите
const
  btapi = 'irprops.cpl';

Да простят нас ребята, которые эту библиотеку писали!

Все эти действия я делал для того, что бы уменьшить архив примера. Да и не нужно тянуть за собой много лишнего. Хотя сама библиотека весьма полезна. Один модуль JwaWindows чего стоит. Там очень много интересного есть. Ну да ладно – что-то я отвлекся.

После того, как мы кастрировали эти модули, давайте добавим их в наш проект. Готово?

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

Приступаем.

Оформление главной формы

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

СвойствоЗначение
AlignalTop
Caption 
NamePanel

Далее поместим компонент TTreeView и установите свойства как в таблице:

СвойствоЗначение
AlignalLeft
CursorcrHandPoint
HideSelectionFalse
HotTrackTrue
NameTreeView
ReadOnlyTrue

Правее TTreeView поместим TSplitter и установим следующие его свойства:

СвойствоЗначение
NameSplitter
Width5

И, наконец, помещаем компонент TListView еще правее TSplitter. Устанавливаем его свойства как в таблице:

СвойствоЗначение
AlignalClient
ColumnClickFalse
CursorcrHandPoint
GridLinesTrue
HideSelectionFalse
HotTrackTrue
NameListView
ReadOnlyTrue
RowSelectTrue
ShowWorkAreasTrue
ViewStylevsReport

На TPanel поместим кнопку TButton.

СвойствоЗначение
CaptionRefresh
NamebtRefresh

Теперь мы готовы писать программу.

Пишем код

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

Для этого в обработчике OnCreate формы fmMain напишем такой код:

procedure TfmMain.FormCreate(Sender: TObject);
begin
   btRefresh.Click;
end;
А в обработчике OnClick кнопки btRefresh напишем следующее:
procedure TfmMain.btRefreshClick(Sender: TObject);
var
   RootNode: TTreeNode;
   hFind: HBLUETOOTH_RADIO_FIND;
   hDevFind: HBLUETOOTH_DEVICE_FIND;
   FindParams: BLUETOOTH_FIND_RADIO_PARAMS;
   SearchParams: BLUETOOTH_DEVICE_SEARCH_PARAMS;
   SearchParamsSize: dword;
   DevInfo: ^PBLUETOOTH_DEVICE_INFO;
   DevInfoSize: dword;
   hRadio: THandle;
   RadioInfo: PBLUETOOTH_RADIO_INFO;
   RadioInfoSize: dword;
   RadioNode: TTreeNode;
   Loop: integer;
   DevNode: TTreeNode;
begin
   with TreeView.Items do
     begin
       BeginUpdate;

       // Очищаем дерево
       for Loop := 0 to Count - 1 do
         begin
           if TreeView.Items[Loop].ImageIndex > 0 then
             CloseHandle(TreeView.Items[Loop].ImageIndex);
           if Assigned(TreeView.Items[Loop].Data) then
             Dispose(TreeView.Items[Loop].Data);
         end;
       Clear;

       // Корневая ветвь в дереве
       RootNode := Add(nil, 'Bluetooth Radios');
       with RootNode do
         begin
           Data := nil;
           ImageIndex := -1;
         end;

       // Начинаем поиск локальных модулей Bluetooth
       FindParams.dwSize := SizeOf(BLUETOOTH_FIND_RADIO_PARAMS);
       hFind := BluetoothFindFirstRadio(@FindParams, hRadio);
       if hFind <> 0 then begin
         repeat
           // Получить информацию о радиомодуле
           New(RadioInfo);
           RadioInfoSize := SizeOf(BLUETOOTH_RADIO_INFO);
           FillChar(RadioInfo^, RadioInfoSize, 0);
           RadioInfo^.dwSize := RadioInfoSize;
           // Ошибки не обрабатываем!!!
           BluetoothGetRadioInfo(hRadio, RadioInfo^);

           // Добавляем радио в дерево
           RadioNode := AddChild(RootNode,
             string(RadioInfo^.szName) + ' [' +
             BTAdrToStr(RadioInfo^.address) + ']');
           with RadioNode do
             begin
             // Так как мы сохраняем Handle, то не закрываем его!
               ImageIndex := hRadio;
               Data := RadioInfo;
             end;

           // Начинаем поиск устройств для найденного радиомодуля.
           SearchParamsSize := SizeOf(BLUETOOTH_DEVICE_SEARCH_PARAMS);
           FillChar(SearchParams, SearchParamsSize, 0);
           SearchParams.dwSize := SearchParamsSize;
           SearchParams.fReturnRemembered := True;
           SearchParams.hRadio := hRadio;

           New(DevInfo);
           DevInfoSize := SizeOf(BLUETOOTH_DEVICE_INFO);
           FillChar(DevInfo^, DevInfoSize, 0);
           DevInfo^.dwSize := DevInfoSize;

           // Ищем первое
           hDevFind := BluetoothFindFirstDevice(SearchParams, DevInfo^);
           if hDevFind <> 0 then begin
             repeat
               // Добавляем в дерево
               DevNode := AddChild(RadioNode,
                 string(DevInfo^.szName) + ' [' +
                 BTAdrToStr(DevInfo^.Address) + ']');
               with DevNode do
                 begin
                   Data := DevInfo;
                   ImageIndex := -2;
                 end;

               // Ищем следующее устройство
               New(DevInfo);
               DevInfoSize := SizeOf(BLUETOOTH_DEVICE_INFO);
               FillChar(DevInfo^, DevInfoSize, 0);
               DevInfo^.dwSize := DevInfoSize;
             until not BluetoothFindNextDevice(hDevFind, DevInfo^);

           // Поиск устройств закончен
           BluetoothFindDeviceClose(hDevFind);
         end;

         // Находим следующее радио
       until not BluetoothFindNextRadio(hFind, hRadio);

       // Поиск радиомодулей закончен
       BluetoothFindRadioClose(hFind);
     end;

     EndUpdate;
   end;

   with TreeView do
     begin
       Selected := RootNode;
       Items[0].Expand(True);
     end;
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_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;
Члены:
dwSizeВходной параметр. Должен быть равен размеру структуры (dwSize := SizeOf(BLUETOOTH_DEVICE_SEARCH_PARAMS))
fReturnAuthenticatedВходной параметр. Функция будет возвращать устройства, прошедшие авторизацию.
fReturnRememberedВходной параметр. Функция будет возвращать устройства, уже запомненные раннее.
fReturnUnknownВходной параметр. Функция будет возвращать новые либо неизвестные устройства.
fReturnConnectedВходной параметр. Функция будет возвращать подключенные устройства.
fIssueInquiryВходной параметр. Заставляет функцию проверять устройства.
cTimeoutMultiplierВходной параметр. Тайм-аут для проверки устройства.
hRadioHandle радиомодуля, для которого проводится поиск устройств. Если 0, то проверяются все радиомодули.

BLUETOOTH_DEVICE_INFO

Объявление:

BLUETOOTH_DEVICE_INFO = record
   dwSize : DWORD;
   Address : BLUETOOTH_ADDRESS;
   ulClassofDevice : ULONG;
   fConnected : BOOL;
   fRemembered : BOOL;
   fAuthenticated : BOOL;
   stLastSeen : SYSTEMTIME;
   stLastUsed : SYSTEMTIME;
   szName : array [0..BLUETOOTH_MAX_NAME_SIZE - 1] of WideChar;
end;
Члены:
dwSizeВходной параметр. Должен быть равен размеру структуры (dwSize := SizeOf(BLUETOOTH_DEVICE_INFO))
AddressАдрес устройства Bluetooth.
ulClassofDeviceКласс устройства. Подробнее по классам смотрите в JwaBluetoothAPIs. Константы с префиксом COD_xxx.
fConnectedЕсли TRUE, то устройство подключено/используется
fRememberedЕсли TRUE, то устройство ранее уже было найдено (запомнено)
fAuthenticatedЕсли TRUE, то устройство прошло авторизацию (авторизированно)
stLastSeenДата и время последнего обнаружения устройства
stLastUsedДата и время последнего использования устройства
szNameНазвание устройства (имя)

BLUETOOTH_RADIO_INFO

Объявление:

BLUETOOTH_RADIO_INFO = record
   dwSize : DWORD;
   address : BLUETOOTH_ADDRESS;
   szName : array [0..BLUETOOTH_MAX_NAME_SIZE - 1] of WideChar;
   ulClassofDevice : ULONG;
   lmpSubversion : Word;
   manufacturer : Word;
end;
Члены:
dwSizeДолжен быть равен размеру структуры (dwSize := SizeOf(BLUETOOTH_RADIO_INFO))
AddressАдрес радиомодуля Bluetooth
szNameИмя радиомодуля
ulClassofDeviceКласс устройства (см. выше)
lmpSubversionЗависит от производителя
ManufacturerКод производителя. Определяется константами BTH_MFG_Xxx. Более полную информацию о производителях можно получить на сайте поддержки Bluetooth.

Далее напишем вот такой обработчик события OnChange для TreeView:

procedure TfmMain.TreeViewChange(Sender: TObject; Node: TTreeNode);
var
   ASelected: TTreeNode;

procedure ShowRadios;
   var
     Info: PBLUETOOTH_RADIO_INFO;
     CurNode: TTreeNode;
begin
   // Строим столбцы
   with ListView.Columns do
     begin
       BeginUpdate;
       with Add do Caption := 'Address';
       with Add do Caption := 'Name';
       with Add do Caption := 'Class Of Device';
       with Add do Caption := 'Manufacturer';
       with Add do Caption := 'Subversion';
       with Add do Caption := 'Connectable';
       with Add do Caption := 'Discoverable';
       EndUpdate;
     end;

   // Заполняем список
   with ListView.Items do
     begin
       BeginUpdate;

       CurNode := ASelected.GetFirstChild;

       while Assigned(CurNode) do          begin
           Info := PBLUETOOTH_RADIO_INFO(CurNode.Data);

           // Перечитать информацию о радиомодуле
           BluetoothGetRadioInfo(CurNode.ImageIndex, Info^);

           with Add do
             begin
               Data := Pointer(CurNode.ImageIndex);
               Caption := BTAdrToStr(Info.address);
               with SubItems do
                 begin
                   Add(string(Info.szName));
                   Add(IntToStr(Info.ulClassofDevice));
                   Add(IntToStr(Info.manufacturer));
                   Add(IntToStr(Info.lmpSubversion));
                   // NEW FUNCTIONS!!!
                   Add(BoolToStr(BluetoothIsConnectable(CurNode.ImageIndex), True));
                   Add(BoolToStr(BluetoothIsDiscoverable(CurNode.ImageIndex), True));
                 end;
             end;

           CurNode := ASelected.GetNextChild(CurNode);
         end;

       EndUpdate;
     end;
   end;

procedure ShowDevices;
   var
     Info: ^PBLUETOOTH_DEVICE_INFO;
     CurNode: TTreeNode;
begin
   // Строим столбцы
   with ListView.Columns do
     begin
       BeginUpdate;
       with Add do Caption := 'Address';
       with Add do Caption := 'Name';
       with Add do Caption := 'Class Of Device';
       with Add do Caption := 'Connected';
       with Add do Caption := 'Remembered';
       with Add do Caption := 'Authenticated';
       with Add do Caption := 'Last Seen';
       with Add do Caption := 'Last Used';
       EndUpdate;
     end;

   // Заполняем список
   with ListView.Items do
     begin
       BeginUpdate;

       CurNode := ASelected.GetFirstChild;

       while Assigned(CurNode) do
         begin
           Info := CurNode.Data;

           // Перечитываем информацию об устройстве
           // Так как передаем указатель, то она автоматом
           // Обновится и в том месте, где мы ее сохраняли
           BluetoothGetDeviceInfo(ASelected.ImageIndex, Info^);

           with Add do
             begin
               Data := Info;
               Caption := BTAdrToStr(Info^.Address);
               with SubItems do
                 begin
                   Add(string(Info^.szName));
                   Add(IntToStr(Info^.ulClassofDevice));
                   Add(BoolToStr(Info^.fConnected, True));
                   Add(BoolToStr(Info^.fRemembered, True));
                   Add(BoolToStr(Info^.fAuthenticated, True));
                   try // stLastSeen может быть 0 и тогда здесь ошибка будет
                     Add(DateTimeToStr(SystemTimeToDateTime(Info^.stLastSeen)));
                   except
                     Add(‘’);
                   end;
                   try // stLastUsed может быть 0 и тогда здесь ошибка будет
                     Add(DateTimeToStr(SystemTimeToDateTime(Info^.stLastUsed)));
                   except
                     Add(‘’);
                   end;
                 end;
               end;

             CurNode := ASelected.GetNextChild(CurNode);
         end;

       EndUpdate;
     end;
end;

procedure ShowServices;
   var
     Info: __PBLUETOOTH_DEVICE_INFO;
     ServiceCount: dword;
     Services: array of TGUID;
     hRadio: THandle;
     Loop: integer;
begin
   // Строим столбцы
   with ListView.Columns do
     begin
       BeginUpdate;
       with Add do Caption := 'GUID';
       EndUpdate;
     end;

   // Заполняем список
   with ListView.Items do
     begin
       BeginUpdate;

       // Получаем размер массива сервисов
       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));

       // Рисуем их
       for Loop := 0 to ServiceCount - 1 do
         with Add do
           Caption := GUIDToString(Services[Loop]);

       // Очищаем память
       Services := nil;

       EndUpdate;
     end;
end;

begin
   ASelected := TreeView.Selected;

   // Очищаем ListView
   with ListView do
     begin
       with Columns do
         begin
           BeginUpdate;
           Clear;
           EndUpdate;
         end;

       with Items do
         begin
           BeginUpdate;
           Clear;
           EndUpdate;
         end;
     end;

   // Заполняем информацией
   if Assigned(ASelected) then
     case ASelected.ImageIndex of
       -2: ShowServices;
       -1: ShowRadios;
     else
       if ASelected.ImageIndex > 0 then ShowDevices;
     end;
end;

В этом коде появилось три новые функции, которые выделены жирным шрифтом. Вот они

BluetoothIsConnectable - определяет, возможно ли подключение к указанному радиомодулю.

Объявление функции:

function BluetoothIsConnectable(
   const hRadio : THandle): BOOL; stdcall;
Параметры:
hRadioHandle радиомодуля, который мы проверяем. Если 0, то проверяются все радиомодули.
Возвращаемые значения:

BluetoothIsDiscoverable - определяет, будет ли виден указанный радиомодуль другим при поиске. Если просматриваются все радиомодули, то вернет TRUE если хотя бы один разрешает обнаружение.

Объявление функции:

function BluetoothIsDiscoverable(
   const hRadio : THandle): BOOL; stdcall;
Параметры:
hRadioHandle радиомодуля, который мы проверяем. Если 0, то проверяются все радиомодули.
Возвращаемые значения:

BluetoothEnumerateInstalledServices - получает список GUID сервисов, предоставляемых устройством. Если параметр hRadio=0, то просматривает все радиомодули.

Объявление функции:

function BluetoothEnumerateInstalledServices(
   hRadio : THandle;
   pbtdi : __PBLUETOOTH_DEVICE_INFO;
   var pcServices : dword;
   pGuidServices : PGUID): dword; stdcall;
Параметры:
hRadioHandle радиомодуля, который мы проверяем. Если 0, то проверяются все радиомодули.
pbtdiУказатель на структуру BLUETOOTH_DEVICE_INFO, в которой описано проверяемое устройство. Необходимо заполнить поля dwSize и Address.
pcServicesПри вызове – количество записей в массиве pGuidServices, возвращает в этом параметре реальное количество сервисов, предоставляемых устройством.
pGuidServicesУказатель на массив TGUID, в который будут записаны GUID предоставляемых устройством сервисом. Если nil и pcServices=0, то в pcServices будет записано количество сервисов. Необходимо выделить для pGuidServices память размером не менее pcServices*SizeOf(TGUID).
Возвращаемые значения:

Примечания:

Посмотрите на код получения списка сервисов:

// Получаем размер массива сервисов
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 и изменим его свойства как показано в таблице.
СвойствоЗначение
NameActionList

Теперь два раза щелкнем по ActionList и в появившемся окне редактора свойств добавим две TAction со следующими свойствами:
СвойствоЗначение
CaptionConnectable
NameacConnectable

СвойствоЗначение
CaptionDiscoverable
NameacDiscoverable

На панель Panel добавим две TButton и установим свойства:
СвойствоЗначение
ActionacConnectable
NamebtConnectable

СвойствоЗначение
ActionacDiscoverable
NamebtDiscoverable

Напишем вот такой обработчик события OnUpdate у acConnectable:

procedure TfmMain.acConnectableUpdate(Sender: TObject);
var
   SelectedItem: TListItem;
   SelectedNode: TTreeNode;
begin
   SelectedNode := TreeView.Selected;
   SelectedItem := ListView.Selected;

   with TAction(Sender) do
     begin
     Enabled := Assigned(SelectedNode) and Assigned(SelectedItem) and (SelectedNode.ImageIndex = -1);

     if Enabled then
       if StrToBool(SelectedItem.SubItems[4])
         then Caption := 'Not conn.'
         else Caption := 'Connectable';
     end;
end;
И то же самое напишем для обработчика события OnUpdate - acDiscoverable:
procedure TfmMain.acDiscoverableUpdate(Sender: TObject);
var
   SelectedItem: TListItem;
   SelectedNode: TTreeNode;
begin
   SelectedNode := TreeView.Selected;
   SelectedItem := ListView.Selected;

   with TAction(Sender) do
     begin
       Enabled := Assigned(SelectedNode) and Assigned(SelectedItem) and (SelectedNode.ImageIndex = -1);

       if Enabled then
         if StrToBool(SelectedItem.SubItems[5])
           then Caption := 'Not disc.'
           else Caption := 'Discoverable';
       end;
end;
Теперь обработчик события 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;
Параметры:
hRadioHandle радиомодуля, статус которого мы хотим изменить. Если 0, то меняем у всех.
fEnabledTRUE – разрешаем подключения; FALSE – запрещаем.
Возвращаемые значения:

BluetoothEnableDiscovery - функция разрешает/запрещает обнаружение локального радиомодуля Bluetooth

Объявление функции:

function BluetoothEnableDiscovery(
   hRadio : THandle;
   fEnabled : BOOL): BOOL; stdcall;
Параметры:
hRadioHandle радиомодуля, статус которого мы хотим изменить. Если 0, то меняем у всех.
fEnabledTRUE – разрешаем обнаружение; FALSE – запрещаем.
Возвращаемые значения:

Теперь давайте научимся выводить системное окно свойств устройства Bluetooth. Для этого добавим к ActionList еще один TAction вот с такими свойствами:
СвойствоЗначение
CaptionProperty
NameacProperty

Добавим на Panel кнопку TButton с такими свойствами:
СвойствоЗначение
ActionacProperty
NamebtProperty

Теперь напишем такой обработчик событий 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), но как я писал выше, этот тип ошибочен. Он не является указателем. Я изменил функцию так, как показано выше (так она и должна быть, если не менять определение типа). Параметры:
hwndParentHandle родительского окна, которому будет принадлежать диалог свойств. Может быть 0, тогда родительским выбирается окно Desktop.
pbtdiУказатель на структуру BLUETOOTH_DEVICE_INFO в которой содержится адрес требуемого устройства.

Возвращаемые значения:

Выбор устройства

Рассмотрим, как вызвать окно диалога выбора устройства.

Добавим в наш проект на Panel еще одну кнопку TButton и установите ее свойства как в таблице:
СвойствоЗначение
CaptionSelect
NamebtSelect

Напишем вот такой обработчик события 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;
Параметры:
pbtsdpОписание смотрите ниже в описании структуры.

Возвращаемые значения:

Если функция вернула TRUE, то пользователь выбрал устройства. Pbtsdp^.pDevices будет указывать на корректные данные. После вызова необходимо проверить флаги fAuthenticated и fRemembered, что бы удостовериться в корректности данных. Для освобождения памяти используйте функцию BluetoothSelectDevicesFree, только если функция вернет TRUE.

Вернет FALSE если вызов прошел не удачно. Используйте GetLastError для получения дополнительных сведений. Возможные ошибки:

ERROR_CANCELLEDПользователь отменил выбор устройства.
ERROR_INVALID_PARAMETERПараметр pbsdp равен nil.
ERROR_REVISION_MISMATCHСтруктура, переданная в pbsdp неизвестного или неверного размера.
Другие ошибки Win32 

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;
Члены:
dwSizeДолжен быть равен размеру структуры (dwSize := SizeOf(BLUETOOTH_RADIO_INFO))
cNumOfClassesВходной параметр. Количество записей в массиве prgClassOfDevice. Если 0, то ищутся все устройства.
prgClassOfDevicesВходной параметр. Массив COD (классов устройств), которые необходимо искать.
pszInfoВходной параметр. Если не nil, то задает текст заголовка окна выбора устройства.
hwndParentВходной параметр. Handle родительского окна для диалога выбора устройства. Если 0, то родителем будет Desktop.
fForceAuthenticationВходной параметр. Если TRUE, то требует принудительной авторизации устройств.
fShowAuthenticatedВходной параметр. Если TRUE, то авторизованные устройства будут доступны для выбора.
fShowRememberedВходной параметр. Если TRUE, то запомненные устройства будут доступны для выбора.
fShowUnknownВходной параметр. Если TRUE, то неизвестные (неавторизованные и не запомненные) устройства будут доступны для выбора.
fAddNewDeviceWizardВходной параметр. Если TRUE, то запускает мастер добавления нового устройства.
fSkipServicesPageВходной параметр. Если TRUE, то пропускает страницу Сервисы в мастере.
pfnDeviceCallbackВходной параметр. Если не nil, то является указателем на функцию обратного вызова, которая вызывается для каждого найденного устройства. Если функция вернет TRUE, то устройства добавляется в список, если нет, то устройство игнорируется.
pvParamВходной параметр. Его значение будет передано функции pfnDeviceCallback в качестве параметра pvParam.
cNumDevicesКак входной параметр – количество устройств, которое требуется вернуть. Если 0, то нет ограничений. Как выходной параметр – количество возвращенных устройств (выбранных).
pDevicesВыходной параметр. Указатель на массив структур BLUETOOTH_DEVICE_INFO. Для его освобождения используйте функцию BluetoothSelectDevicesFree.
Важно: В оригинале этот параметр объявлен как PBLUETOOTH_DEVICE_INFO. По этому поводу здесь много комментариев.

BluetoothSelectDevicesFree - функция должна вызываться, только если вызов BluetoothSelectDevices был успешен. Эта функция освобождает память и ресурсы, задействованные функцией BluetoothSelectDevices в структуре BLUETOOTH_SELECT_DEVICE_PARAMS.

Объявление функции:

function BluetoothSelectDevices(
   pbtsdp : PBLUETOOTH_SELECT_DEVICE_PARAMS): BOOL; stdcall;
Параметры:
pbtsdpОписание смотрите выше в описании структуры.
Возвращаемые значения:

Управление сервисами

Для управления сервисами Microsoft Bluetooth API предоставляет функцию:

BluetoothSetServiceState - включает или выключает указанный сервис для устройства Bluetooth. Система проецирует сервис Bluetooth на соответствующий драйвер. При отключении сервиса – драйвер удаляется. При его включении – драйвер устанавливается. Если выполняется включение не поддерживаемого сервиса, то драйвер не будет установлен.

Объявление функции:

function BluetoothSetServiceState(
   hRadio : Thandle;
   var pbtdi : PBLUETOOTH_DEVICE_INFO;
   const pGuidService : TGUID;
   dwServiceFlags : DWORD): DWORD; stdcall;

Параметры:
hRadioОписатель радиомодуля.
pbtdiУказатель на структуру BLUETOOTH_DEVICE_INFO.
pGuidServiceGUID сервиса, который необходимо включить/выключить.
dwServiceFlagsФлаги управления сервисом:
BLUETOOTH_SERVICE_DISABLE – отключает сервис;
BLUETOOTH_SERVICE_ENABLE – включает сервис.

Возвращает ERROR_SUCCESS если вызов прошел успешно. Если вызов не удался вернет один из следующих кодов:
ERROR_INVALID_PARAMETERНеверные флаги в dwServiceFlags
ERROR_SERVICE_DOES_NOT_EXISTУказанный сервис не поддерживается
Другие ошибки Win32 

Важно: В оригинале (см. примечание выше) функция выглядит вот так:
function BluetoothSetServiceState(
   hRadio : Thandle;
   pbtdi : PBLUETOOTH_DEVICE_INFO;
   const pGuidService : TGUID;
   dwServiceFlags : DWORD): DWORD; stdcall;
Это не верно, так как в документации Microsoft указано, что параметр pbtdi должен передаваться как указатель (что подразумевает запись PBLUETOOTH_DEVICE_INFO), но как я писал выше, этот тип ошибочен. Он не является указателем. Я изменил функцию так, как показано выше (так она и должна быть, если не менять определение типа).

Как использовать функцию? Давайте добавим к ActionList еще одну TAction с такими свойствами:
СвойствоЗначение
CaptionDisable
NameacEnable

И добавим на Panel еще одну кнопку TButton, установив у нее следующие свойства:
СвойствоЗначение
ActionacEnable
NamebtEnable
В обработчике события OnUpdate для acEnable напишем вот такой код:

procedure TfmMain.acEnableUpdate(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 = -2);
end;
А в обработчике 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;
Параметры:
hAddressАдрес устройства, которое удаляется.
Возвращаемые значения:
ERROR_SUCCESSустройство удалено
ERROR_NOT_FOUNDустройство не найдено

Давайте попробуем. Добавим в ActionList TAction со следующими свойствами:
СвойствоЗначение
CaptionRemove
NameacRemove

И на Panel кнопку TButton со свойствами:
СвойствоЗначение
ActionacRemove
NamebtRemove

В обработчике 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;
Параметры:
pbtduУказатель на структуру BLUETOOTH_DEVICE_INFO. В ней должны быть заполнены поля:
dwSize – размер структуры;
Address – адрес устройства;
szName – новое имя устройства.
Возвращаемые значения:
ERROR_SUCCESSФункция выполнена успешно
ERROR_INVALID_PARAMETERУказатель pbtdi=nil. (Для варианта в Delphi не реально, так как указатель мы получаем из структуры, передавая ее как var-параметр).
ERROR_REVISION_MISMATCHРазмер структуры в dwSize не правильный
Другие ошибки Win32 

Попробуем использовать и ее. Схема стандартная: TAction к ActionList, TButton на Panel:
СвойствоЗначение
CaptionUpdate
NameacUpdate
СвойствоЗначение
ActionacUpdate
NamebtUpdate

Код:

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;
Параметры:
hwndParentHandle родительского окна. Если 0, то родительским окном станет окно Desktop.
hRadioHandle локального радиомодуля. Если 0, то авторизация будет проведена на всех радиомодулях. Если хотя бы один пройдет авторизацию, функция выполнится успешно.
pbdtiИнформация об устройстве, на котором необходимо авторизироваться.
pszPasskeyPIN для авторизации. Если nil, то вызывается мастер авторизации (описано выше). Важно: pszPasskey не NULL-терминированная строка!
ulPasskeyLengthДлина строки в байтах. Должна быть меньше либо равна BLUETOOTH_MAX_PASSKEY_SIZE * SizeOf(WCHAR).
Возвращаемые значения:
ERROR_SUCCESSФункция выполнена успешно
ERROR_CANCELLEDПользователь отменил процесс авторизации
ERROR_INVALID_PARAMETERСтруктура pbtdi не верна
ERROR_NO_MORE_ITEMSУстройство в pbtdi уже авторизированно
Другие ошибки Win32 

Для "Blind Mode" соответствие кодов ошибок Bluetooth кодам ошибок Win32 приведено в таблице:

BluetoothWin32
BTH_ERROR_SUCCESSERROR_SUCCESS
BTH_ERROR_NO_CONNECTIONERROR_DEVICE_NOT_CONNECTED
BTH_ERROR_PAGE_TIMEOUTWAIT_TIMEOUT
BTH_ERROR_HARDWARE_FAILUREERROR_GEN_FAILURE
BTH_ERROR_AUTHENTICATION_FAILUREERROR_NOT_AUTHENTICATED
BTH_ERROR_MEMORY_FULLERROR_NOT_ENOUGH_MEMORY
BTH_ERROR_CONNECTION_TIMEOUTWAIT_TIMEOUT
BTH_ERROR_LMP_RESPONSE_TIMEOUTWAIT_TIMEOUT
BTH_ERROR_MAX_NUMBER_OF_CONNECTIONSERROR_REQ_NOT_ACCEP
BTH_ERROR_PAIRING_NOT_ALLOWEDERROR_ACCESS_DENIED
BTH_ERROR_UNSPECIFIED_ERRORERROR_NOT_READY
BTH_ERROR_LOCAL_HOST_TERMINATED_CONNECTIONERROR_VC_DISCONNECTED

Аналогичная функция:

BluetoothAuthenticateMultipleDevices - позволяет авторизироваться сразу на нескольких устройствах при помощи одной копии "Мастера авторизации".

Объявление функции:

function BluetoothAuthenticateMultipleDevices(
   hwndParent : HWND;
   hRadio : THandle;
   cDevices : DWORD;
   rgpbtdi : __PBLUETOOTH_DEVICE_INFO): DWORD; stdcall;
Параметры:
hwndParentHandle родительского окна. Если 0, то родительским окном станет окно Desktop.
hRadioHandle локального радиомодуля. Если 0, то авторизация будет проведена на всех радиомодулях. Если хотя бы один пройдет авторизацию, функция выполнится успешно.
cDevicesКоличество элементов в массиве rgpbtdi.
rgpbtdiМассив структур BLUETOOTH_DEVICE_INFO, в котором представлены устройства для авторизации.
Возвращаемые значения:
ERROR_SUCCESSФункция выполнена успешно. Проверьте флаг fAuthenticated у каждого устройства, что бы знать, какие прошли авторизацию.
ERROR_CANCELLEDПользователь отменил процесс авторизации. Проверьте флаг fAuthenticated у каждого устройства, что бы знать, какие прошли авторизацию.
ERROR_INVALID_PARAMETERОдин или несколько элементов массива rgpbtdi не верны.
ERROR_NO_MORE_ITEMSВсе устройства в массиве уже авторизированны.
Другие ошибки Win32 

Важно: В оригинале функция выглядит вот так:

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;
Параметры:
pbtdiУказатель на BLUETOOTH_DEVICE_INFO. Используется адрес устройства, для которого регистрируется функция. Обратите внимание на параметр. В оригинале он опять передается не как указатель.
phRegHandleУказатель, куда будет возвращен Handle регистрации, которой потом используется в BluetoothUnregisterAuthentication.
pfnCallbackФункция обратного вызова.
pvParamОпциональный параметр, который без изменения передается в функцию обратного вызова.
Возвращаемые значения:
ERROR_SUCCESSФункция выполнена успешно.
ERROR_OUTOFMEMORYНедостаточно памяти.
Другие ошибки Win32 

BluetoothUnregisterAuthentication - удаляет функцию обратного вызова, зарегистрированную функцией BluetoothRegisterForAuthentication и закрывает Handle.

Объявление функции:

function BluetoothUnregisterAuthentication(
   hRegHandle : HBLUETOOTH_AUTHENTICATION_REGISTRATION): BOOL; stdcall;
Параметры:
hRegHandleHandle регистрации, полученный функцией BluetoothRegisterForAuthentication.

Возвращаемые значения:

Вернет TRUE, если вызов успешен и FALSE в случае неудачи. Используйте GetLastError для получения дополнительной информации.

BluetoothSendAuthenticationResponse - эта функция должна вызываться из функции обратного вызова при запросе авторизации удаленным устройством для передачи PIN.

Объявление функции:

function BluetoothSendAuthenticationResponse(
   hRadio : THandle;
   pbtdi : PBLUETOOTH_DEVICE_INFO;
   pszPasskey : LPWSTR): DWORD; stdcall;
Параметры:
hRadioHandle радиомодуля, для которого проводим авторизацию. Если 0, то пытаемся на всех.
pbtdiУказатель на BLUETOOTH_DEVICE_INFO с данными об устройстве, от которого поступил запрос на авторизацию. Может быть тот же указатель, который передан в функцию обратного вызова.
pszPasskeyУказатель на UNICODE строку, в которой содержится ключ авторизации (PIN).
Возвращаемые значения:
ERROR_SUCCESSФункция выполнена успешно.
ERROR_CANCELLEDУстройство отвергло авторизационный код (PIN). Так же, возможно, имеются проблемы со связью
E_FAILУстройство вернуло ошибку авторизации.
Другие ошибки Win32 

И, наконец, функция обратного вызова:

PFN_AUTHENTICATION_CALLBACK

Описание этой функции дано выше. Здесь приведу лишь определеннее.

Объявление функции:

PFN_AUTHENTICATION_CALLBACK =
   function(pvParam : Pointer;
     pDevice : PBLUETOOTH_DEVICE_INFO): BOOL; stdcall;
Параметры:
pvParamУказатель на параметр, который мы передали в BluetoothRegisterForAuthentication.
pDeviceУказатель на BLUETOOTH_DEVICE_INFO с данными об устройстве, от которого поступил запрос на авторизацию.

Заключение

На этот раз все. Мы рассмотрели все функции Bluetooth API от Microsoft. Также мы научились управлять устройствами и радиомодулями Bluetooth, проводить авторизацию и получать информацию об этих устройствах.

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

Ответ на этот вопрос читайте в следующей серии статей "Передача данных через Bluetooth".

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

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

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

P.S. Внимательно относитесь к сторонним библиотекам. Как видите, в JWALIB оказалось много ошибок, которые порой загоняют в тупик. Я минут 20 смотрел на Access Violation, пока не понял, в чем дело.

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

Copyright© 2006 Петриченко Михаил, Soft Service Company
Специально для Delphi Plus


Пожалуйста, оцените статью
Отлично
Хорошо
Средне
Плохо
Очень плохо

Практика программирования USB