Pull to refresh

Перевод: 30 дней Windows Mobile — день второй (Winforms/С# vs WinAPI/C)

Reading time 6 min
Views 1.6K
Original author: Chris Craft и Christopher Fairbairn
Итак, продолжаем перевод цикла статей 30 дней .NET [Windows Mobile]. Напоминаю, что для большего интереса переводятся сразу две статьи — из блога Криса Крафта (Windows Forms — C#) и Кристофера Фэрбейрна (WinAPI — C). На очереди день второй — управление bluetooth. Предыдущая статья из цикла —
http://habrahabr.ru/blogs/mobiledev/61248/.


Крис Крафт. C#


Оригинал находится здесь.

Как известно, постоянно включенный Bluetooth изрядно уменьшает время жизни телефона. В стандартной поставке Windows Mobile отсутствует удобный способ включать и выключать bluetooth, а по дороге на работу так хочется легко и просто включить его, чтобы получить возможность разговаривать через гарнитуру! Важная особенность — для этого переключения очень не хочется, чтобы было необходимо смотреть на экран.

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

Итак, в соответствии с обратным отсчетом до полуночи, у меня осталось 2 часа 21 минута и 6 секунд, чтобы разработать данное приложение.

Менеджер Bluetooth



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

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

Единственная сложность при разработке — необходимость использовать P/Invoke для работы с Bluetooth. Вот необходимые объявления:
[DllImport("BthUtil.dll")]
private static extern int BthGetMode(out RadioMode dwMode);

[DllImport("BthUtil.dll")]
private static extern int BthSetMode(RadioMode dwMode);


* This source code was highlighted with Source Code Highlighter.


Кроме этого, я также воспользовался брокером состояний и уведомлений (SNAPI). Суть в том, что если состояние bluetooth будет изменяться за пределами нашего приложения, без SNAPI мы об этом не узнаем. Можно подписаться практически на любые уведомления. Мы же подпишемся только на bluetooth:
SystemState bluetoothStatePowerOn = new SystemState(SystemProperty.BluetoothStatePowerOn);
  bluetoothStatePowerOn.Changed += new ChangeEventHandler(bluetoothStatePowerOn_Changed);

  void bluetoothStatePowerOn_Changed(object sender, ChangeEventArgs args)
  {
    UpdateScreen();
  }


* This source code was highlighted with Source Code Highlighter.


Ну и напоследок нам необходимо разобраться с автоматическим завершением приложения. Всё очень просто — есть таймер, который ждёт минуту, после чего выполняется цикл, на каждой итерации которого вызывается Thread.Sleep(1000). Это создает отличный эффект и позволяет пользователю понять, что приложение не закончилось аварийно. (Прим. пер.: если честно, совершенно не понял, как эти 10 секунд могут помочь пользователю и чем :) UPD: разобрались, в течение 10 секунд показывается сообщение о том, что приложение выключится через Х секунд.)

Исходный код С#: bluetoothManager.zip.

Кристофер Фэрбейрн. WinAPI — C


Оригинал находится здесь.

Доступ к Bluetooth


Начну с того, что приложение работает только на тех устройствах, которые используют Microsoft Bluetooth Stack. В отличие от других аспектов платформы, для bluetooth нет фиксированных стандартов. Каждый производитель оборудования может выбирать всё, что посчитает оптимальным для своих целей.

Для использования Microsoft Bluetooth API необходимо подключить заголовочный файл «bthutil.h» и библиотеку «bthutil.lib» — см. документацию.

Для изменения состояния Bluetooth воспользуемся функцией BthSetMode:
// Выключим Bluetooth<br/>
BthSetMode(BTH_POWER_OFF);


* This source code was highlighted with Source Code Highlighter.


Данная функция принимает следующие константы в качестве параметра:
Константа Описание
BTH_POWER_OFF Выключить Bluetooth
BTH_CONNECTABLE Bluetooth включен и другие устройства могут подключаться к устройству
BTH_DISCOVERABLE Bluetooth включен и другие устройства могут обнаруживать и подключаться к устройству


Также мы можем получить текущее состояние Bluetooth с помощью BthGetMode:
DWORD dwMode = BTH_POWER_OFF;
BthGetMode(&dwMode);

if (dwMode == BTH_CONNECTABLE)
{
 // Сделаем что-нибудь, если bluetooth включен
}


* This source code was highlighted with Source Code Highlighter.


Использование брокера состояний и уведомлений


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

Брокер состояний и уведомлений (SNAPI) построен с использованием механизма мониторинга ключей реестра. Нам потребуются следующие заголовочные файлы: «regext.h», обеспечивающий мониторинг реестра, и «snapi.h», содержащий объявления необходимых ключей с системной информацией.

Воспользуемся функцией RegistryNotifyWindow из «regext.h». Она осуществляет мониторинг ключа реестра и отправляет сообщение указанному окну в случае, если произошло изменение значения.

HREGNOTIFY hregNotify;

// Нас интересует изменение значения ключа реестра.<br/>
NOTIFICATIONCONDITION condition = {
 REG_CT_ANYCHANGE,
 SN_BLUETOOTHSTATEPOWERON_BITMASK, 0
};

// Мы хотим следить за ключом BLUETOOTHSTATEPOWERON<br/>
// и получать сообщение WM_BLUETOOTH_STATE_CHANGE<br/>
// в окно 'hdlg'.<br/>
RegistryNotifyWindow(SN_BLUETOOTHSTATEPOWERON_ROOT,
 SN_BLUETOOTHSTATEPOWERON_PATH,
 SN_BLUETOOTHSTATEPOWERON_VALUE,
 hDlg,
 WM_BLUETOOTH_STATE_CHANGED,
 0,
 &condition,
 &hregNotify);


* This source code was highlighted with Source Code Highlighter.


Изменяя параметры структуры NOTIFICATIONCONDITION мы можем подписаться и на более сложные события, включая получение уведомления в случае, например, превышения некоего заданного порога.

Использование кнопок на экране


В приложении используется две кнопки. Когда пользователь нажимает любую из них, диалогу отправляется сообщение WM_COMMAND:

case WM_COMMAND:
  // Определяем, какая кнопка нажата<br/>
  switch (LOWORD(wParam))
  {
   case IDC_BUTTON_POWERON:
    // Вкл<br/>
    break;

   case IDC_BUTTON_POWEROFF:
    // Выкл<br/>
    break;
  }
  break;


* This source code was highlighted with Source Code Highlighter.


Текстовое поле


Для изменения значения текстового поля (TextBox) можно воспользоваться функцией SetWindowText, совсем как для статического контрола:

HWND hWndCtrl = GetDlgItem(hDlg, IDC_EDIT_LOG);
SetWindowText(hWndCtrl, L"This is the new content");


* This source code was highlighted with Source Code Highlighter.


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

// Перемещаем курсор в конец<br/>
SendMessage(hWndCtrl, EM_SETSEL, -1, -1);

// Заменяем выделение своим содержимым<br/>
// to append to the end of the edit control<br/>
SendMessage(hWndCtrl, EM_REPLACESEL, FALSE,
 (LPARAM)L"Text to add");


* This source code was highlighted with Source Code Highlighter.


Выводим изображение


В Compact Framework можно обойтись PictureBox контролом. Мы же продолжим пользоваться статическим контролом, отправляя ему сообщение STM_SETIMAGE:

//Получаем контрол<br/>
HWND hWndCtrl = GetDlgItem(hDlg, IDC_STATUS_BITMAP);

// Загружаем изображение из ресурсов<br/>
HBITMAP hBitmap = LoadBitmap(GetModuleHandle(NULL),
 MAKEINTRESOURCE(IDB_POWER_OFF));

// посылаем сообщение STM_SETIMAGE

// статическому контролу<br/>
SendMessage(hWndCtrl, STM_SETIMAGE, IMAGE_BITMAP,
 (LPARAM)hBitmap);


* This source code was highlighted with Source Code Highlighter.


Напоследок сделаем так, чтобы на изображение можно было нажимать. По умолчанию статические контролы не обрабатывают нажатия. Чтобы добиться этого, в редакторе ресурсов необходимо у статического контрола выставить значение свойства Notify в true. В этом случае WM_COMMAND будет отправляться при нажатии на изображение.

Тестовое приложение


Скачать: bluetoothmanager.zip — 99Кб

В силу того, что работать с bluetooth через брокер состояний и уведомлений (SNAPI) стало возможно только в Windows Mobile 6.0, в идеале было бы неплохо установить ограничение на минимальную версию ОС в установочном cab-файле, чтобы избежать установки на более старые версии.

Также в качестве домашнего задания попытайтесь скачать Broadcom Bluetooth SDK и модифицировать приложение так, чтобы оно работало на этом стеке протокола Bluetooth. (Прим.пер.: А лучше сразу на обоих стеках! :))

Tags:
Hubs:
+14
Comments 11
Comments Comments 11

Articles