Delphi и Bluetooth. Часть 2 (исходники)

Источник: mobileservicesoft
Петриченко Михал

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

Получение списка устройств Bluetooth 

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

BluetoothFindFirstDevice 

Начинает перечисление устройств Bluetooth.

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

function BluetoothFindFirstDevice(
const pbtsp : BLUETOOTH_DEVICE_SEARCH_PARAMS;
var pbtdi : BLUETOOTH_DEVICE_INFO): HBLUETOOTH_DEVICE_FIND; stdcall; 

Параметры: 

pbtsp - указатель на структуру BLUETOOTH_DEVICE_SEARCH_PARAMS. Член dwSize этой структуры должен содержать размер структуры (устанавливается посредством SizeOf(BLUETOOTH_DEVICE_SEARCH_PARAMS)). Член hRadio должен содержать описатель локального радиомодуля, полученный вызовом функции BluetoothFindFirstRadio. 
pbtdi - структура BLUETOOTH_DEVICE_INFO в которую будет возвращена информации об устройстве Bluetooth. 

Возвращаемые значения: в случае успешного выполнения функция вернет корректный описатель в качестве результата. В случае ошибки будет возвращен 0. Для получения кода ошибки используйте функцию GetLastError.

BluetoothFindNextDevice

Находит следующее устройство Bluetooth.

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

function BluetoothFindNextDevice(
hFind : HBLUETOOTH_RADIO_FIND;
var pbtdi : BLUETOOTH_DEVICE_INFO): BOOL; stdcall; 

Параметры: 

hFind - описатель, который вернула функция BluetoothFindFirstDevice. 
pbtdi - структура BLUETOOTH_DEVICE_INFO, в которую будет помещена информацию об устройстве. 

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

BluetoothFindDeviceClose 

Закрывает описатель перечисления устройств Bluetooth.

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

function BluetoothFindDeviceClose(
hFind : HBLUETOOTH_DEVICE_FIND): BOOL; stdcall;

Параметры: 

hFind - описатель, который вернула функция BluetoothFindFirstDevice. 

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

BluetoothGetDeviceInfo 

Возвращает информацию об указанном устройстве Bluetooth.

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

function BluetoothGetDeviceInfo(
hRadio : THandle
var pbtdi : BLUETOOTH_DEVICE_INFO): DWORD; stdcall;

Параметры: 

hRadio - описатель локального радиомодуля Bluetooth. 
pbtdi - структура BLUETOOTH_DEVICE_INFO, в которую возвразается информация об устройстве. dwSize должен быть равен размеру структуры. addreess должен содержать адрес устройства, о котором хотим получить информацию. 

Возвращаемые значения: вернет ERROR_SUCCESS если выполнено успешно и информация занесена в структуру pbtdi. Остальные значения - код ошибки.

Обладая этими знаниями, можно написать процедуру получения информации об устройствах Bluetooth.

procedure GetDevices(_hRadio: THandle);
var
DeviceInfo: PBLUETOOTH_DEVICE_INFO;
DeviceSearchParams: BLUETOOTH_DEVICE_SEARCH_PARAMS;
DeviceFind: HBLUETOOTH_DEVICE_FIND;
begin
// Инициализация структуры BLUETOOTH_DEVICE_SEARCH_PARAMS
with DeviceSearchParams do begin
dwSize := SizeOf(BLUETOOTH_DEVICE_SEARCH_PARAMS);
fReturnRemembered := true; // Вернуть запомненные
hRadio := _hRadio
end;

// Инициализация структуры BLUETOOTH_DEVICE_INFO
FillChar(DeviceInfo, SizeOf(PBLUETOOTH_DEVICE_INFO), 0);
DeviceInfo.dwSize := SizeOf(PBLUETOOTH_DEVICE_INFO);

// Начинаем поиск
DeviceFind := BluetoothFindFirstDevice(DeviceSearchParams, DeviceInfo);
if (DeviceFind <> 0) then begin
repeat
// Что-то сделать с полученным данными

// Инициализация структуры BLUETOOTH_DEVICE_INFO
FillChar(DeviceInfo, SizeOf(PBLUETOOTH_DEVICE_INFO), 0);
DeviceInfo.dwSize := SizeOf(PBLUETOOTH_DEVICE_INFO);
// Находим следующее устройство
until (not BluetoothFindNextDevice(DeviceFind, DeviceInfo));

// Закрываем поиск
BluetoothFindDeviceClose(DeviceFind);
end;
end; 

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

Так что ИСПОЛЬЗУЙТЕ файл из примера, а не из исходной библиотеки. Иначе получите нарушение доступа к памяти.

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

В принципе то, что описано выше, мы уже знали, кроме двух указанных функций.

Давайте расширим возможности нашего приложения. Добавим функции запрета/разрешения обнаружения радиомодуля и запрета/разрешения подключения к нему.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=4364