|
Навигация
|
Главная » Delphi Delphi и Bluetooth. Часть 3 (исходники)Источник: mobileservicesoft Петриченко Михаил Часть 1 :: Часть 2 ВступлениеИтак, в предыдущих частях мы научились получать список локальных радиомодулей 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 и установите следующие свойства:
Далее поместим компонент TTreeView и установите свойства как в таблице:
Правее TTreeView поместим TSplitter и установим следующие его свойства:
И, наконец, помещаем компонент TListView еще правее TSplitter. Устанавливаем его свойства как в таблице:
На TPanel поместим кнопку TButton.
Теперь мы готовы писать программу. Пишем кодПри старте нашей программы, желательно чтобы сразу заполнялся 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; Члены:
Объявление: 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; Члены:
Объявление: 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; Члены:
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; Параметры:
Возвращаемые значения:
Объявление функции: function BluetoothIsDiscoverable( const hRadio : THandle): BOOL; stdcall; Параметры:
Возвращаемые значения:
Объявление функции: function BluetoothEnumerateInstalledServices( hRadio : THandle; pbtdi : __PBLUETOOTH_DEVICE_INFO; var pcServices : dword; pGuidServices : PGUID): dword; stdcall; Параметры:
Возвращаемые значения:
Посмотрите на код получения списка сервисов: // Получаем размер массива сервисов ServiceCount := 0; Services := nil; hRadio := ASelected.Parent.ImageIndex; Info := ASelected.Data; // NEW FUNCTION BluetoothEnumerateInstalledServices(hRadio, Info, ServiceCount, nil); // Выделяем память. SetLength(Services, ServiceCount); // Получаем список сервисов BluetoothEnumerateInstalledServices(hRadio, Info, ServiceCount, PGUID(Services)) Сначала мы вызываем функцию с pcServices=0 и pGuidServices=nil для того, чтобы получить количество сервисов, предоставляемых устройством. Потом выделяем память (SetLength()) и только затем вызываем функцию с реальными параметрами и получаем список сервисов. Еще важное замечание. В файле JwaBluetoothAPIs.pas параметр pbtdi имеет тип PBLUETOOTH_DEVICE_INFO, который раскрывается в BLUETOOTH_DEVICE_INFO. Заметьте, что это НЕ УКАЗАТЕЛЬ. Это не верно, так как в исходном виде функция требует именно указатель. По-этому, я ввел тип type __PBLUETOOTH_DEVICE_INFO = ^PBLUETOOTH_DEVICE_INFO Так что ИСПОЛЬЗУЙТЕ файл из примера, а не из исходной библиотеки. Иначе получите нарушение доступа к памяти. Комментарий к коду: Мы перечитываем информацию об устройстве, так как за время, пока мы любуемся программой, могли произойти различные события: устройство отключили, отменили авторизацию и т. п. А мы хотим иметь самую свежую информацию об устройстве. В принципе то, что описано выше, мы уже знали, кроме двух указанных функций. Давайте расширим возможности нашего приложения. Добавим функции запрета/разрешения обнаружения радиомодуля и запрета/разрешения подключения к нему. BluetoothEnableIncomingConnections и BluetoothEnableDiscoverable Поместим на форму компонент TactionList и изменим его свойства как показано в таблице.
Теперь два раза щелкнем по ActionList и в появившемся окне редактора свойств добавим две TAction со следующими свойствами:
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; Параметры:
Возвращаемые значения:
Объявление функции: function BluetoothEnableDiscovery( hRadio : THandle; fEnabled : BOOL): BOOL; stdcall; Параметры:
Возвращаемые значения:
Добавим на Panel кнопку TButton с такими свойствами:
Теперь напишем такой обработчик событий OnUpdate у acProperty: procedure TfmMain.acPropertyUpdate(Sender: TObject); var SelectedNode: TTreeNode; SelectedItem: TListItem; begin SelectedNode := TreeView.Selected; SelectedItem := ListView.Selected; TAction(Sender).Enabled := Assigned(SelectedNode) and Assigned(SelectedItem) and (SelectedNode.ImageIndex > 0); end; И обработчик OnExecute для нее же: procedure TfmMain.acPropertyExecute(Sender: TObject); var Info: BLUETOOTH_DEVICE_INFO; begin Info := BLUETOOTH_DEVICE_INFO(ListView.Selected.Data^); BluetoothDisplayDeviceProperties(Handle, Info); end; Важно: В исходном виде в файле JwaBluetoothAPIs функция BluetoothDisplayDeviceProperties объявлена не верно. Второй параметр должен быть указателем, а там он передается как структура. Я исправил функцию так, чтобы он передавался как var-параметр (по ссылке). Используйте модуль JwaBluetoothAPIs из этого примера, чтобы не возникало ошибок доступа к памяти. Важно: Ни в этой процедуре, ни ранее, ни далее я не провожу проверку ошибок, чтобы не загромождать код лишними подробностями. В реальном приложении НЕОБХОДИМО проверять возвращаемые функциями значения и указатели. И так, в этом коде есть новая функция, выделенная жирным шрифтом. BluetoothDisplayDeviceProperty - функция выводит стандартное окно свойств устройства Bluetooth. Объявление функции: function BluetoothEnableDiscovery( hwndParent : HWND; var pbtdi : PBLUETOOTH_DEVICE_INFO): BOOL; stdcall; Важно: В оригинале (см. примечание выше) функция выглядит вот так: function BluetoothEnableDiscovery( hwndParent : HWND; pbtdi : PBLUETOOTH_DEVICE_INFO): BOOL; stdcall; Это не верно, так как в документации Microsoft указано, что параметр pbtdi должен передаваться как указатель (что подразумевает запись PBLUETOOTH_DEVICE_INFO), но как я писал выше, этот тип ошибочен. Он не является указателем. Я изменил функцию так, как показано выше (так она и должна быть, если не менять определение типа). Параметры:
Возвращаемые значения:
Выбор устройстваРассмотрим, как вызвать окно диалога выбора устройства.Добавим в наш проект на Panel еще одну кнопку TButton и установите ее свойства как в таблице:
Напишем вот такой обработчик события OnClick у этой кнопки: procedure TfmMain.btSelectClick(Sender: TObject); var ASelParams: BLUETOOTH_SELECT_DEVICE_PARAMS; ASelParamsSize: dword; begin ASelParamsSize := SizeOf(BLUETOOTH_SELECT_DEVICE_PARAMS); FillChar(ASelParams, ASelParamsSize, 0); with ASelParams do begin dwSize := ASelParamsSize; hwndParent := Handle; fShowRemembered := True; fAddNewDeviceWizard := True; end; BluetoothSelectDevices(@ASelParams); BluetoothSelectDevicesFree(@ASelParams); end В этой части кода две новые функции. BluetoothSelectDevices - функция разрешает/запрещает обнаружение локального радиомодуля Bluetooth. Объявление функции: function BluetoothSelectDevices( pbtsdp : PBLUETOOTH_SELECT_DEVICE_PARAMS): BOOL; stdcall; Параметры:
Если функция вернула TRUE, то пользователь выбрал устройства. Pbtsdp^.pDevices будет указывать на корректные данные. После вызова необходимо проверить флаги fAuthenticated и fRemembered, что бы удостовериться в корректности данных. Для освобождения памяти используйте функцию BluetoothSelectDevicesFree, только если функция вернет TRUE. Вернет FALSE если вызов прошел не удачно. Используйте GetLastError для получения дополнительных сведений. Возможные ошибки:
Объявление: 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; Члены:
Объявление функции: function BluetoothSelectDevices( pbtsdp : PBLUETOOTH_SELECT_DEVICE_PARAMS): BOOL; stdcall; Параметры:
Возвращаемые значения:
BluetoothSetServiceState - включает или выключает указанный сервис для устройства Bluetooth. Система проецирует сервис Bluetooth на соответствующий драйвер. При отключении сервиса - драйвер удаляется. При его включении - драйвер устанавливается. Если выполняется включение не поддерживаемого сервиса, то драйвер не будет установлен. Объявление функции: function BluetoothSetServiceState( hRadio : Thandle; var pbtdi : PBLUETOOTH_DEVICE_INFO; const pGuidService : TGUID; dwServiceFlags : DWORD): DWORD; stdcall; Параметры:
Возвращает ERROR_SUCCESS если вызов прошел успешно. Если вызов не удался вернет один из следующих кодов:
Важно: В оригинале (см. примечание выше) функция выглядит вот так: function BluetoothSetServiceState( hRadio : Thandle; pbtdi : PBLUETOOTH_DEVICE_INFO; const pGuidService : TGUID; dwServiceFlags : DWORD): DWORD; stdcall; Это не верно, так как в документации Microsoft указано, что параметр pbtdi должен передаваться как указатель (что подразумевает запись PBLUETOOTH_DEVICE_INFO), но как я писал выше, этот тип ошибочен. Он не является указателем. Я изменил функцию так, как показано выше (так она и должна быть, если не менять определение типа). Как использовать функцию? Давайте добавим к ActionList еще одну TAction с такими свойствами:
И добавим на Panel еще одну кнопку TButton, установив у нее следующие свойства:
В обработчике события 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; Параметры:
Возвращаемые значения:
И на Panel кнопку TButton со свойствами:
В обработчике 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; Параметры:
Возвращаемые значения:
Код: 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; Параметры:
Возвращаемые значения:
BluetoothAuthenticateMultipleDevices - позволяет авторизироваться сразу на нескольких устройствах при помощи одной копии "Мастера авторизации". Объявление функции: function BluetoothAuthenticateMultipleDevices( hwndParent : HWND; hRadio : THandle; cDevices : DWORD; rgpbtdi : __PBLUETOOTH_DEVICE_INFO): DWORD; stdcall; Параметры:
Возвращаемые значения:
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; Параметры:
Возвращаемые значения:
Объявление функции: function BluetoothUnregisterAuthentication( hRegHandle : HBLUETOOTH_AUTHENTICATION_REGISTRATION): BOOL; stdcall; Параметры:
Вернет TRUE, если вызов успешен и FALSE в случае неудачи. Используйте GetLastError для получения дополнительной информации. BluetoothSendAuthenticationResponse - эта функция должна вызываться из функции обратного вызова при запросе авторизации удаленным устройством для передачи PIN. Объявление функции: function BluetoothSendAuthenticationResponse( hRadio : THandle; pbtdi : PBLUETOOTH_DEVICE_INFO; pszPasskey : LPWSTR): DWORD; stdcall; Параметры:
Возвращаемые значения:
PFN_AUTHENTICATION_CALLBACK Описание этой функции дано выше. Здесь приведу лишь определеннее. Объявление функции: PFN_AUTHENTICATION_CALLBACK = function(pvParam : Pointer; pDevice : PBLUETOOTH_DEVICE_INFO): BOOL; stdcall; Параметры:
На этот раз все. Мы рассмотрели все функции Bluetooth API от Microsoft. Также мы научились управлять устройствами и радиомодулями Bluetooth, проводить авторизацию и получать информацию об этих устройствах. |
© 2024 Team.Furia.Ru.
Частичное копирование материалов разрешено. |