Четверг, 02.05.2024
Королевство Delphi
Главное меню
Статьи
Наш опрос
Нашли свой исходник?
Всего ответов: 94
Статистика
Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа
Главная » Статьи » Система » Работа с Bluetooth

Работа с Bluetooth в Delphi. Часть 3

Вступление

И так, в предыдущих частях мы научились получать список локальных радиомодулей 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 и установите следующие свойства:

Свойство Значение
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;

А в обработчике 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 Входной параметр. Тайм-аут для проверки устройства.
hRadio Handle радиомодуля, для которого проводится поиск устройств. Если 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; 

Параметры:

hRadio Handle радиомодуля, который мы проверяем. Если 0, то проверяются все радиомодули.

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

  • Вернет TRUE, если указанный радиомодуль разрешает входящие подключения. Если hRadio=0, то вернет TRUE, если хотя бы один радиомодуль разрешает входящие подключения.
  • Если входящие подключения запрещены, то вернет FALSE.

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

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

function BluetoothIsDiscoverable(
 const hRadio : THandle): BOOL; stdcall; 

Параметры:

hRadio Handle радиомодуля, который мы проверяем. Если 0, то проверяются все радиомодули.

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

  • Вернет TRUE, если указанный радиомодуль разрешает обнаружение. Если hRadio=0, то вернет TRUE, если хотя бы один радиомодуль разрешает обнаружение.
  • Если обнаружение запрещено, то вернет FALSE.

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

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

function BluetoothEnumerateInstalledServices(
 hRadio : THandle;
 pbtdi : __PBLUETOOTH_DEVICE_INFO;
 var pcServices : dword;
 pGuidServices : PGUID): dword; stdcall; 

Параметры:

hRadio Handle радиомодуля, который мы проверяем. Если 0, то проверяются все радиомодули.
pbtdi Указатель на структуру BLUETOOTH_DEVICE_INFO, в которой описано проверяемое устройство. Необходимо заполнить поля dwSize и Address.
pcServices При вызове – количество записей в массиве pGuidServices, возвращает в этом параметре реальное количество сервисов, предоставляемых устройством.
pGuidServices Указатель на массив TGUID, в который будут записаны GUID предоставляемых устройством сервисом. Если nil и pcServices=0, то в pcServices будет записано количество сервисов. Необходимо выделить для pGuidServices память размером не менее pcServices*SizeOf(TGUID).

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

  • Вернет ERROR_SUCCESS, если вызов успешен и количество сервисов в pcServices соответствует реальности.
  • Вернет ERROR_MORE_DATA, если вызов успешен, но выделенное количество памяти (pcServices при вызове) меньше, чем количество предоставляемых сервисов.
  • В случае ошибки – другие коды ошибок Win32.

Примечания:

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

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

Напишем вот такой обработчик события 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; 

Параметры:

hRadio Handle радиомодуля, статус которого мы хотим изменить. Если 0, то меняем у всех.
fEnabled TRUE – разрешаем подключения; FALSE – запрещаем.

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

  • TRUE - если вызов успешен и статус изменен,
  • FALSE - в противном случае.

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

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

function BluetoothEnableDiscovery(
 hRadio : THandle;
 fEnabled : BOOL): BOOL; stdcall; 

Параметры:

hRadio Handle радиомодуля, статус которого мы хотим изменить. Если 0, то меняем у всех.
fEnabled TRUE – разрешаем обнаружение; FALSE – запрещаем.

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

  • TRUE - если вызов успешен и статус изменен,
  • FALSE - в противном случае.

Теперь давайте научимся выводить системное окно свойств устройства 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 из этого примера, чтобы не возникало ошибок доступа к памяти.

Важно: Ни в этой процедуре, ни ранее, ни далее я не провожу проверку ошибок, чтобы не загромождать код лишними подробностями. В реальном приложении НЕОБХОДИМО проверять возвращаемые функциями значения и указатели.

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

Продолжение....

Получить ссылку на материал

Категория: Работа с Bluetooth | Добавил: Барон (08.12.2011)
Просмотров: 1734 | Теги: delphi, Bluetooth | Рейтинг: 1.0/1
[ Пожертвования для сайта ] [ Пожаловаться на материал ]

Если вам помог материал сайта кликните по оплаченной рекламе размещенной в центре

Поиск
Категории раздела
ActiveX [10]
CORBA и COM [16]
Kol и MCK [23]
WinAPI [28]
Компоненты [27]
Работа с Bluetooth [4]
Железо [8]
Текст [18]
Разное [98]
Королевство Delphi © 2010-2024
Яндекс цитирования