通用应用程序

使应用程序支持 OBEX

Uday Gupta

下载代码示例

过去十年来,蓝牙已成为在设备(例如手机、个人计算机和耳机)之间进行短距离无线通信的广泛使用的技术。蓝牙技术联盟 (Bluetooth Special Interest Group, BTSIG) 是定义蓝牙规范中的无线服务标准的行业组织。

其中一个规范是对象推送配置文件 (OPP),其用于将文件从一台设备发送到另一台设备。对象交换协议 (OBEX) 是 OPP 基础的一部分。OBEX 还可以在除 OPP 以外的其他配置文件中使用,因为它是在设备之间传输对象的通用协议。

对于要在应用程序中使用 OBEX 的开发人员,我已经基于在应用程序中提供 OBEX 的 Windows 运行时 (WinRT) 蓝牙 API 开发了一组 API。这些 API 作为通用应用程序的库程序包,表示 Windows 应用商店应用程序和 Windows Phone Silverlight 应用程序可以利用相同的 API 集。

OBEX 和 OPP

首先,了解什么是 OBEX 和 OPP 以及它们的工作原理很重要。OPP 可允许蓝牙设备向其他具有 OPP 功能的蓝牙设备发送文件或对象。OBEX 的用途是通过红外线通道进行文件共享。BTSIG 选择重新使用此协议,以通过蓝牙进行文件共享。除了底层传输媒体不同以外,基于红外线的 OBEX 和基于蓝牙的 OBEX 很相似。

OBEX 基于客户端服务器模型,在该模型中,接收方蓝牙设备运行侦听和接受客户端连接的 OBEX 服务器。客户端蓝牙设备连接到服务器蓝牙设备作为基于蓝牙的流通道。允许连接和对象传输的身份验证要求取决于使用 OBEX 的服务或应用程序。例如,OPP 可以允许未经身份验证的连接,目的是为了简化快速交换名片的过程。使用 OBEX 的其他服务可能只允许经过身份验证的连接。

文件可使用三种数据包进行共享。这些数据包称为初始 PUT、中间 PUT 和最终 PUT。初始 PUT 数据包标记文件传输开始,最终 PUT 标记传输完成。多个中间 PUT 数据包包含大量数据。在服务器接收每个 PUT 数据包后,会向客户端返回一个确认数据包。

通常情况下,OBEX 数据包发送方式如下:

  1. OBEX 客户端通过发送连接数据包连接到接收方设备。此数据包指定客户端可以接收的数据包大小上限。
  2. 从服务器收到响应表示连接已接受之后,OBEX 客户端即发送初始 PUT 数据包。此数据包包含描述对象的元数据,包括文件名和大小。(尽管 OBEX 协议允许此初始 PUT 数据包也可以包含对象数据,但是我开发的库中的 OBEX 实现不会在初始 PUT 数据包中发送任何数据)。
  3. 收到服务器确认已收到初始 PUT 数据包的响应后,OBEX 客户端将发送多个包含对象数据的 PUT 数据包。这些数据包的长度受根据服务器对步骤 1 中发送的连接数据包的响应设置的服务器可以接收的最大数据包大小限制。
  4. 最终 PUT 数据包包含最终 PUT 常量和最终对象数据区块。
  5. 文件共享完成后,OBEX 客户端发送断开连接数据包,并关闭蓝牙连接。OBEX 协议允许重复步骤 2 到步骤 3,以通过同一连接发送多个对象。

通过发送 ABORT 数据包,OBEX 客户端可以随时中止共享过程。共享将立即取消。在我编写的库中,我隐藏了 OBEX 协议实现详细信息,因此您只能看到高级 API。

OBEX 库

适用于 Windows 应用商店应用程序的蓝牙 OBEX 客户端库设计为面向 Windows 应用商店应用程序和 Windows Phone Silverlight 8.1 应用程序的可移植库。包含三个可以使库成为 OBEX 客户端运行时的 DLL。每个 DLL 设计用于处理特定任务:Bluetooth.Core.Service、Bluetooth.Core.Sockets 和 Bluetooth.Services.Obex。

蓝牙核心服务:文件 Bluetooth.Core.Service.dll 包含 Bluetooth.Core.Service 命名空间。此库旨在搜索和统计与客户端设备配对的附近蓝牙设备数量(请参阅图 1)。目前限制为一次性统计配对设备数。未来版本将包含一个监视器,以不断搜索其他蓝牙设备。

图 1 BluetoothService 的方法和关联事件

方法(包含参数) 关联事件
[静态] GetDefault 无关联事件
SearchForPairedDevicesAsync

成功 - SearchForDevicesSucceeded

失败 - SearchForPairedDevicesFailed

核心蓝牙服务用静态类 BluetoothService 表示(如图 2 所示)。此类含有以异步方式统计设备数量的 API。

图 2 统计配对设备数量的 BluetoothService

BluetoothService btService = BluetoothService.GetDefault();
btService.SearchForPairedDevicesFailed 
  += btService_SearchForPairedDevicesFailed;
btService.SearchForPairedDevicesSucceeded 
  += btService_SearchForPairedDevicesSucceeded;
await btService.SearchForPairedDevicesAsync();
void btService_SearchForPairedDevicesSucceeded(object sender,
  SearchForPairedDevicesSucceededEventArgs e)
{
  // Get list of paired devices from e.PairedDevices collection
}
void btService_SearchForPairedDevicesFailed(object sender,
  SearchForPairedDevicesFailedEventArgs e)
{
  // Get the failure reason from e.FailureReason enumeration
}

蓝牙核心套接字:文件 Bluetooth.Core.Sockets.dll 包含 Bluetooth.Core.Sockets 命名空间,旨在支持通过蓝牙连接的基于流的套接字操作。套接字功能通过 BluetoothSockets 类公开(请参阅图 3)。这既不是 TCP 套接字,也不是 UDP 套接字。与接收方设备发生的所有通信均通过 BluetoothSockets 完成。

图 3 BluetoothSockets 的方法和关联事件

方法(包含参数) 关联事件

Constructor(Bluetooth.Core.Services.BluetoothDevice)

Constructor(Bluetooth.Core.Services.BluetoothDevice, System.UInt32)

Constructor(Bluetooth.Core.Services.BluetoothDevice, System.String)

Constructor(Bluetooth.Core.Services.BluetoothDevice, System.UInt32, System.String)

无关联事件
PrepareSocketAsync

成功 – SocketConnected

失败 – ErrorOccured

SendDataAsync(System.Byte[])

SendDataAsync(System.String)

无关联事件
CloseSocket SocketClosed
无关联方法 DataReceived

蓝牙服务 Obex:Bluetooth.Services.Obex.dll 文件包含 Bluetooth.Services.Obex 命名空间。这是 OBEX 的核心实现,通过类 ObexService 公开。此类提供蓝牙 OPP 规范的抽象视图。它将公开帮助连接、发送以及从接收方蓝牙设备断开连接的方法。图 4 列出了此类公开的 API 及关联事件,图 5 介绍了使用情况。

图 4 ObexService 的方法和关联事件

方法(包含参数) 关联事件
[静态] GetDefaultForBluetoothDevice(Bluetooth.Core.Services.BluetoothDevice) 无关联事件
ConnectAsync

成功 – DeviceConnected

失败 – ConnectionFailed

SendFileAsync(Windows.Storage.IStorageFile)

成功:

ServiceConnected

DataTransferProgressed

DataTransferSucceeded

Disconnecting

Disconnected

失败:

ConnectionFailed

DataTransferFailed

AbortAsync Aborted

图 5 Obex 服务使用情况

protected async override void OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
  ObexService obexService = ObexService.GetDefaultForBluetoothDevice(null);
  obexService.DeviceConnected += obexService_DeviceConnected;
  obexService.ServiceConnected += obexService_ServiceConnected;
  obexService.ConnectionFailed += obexService_ConnectionFailed;
  obexService.DataTransferProgressed += obexService_DataTransferProgressed;
  obexService.DataTransferSucceeded += obexService_DataTransferSucceeded;
  obexService.DataTransferFailed += obexService_DataTransferFailed;
  obexService.Disconnecting += obexService_Disconnecting;
  obexService.Disconnected += obexService_Disconnected;
  obexService.Aborted += obexService_Aborted;
  await obexService.ConnectAsync();
}
async void obexService_DeviceConnected(object sender, EventArgs e)
{
  // Device is connected, now send file
  await (sender as ObexService).SendFileAsync(fileToSend);
}
void obexService_ServiceConnected(object sender, EventArgs e)
{
  // Device connected to Obex Service on target device
}
void obexService_ConnectionFailed(object sender, 
  ConnectionFailedEventArgs e)
{
  // Connection to service failed
}
void obexService_DataTransferProgressed(object sender, 
  DataTransferProgressedEventArgs e)
{
  // Get data transfer progress from DataTransferProgressedEventArgs
}
void obexService_DataTransferSucceeded(object sender, EventArgs e)
{
  // Data transfer succeeded
}
void obexService_DataTransferFailed(object sender, DataTransferFailedEventArgs e)
{
  // Data transfer failed, get the reason from DataTransferFailedEventArgs
}
void obexService_Disconnecting(object sender, EventArgs e)
{
  // Device is disconnecting from service
  }
void obexService_Disconnected(object sender, EventArgs e)
{
  // Device is now disconnected from targeted device and service
}
void obexService_Aborted(object sender, EventArgs e)
{
  // Data transfer operation is aborted
}

使用这些库的典型使用环境如下:

  • 通过使用 Bluetooth.Core.Service 中的 API,统计所有配对的、具有 OPP 功能的蓝牙设备数量。已实现 OPP 功能检查。
  • 获取您共享文件需要使用的 BluetoothDevice 实例。
  • 通过将 BluetoothDevice 实例传递到工厂方法,获取接收方蓝牙设备的 ObexService 实例。ObexService 类将在内部创建 BluetoothSocket 实例,而 ObexService 就是在 BluetoothSocket 的基础上进行文件共享。
  • 文件共享完成后,ObexService 将自动断开连接。

入门

由于我的库面向 Windows 应用商店应用程序和 Windows Phone 8.1 应用程序,因此我将从一个通用应用程序开始。这是开发适用于所有 Windows 设备的应用程序的好方法。要了解有关通用应用程序的详细信息,请访问 bit.ly/1h3AQeu。要从通用应用程序开始,我将使用 Visual Studio 2013,并在“应用商店应用程序”节点下创建新的通用应用程序项目(请参阅图 6)。我使用的是 Visual C#,您可以使用 Visual Basic 和 Visual C++。

用于创建新项目的空白通用应用程序
图 6 用于创建新项目的空白通用应用程序

在我开始对蓝牙 OBEX 客户端应用程序进行编程之前,我将更新两个项目(适用于 Windows 8.1 和 Windows Phone 8.1 的应用程序)的 package.appxmanifest 文件:

<Capabilities>
  <m2:DeviceCapability Name="bluetooth.rfcomm">
    <m2:Device Id="any">
      <m2:Function Type="name:obexObjectPush"/>
    </m2:Device>
  </m2:DeviceCapability>
</Capabilities>

为了应用此更新,我通过在解决方案资源管理器的上下文菜单中选择“视图代码”,将 package.appxmanifest 作为代码文件打开。我将此代码段放在 <Application> 代码的末尾。要提供在此应用程序中使用蓝牙无线射频通信 (RFCOMM) 服务的设备级功能需要该代码段。我还指定了与此设备兼容的所有服务和设备类型。

对我的方案,我需要与所有具有 OPP 功能的设备相兼容的设备支持 obexObjectPush。有关 Windows 8.1 和 Windows Phone 8.1 上受支持的配置文件的更多信息,请访问 bit.ly/1pG6rYO。如果未提及该功能,则设备枚举将失败,并出现 CapabilityNotDefined 枚举常量。

在开始编码之前,我将向之前提及的三个库文件添加引用,因此我可以在这些库中使用 OBEX 实现。我需要分别向两个项目的这些库中添加引用。如果未添加引用,项目将无法使用任何功能。

我将遵循以下编码模式和做法:

  • 在 Windows 项目中,实现适用于 Windows Store 8.1 应用程序的 UX 设计。
  • 在 Windows Phone 项目中,实现适用于 Windows Phone 8.1 应用程序的 UX 设计。
  • 在共享项目中实现常见功能。
  • 通过特定于平台的编译器常量,在共享项目中执行特定于平台的实现。对于 Windows 8.1,我将使用 WINDOWS_APP。对于 Windows Phone 8.1,我将使用 WINDOWS_PHONE_APP。这些编译器常量已定义为项目的一部分。

下载示例代码以获得有关这些库的实践经验,以及开发通用应用程序应遵循的编码做法。图 7 显示了示例项目的解决方案资源管理器窗口,其中包含文件结构和模式。

BluetoothDemo 应用程序的解决方案资源管理器
图 7 BluetoothDemo 应用程序的解决方案资源管理器

枚举配对设备

在与配对设备共享文件之前,我需要查看配对设备列表,并选择目标。我将处理 Bluetooth.Core.Services.BluetoothService 实例,其表示我的设备提供的核心蓝牙服务。我使用 GetDefault 静态工厂方法获取此实例,因为每台设备仅有一个可用的蓝牙服务。

对于枚举,我将调用 SearchForPairedDevicesAsync 方法。此方法将枚举与我的设备配对的设备。对于 Windows Store 8.1 应用程序,我需要允许使用配对设备以获取枚举的设备。如果不允许使用此方法,则不会枚举配对设备。

如果该 API 成功,将引发 SearchForPairedDevicesSucceeded 事件,并从其事件参数中提取配对设备的集合。否则,将引发 SearchForPairedDevicesFailed 事件,且其事件参数中包含失败枚举常量。图 8 显示了枚举设备的代码。

图 8 枚举配对设备

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
  await EnumerateDevicesAsync();
}
public async Task EnumerateDevicesAsync()
{
  BluetoothService btService = BluetoothService.GetDefault();
  btService.SearchForPairedDevicesFailed +=
    btService_SearchForPairedDevicesFailed;
  btService.SearchForPairedDevicesSucceeded +=
    btService_SearchForPairedDevicesSucceeded;
  await btService.SearchForPairedDevicesAsync();
}
void btService_SearchForPairedDevicesSucceeded(object sender,
  SearchForPairedDevicesSucceededEventArgs e)
{
  (sender as BluetoothService).SearchForPairedDevicesFailed -=
    btService_SearchForPairedDevicesFailed;
  (sender as BluetoothService).SearchForPairedDevicesSucceeded -=
    btService_SearchForPairedDevicesSucceeded;
  this.cvBtDevices.Source = e.PairedDevices;
}
void btService_SearchForPairedDevicesFailed(object sender,
  SearchForPairedDevicesFailedEventArgs e)
{
  (sender as BluetoothService).SearchForPairedDevicesFailed -=
    btService_SearchForPairedDevicesFailed;
  (sender as BluetoothService).SearchForPairedDevicesSucceeded -=
    btService_SearchForPairedDevicesSucceeded;
  txtblkErrorBtDevices.Text = e.FailureReason.ToString();
}

我还在 Windows 8.1 的 BottomAppBar 和 Windows Phone 8.1 的 ApplicationBar 中提供了扫描按钮。这样,在出现新的配对设备时,应用程序用户可以重新扫描设备。

在 Windows Phone 8.1 上枚举设备时,将枚举所有具有 OBEX 功能的设备,无论其是否实际存在以及蓝牙无线是否已打开。但是,在 Windows 8.1 上枚举设备时,将只列出 Windows 8.1 附近实际存在且蓝牙无线已打开的设备。

在枚举配对设备后,我可以选择要共享文件的设备。每个设备都表示为 Bluetooth.Core.Services.BluetoothDevice 类的对象。该对象包含连接详细信息和配对设备的显示名称。Bluetooth.Services.Obex.ObexService 将内部使用连接详细信息来创建 Bluetooth.Core.Sockets.BluetoothSocket 实例,并连接到配对设备。

使用 ObexService

获取表示要共享文件的目标设备的 Bluetooth.Core.Services.BluetoothDevice 对象的实例后,我可以通过 OPP 使用 Bluetooth.Services.Obex.ObexService 进行文件共享。我还需要文件列表,以便对要共享的文件进行排队。在代码示例中,我只提供了少量文件。否则,我可以使用 Windows.Storage.Pickers.FileOpenPicker(请参阅 bit.ly/1qtiLeh)或 Windows.Storage.ApplicationData.Current.LocalFolder(请参阅 bit.ly/1qtiSGI)中的自定义逻辑来选择多个文件。

目前,每次连接只能共享一个文件。当共享完成后,与目标设备的连接即关闭。如果需要发送多个文件,我需要多次处理 Bluetooth.Services.Obex.ObexService 实例。我可以通过静态工厂方法 GetDefaultForBluetoothDevice(Bluetooth.Core.Services.BluetoothDevice) 来获取此实例。此方法将返回表示设备上的 Obex 服务的 Bluetooth.Services.Obex.ObexService 的单一实例。

为了表示要共享的文件,我将使用 FileItemToShare 类。该类包含文件名称、路径和大小以及表示磁盘上文件实例的 Windows.Storage.IStorageFile 实例(请参阅 bit.ly/1qMcZlB)。我将根据数据结构对象对要共享的所有文件进行排队。在共享多个文件时,列表中的第一个文件是当前正在共享的文件。共享完成后,文件将从列表中删除。图 9 展示了如何挂接 ObexService 及其事件,以便共享文件。

图 9 挂接 ObexService 及其事件

ObexService obexService = null;
BluetoothDevice BtDevice = null;
ObservableCollection<FileItemToShare> filesToShare = null;
async override void OnNavigatedTo(NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
  if (e.Parameter == null || !(e.Parameter is BluetoothDevice))
  {
    MessageDialog messageBox = new MessageDialog(
      "Invalid navigation detected. Moving to Main Page", "Bluetooth Hub");
    messageBox.Commands.Add(new UICommand("OK", (uiCommand) =>
    {
      this.Frame.Navigate(typeof(MainPage));
    }));
    await messageBox.ShowAsync();
    return;
  }
  BtDevice = e.Parameter as BluetoothDevice;
  filesToShare = GetFilesFromLocalStorage();
  this.cvFileItems.Source = filesToShare;
  obexService = ObexService.GetDefaultForBluetoothDevice(BtDevice);
  obexService.Aborted += obexService_Aborted;
  obexService.ConnectionFailed += obexService_ConnectionFailed;
  obexService.DataTransferFailed += obexService_DataTransferFailed;
  obexService.DataTransferProgressed += obexService_DataTransferProgressed;
  obexService.DataTransferSucceeded += obexService_DataTransferSucceeded;
  obexService.DeviceConnected += obexService_DeviceConnected;
  obexService.Disconnected += obexService_Disconnected;
  obexService.Disconnecting += obexService_Disconnecting;
  obexService.ServiceConnected += obexService_ServiceConnected;
  await obexService.ConnectAsync();
}

当我调用 ConnectAsync 方法时,ObexService 对象从在工厂方法中传递的 BluetoothDevice 对象中获取连接属性。它尝试通过蓝牙通道创建与目标 BluetoothDevice 的连接。连接成功后,将引发 DeviceConnected 事件。图 10 显示了 ObexService 的 DeviceConnected 事件处理程序。

图 10 DeviceConnected 事件处理程序方法

async void obexService_DeviceConnected(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    System.Diagnostics.Debug.WriteLine("device connected");
    if (filesToShare.Count > 0)
    {
      filesToShare.ShareStatus = FileShareStatus.Connecting;
      await obexService.SendFileAsync(filesToShare[0].FileToShare);
    }
    ...
  });
}

设备连接到目标设备后,我就从文件列表的索引 0 开始共享文件。通过调用 SendFileAsync(Windows.Storage.IStorageFile) 和传递由类型 FileToShare 数据结构的对象中的 IStorageFile 表示的文件对象来共享文件。调用此方法后,ObexService 将尝试连接到在目标设备上运行的 OBEX 服务器。如果连接成功,将引发 ServiceConnected 事件。否则,将引发 ConnectionFailed 事件。此代码显示了 ServiceConnected 事件处理程序:

async void obexService_ServiceConnected(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    System.Diagnostics.Debug.WriteLine("service connected");
    filesToShare[0].ShareStatus = FileShareStatus.Sharing;
  });
}

图 11 显示了 ObexService 的 ConnectionFailed 事件处理程序。

图 11 ConnectionFailed 事件处理程序方法

async void obexService_ConnectionFailed(object sender, 
  ConnectionFailedEventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    System.Diagnostics.Debug.WriteLine("connection failed");
    filesToShare[0].ShareStatus = FileShareStatus.Error;
    filesToShare[0].Progress = 0;
    FileItemToShare currentItem = filesToShare[0];
    filesToShare.RemoveAt(0);
    filesToShare.Add(currentItem);
  });
}

当我的设备连接到目标设备的 OBEX 服务器时,文件共享过程开始。文件共享进度可以在 DataTransferProgressed 事件中确定。以下代码显示了 DataTransferProgressed 方法:

async void obexService_DataTransferProgressed(object sender,
  DataTransferProgressedEventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    System.Diagnostics.Debug.WriteLine("Bytes {0}, Percentage {1}",
      e.TransferInBytes, e.TransferInPercentage);
    filesToShare[0].Progress = e.TransferInBytes;
  });
}

文件共享完成后,将引发 DataTransferSucceeded 事件。如果文件共享不成功,将引发 DataTransferFailed 事件。以下代码显示了 DataTransferSucceeded 事件处理程序:

async void obexService_DataTransferSucceeded(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    System.Diagnostics.Debug.WriteLine("data transfer succeeded");
    filesToShare.RemoveAt(0);
  });
}

如果文件共享出错,将引发 DataTransferFailed 事件。以下代码显示了事件处理程序:

async void obexService_DataTransferFailed(object sender,
  DataTransferFailedEventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    System.Diagnostics.Debug.WriteLine("Data transfer failed {0}",
      e.ExceptionObject.ToString());
    filesToShare[0].ShareStatus = FileShareStatus.Error;
    filesToShare[0].Progress = 0;
    FileItemToShare fileToShare = this.filesToShare[0];
    filesToShare.RemoveAt(0);
    this.filesToShare.Add(fileToShare);
  });
}

数据传输完成后,将从列表中删除共享的文件,且 ObexService 将断开连接。ObexService 断开连接时,将引发 Disconnecting 事件。同时,正确断开连接后,将引发 Disconnected 事件。Disconnecting 事件处理程序如下所示:

void obexService_Disconnecting(object sender, EventArgs e)
{
  System.Diagnostics.Debug.WriteLine("disconnecting");
}

成功断开连接后,图 12 中的代码将处理引发的 Disconnected 事件。

图 12 Disconnected 事件处理程序方法

async void obexService_Disconnected(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    System.Diagnostics.Debug.WriteLine("disconnected");
    obexService.Aborted -= obexService_Aborted;
    obexService.ConnectionFailed -= obexService_ConnectionFailed;
    obexService.DataTransferFailed -= obexService_DataTransferFailed;
    obexService.DataTransferProgressed -= 
      obexService_DataTransferProgressed;
    obexService.DataTransferSucceeded -= 
      obexService_DataTransferSucceeded;
    obexService.DeviceConnected -= obexService_DeviceConnected;
    obexService.Disconnected -= obexService_Disconnected;
    obexService.Disconnecting -= obexService_Disconnecting;
    obexService.ServiceConnected -= obexService_ServiceConnected;
    obexService = null;
    if (filesToShare.Count.Equals(0))
    {
      ...
      MessageDialog messageBox =
        new MessageDialog("All files are shared successfully",
        "Bluetooth DemoApp");
      messageBox.Commands.Add(new UICommand("OK", (uiCommand) =>
      {
        this.Frame.Navigate(typeof(MainPage));
      }));
      await messageBox.ShowAsync();
    }
    else
    {
      obexService = ObexService.GetDefaultForBluetoothDevice(BtDevice);
      obexService.Aborted += obexService_Aborted;
      obexService.ConnectionFailed += obexService_ConnectionFailed;
      obexService.DataTransferFailed += obexService_DataTransferFailed;
      obexService.DataTransferProgressed += 
        obexService_DataTransferProgressed;
      obexService.DataTransferSucceeded += 
        obexService_DataTransferSucceeded;
      obexService.DeviceConnected += obexService_DeviceConnected;
      obexService.Disconnected += obexService_Disconnected;
      obexService.Disconnecting += obexService_Disconnecting;
      obexService.ServiceConnected += obexService_ServiceConnected;
      await obexService.ConnectAsync();
    }
  });
}

引发 Disconnected 事件后,将删除所有处理程序,并清理 ObexService 实例。在数据传输期间,可能会出现需要中止当前传输的情况。要中止当前传输,请调用 AbortAsync。使用以下代码引发 Aborted 事件,随后指向目标设备的连接结束:

async void obexService_Aborted(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    System.Diagnostics.Debug.WriteLine("Aborted");
    if (!filesToShare.Count.Equals(0))
    {
      filesToShare.RemoveAt(0);
    }
  });
}

总结

现在,演示应用程序即已完成。通用应用程序的概念实际上可以帮助您为多个 Windows 平台和外观造型编写一个代码段,从而减少整体的开发工作。

我在许多 Windows 应用商店和 Windows Phone 的应用程序中使用了这些库。搜索 Code Create(一款免费 Windows Phone 应用程序)或 OBEX(通用应用程序),初步了解这些 API 如何与应用程序结合使用。可从 NuGet 存储库下载这些库。只需直接从 Visual Studio Solution 中的 NuGet 在线对话中搜索“适用于应用商店应用程序的蓝牙 OBEX”,并导入这些库作为项目参考。


Uday Gupta 是印度 Symphony Teleca Corp. Pvt Ltd. 的产品开发高级工程师。他在 .NET 技术方面,尤其 Windows Presentation Foundation、Silverlight、Windows Phone 和 Windows 8.x 方面拥有丰富的经验。

衷心感谢以下 Microsoft 技术专家对本文的审阅:Jeff Kelley 和 Guruprasad Subbarayan