文章目錄
在之前的兩篇文章分別講述了在.NET Compact Framework下使用Windows Embedded Source Tools for Bluetooth和32feet.NET進行Bluetooth的開發,連結如下:
.NET Compact Framework下的Bluetooth開發 之 Windows Embedded Source Tools for Bluetooth
.NET Compact Framework下的Bluetooth開發 之 32feet.NET
在這篇文章講述Bluetooth Virtual Serial Port的開發,所謂Bluetooth Virtual Serial Port,其實是從軟體的角度看,把Bluetooth的通訊轉化成Serial Port(串口)。經過這樣的轉換後,使用Bluetooth的Client程式可以像使用串口一樣操作Bluetooth。這個應用方式的出現是為了支援現有應用(Legacy system,遺產應用)。舉個例子,在Bluetooth出現以前,大部分行動裝置都是通過串口串連的GPS receiver的,基於GPS應用程式的開發也就通過的串口通訊取出NMEA data。關於GPS NMEA data的開發可以參考 .NET Compact Framework下的GPS NMEA data資料分析 。串口的開發可以參考.NET Compact Framework下的串口通訊。隨著Bluetooth的普及,行動裝置可以通過Bluetooth來串連GPS receiver了,那麼原先基於GPS的應用程式需要重新開發通訊部分去讀取NMEA data,這為現有應用帶來很多麻煩,所有的現有應用都需要重寫通訊部分,因此人們想出解決方案,把Bluetooth的通訊轉化成Serial Port(串口)。硬體上使用Bluetooth來進行通訊,在軟體上虛擬一個串口給應用程式,應用程式不需要任何的修改就可以支援Bluetooth的GPS Receiver了。這就像設計模式裡面的Adapter模式,但是這裡是為新裝置提供原有的介面,使得原先的Client不需要更改。
由於Bluetooth Virtual Serial Port的出現基於對現有系統(Legacy System)支援的需求,所以對於新的系統,MS不推薦使用Bluetooth Virtual Serial Port,而是直接使用Winsock進行通訊。在使用Winsock進行Bluetooth通訊需要指定服務,因此可以指定使用串口服務進行通訊。Bluetooth Virtual Serial Port和Winsock的Bluetooth通訊都是使用RFCOMM協議,所以兩者等同。使用Winsock的Bluetooth通訊比Bluetooth Virtual Serial Port更簡單,不需要配置。而且更強壯(robust),因為使用Winsock的Bluetooth通訊可以直接監聽到藍牙裝置關閉或者離開通訊範圍,而Bluetooth Virtual Serial Port只能通過Timeout來檢查。
由於支援現有系統(Legacy System),Bluetooth Virtual Serial Port還是有存在的價值,下面講述Bluetooth Virtual Serial Port的開發。在Windows Mobile下有兩種方法可以建立Bluetooth Virtual Serial Port:調用API建立Bluetooth Virtual Serial Port和修改註冊表建立Bluetooth Virtual Serial Port
調用API建立Bluetooth Virtual Serial Port
調用API建立Bluetooth Virtual Serial Port可以調用RegisterDevice
HANDLE h = RegisterDevice (L"COM", index, L"btd.dll", (DWORD)&pp);
建立服務連接埠,需要配置以下參數
PORTEMUPortParams pp;
memset (&pp, 0, sizeof(pp));
pp.flocal = TRUE;
pp.channel = channel & 0xff;
服務連接埠flocal為true。channel可以使用RFCOMM_CHANNEL_MULTIPLE (0xfe),這樣RFCOMM 會自動分配可用的通道。
建立用戶端口,需要配置以下參數
PORTEMUPortParams pp;
memset (&pp, 0, sizeof(pp));
pp.device = ba;
pp.channel = channel & 0xff;
服務連接埠flocal為false。device為服務端地址。
反註冊連接埠使用以下API
DeregisterDevice (h);
詳細可以參考MSDN文章 Creating a Connection to a Remote Device Using a Virtual COM Port,連結見參考文獻。
在32feet.net裡面,這些API封裝在InTheHand.Net.Ports.BluetoothSerialPort,可以直接使用。但是32feet.net的作者提醒這些API是不是可信賴的(unreliable),所以在使用之前請要謹慎考慮和詳細測試。我在wince 5下測試過,不能成功建立Bluetooth Virtual Serial Port。
使用32feet.net建立服務連接埠
public static void CreateIncomingPort()
{
BluetoothSerialPort port = BluetoothSerialPort.CreateServer(BluetoothService.SerialPort);
Console.WriteLine(port.PortName);
}
使用32feet.net建立用戶端口
public static void CreateIncomingPort()
{
BluetoothClient client = new BluetoothClient();
BluetoothDeviceInfo[] devices = client.DiscoverDevices();
BluetoothDeviceInfo device = null;
foreach (BluetoothDeviceInfo d in devices)
{
if (d.DeviceName == "BLUETOOTH_DEVICE")
{
device = d;
break;
}
}
BluetoothEndPoint endPoint = new BluetoothEndPoint(device.DeviceAddress, BluetoothService.SerialPort);
BluetoothSerialPort port = BluetoothSerialPort.CreateClient(endPoint);
Console.WriteLine(port.PortName);
}
修改註冊表建立Bluetooth Virtual Serial Port
由於第一種方法不是很可靠,所以可以選擇第二種方法,第二種方法其實就是修改註冊表,把
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Bluetooth\\Serial\\Ports的項進行修改。可是這個方法只是支援windows mobile5以上的系統,不支援wince5。同時,如果使用這個方法,需要重啟系統。
這是Windows Mobile的註冊表,可以通過程式修改註冊表的項目,通過程式修改後需要重啟Windows Mobile。
為Wince 5的註冊表,結構不一樣,在Wince5不能通過修改註冊表的方式實現Bluetooth Virtual Serial Port。
下面的代碼來源自32feet.net的BluetoothDeviceInfo類裡面的SetServiceState()方法。這裡示範了如何修改註冊表。
if (state)
{
//write registry settings for WM5 Serial Port support
//get available ports
Microsoft.Win32.RegistryKey rkPorts = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Bluetooth\\Serial\\Ports", true);
string[] supportedPorts = (string[])rkPorts.GetValue("SupportedPorts");
System.Collections.ArrayList alPorts = new System.Collections.ArrayList(supportedPorts);
//check availability
foreach (string deviceid in rkPorts.GetSubKeyNames())
{
Microsoft.Win32.RegistryKey rkDevice = rkPorts.OpenSubKey(deviceid);
//remove port from arraylist if unavailable
string port = rkDevice.GetValue("Port").ToString();
int nullPos = port.IndexOf('\0');
if (nullPos > -1)
{
port = port.Substring(0, nullPos);
}
if (alPorts.Contains(port))
{
alPorts.Remove(port);
}
rkDevice.Close();
}
if (alPorts.Count == 0)
{
throw new InvalidOperationException("No ports available");
}
//write port details to registry
Microsoft.Win32.RegistryKey rkNewPort = rkPorts.CreateSubKey(this.DeviceAddress.ToString("8"));
rkNewPort.SetValue("KeepDCB", 0);
rkNewPort.SetValue("RemoteDCB", 0);
rkNewPort.SetValue("Encryption", 0);
rkNewPort.SetValue("Authentication", 0);
rkNewPort.SetValue("Port", alPorts[0]);
rkNewPort.SetValue("Server", 0);
rkNewPort.Close();
rkPorts.Close();
//try open port now
try
{
InTheHand.Net.Ports.BluetoothSerialPort.CreateClient(alPorts[0].ToString(), new BluetoothEndPoint(this.DeviceAddress, BluetoothService.SerialPort));
}
catch
{
}
}
else
{
//find and remove registry entries
Microsoft.Win32.RegistryKey rkPorts = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Bluetooth\\Serial\\Ports", true);
foreach (string deviceAddress in rkPorts.GetSubKeyNames())
{
if (deviceAddress == this.DeviceAddress.ToString("8"))
{
rkPorts.DeleteSubKeyTree(deviceAddress);
break;
}
}
rkPorts.Close();
}
當state為true時為註冊,當state為false時為反註冊。
參考文獻
Bluetooth COM Ports On Windows CE
Windows Mobile 5.0 Bluetooth Virtual Serial Ports
Any Port in a Storm
MSDN:Creating a Connection to a Remote Device Using a Virtual COM Port