WPFxUPnP 2 その2
その1で、示したクラスをそのまま使うと、
解放せずに検索スレッドが終了すると例外が発生するので、ちょっと使いづらいのですよね。
(操作とか後々利用したいので、解放せずに残しておきたい!)
そこで、今回はラップクラスを作成して例外が発生しないようにします。
まずはデバイスを管理するリスト。
using System.Collections.Generic; using UPNPLib; public class DeviceList : List<UPnPDevice> { }
そして、ラップのクラスです。
最初に、利用側クラスに通知するためのイベント関連の定義
using System; using System.Runtime.InteropServices; using UPNPLib; public class DeviceFinder : IUPnPDeviceFinderCallback { public delegate void DeviceAddEventHandler(UPnPDevice pDevice); public delegate void DeviceRemoveEventHandler(string bstrUDN); public delegate void DeviceFindComplateEventHandler(); public event DeviceAddEventHandler AddEvent; public event DeviceRemoveEventHandler RemoveEvent; public event DeviceFindComplateEventHandler ComplateEvent; }
利用するUPnPDeviceFinder関連。
ほとんど、その1ですね。
int DeviceFindID = 0; UPnPDeviceFinder finder = new UPnPDeviceFinder(); public DeviceList DeviceList = new DeviceList(); public void FindDeviceAsync(string device) { DeviceListClear(); DeviceFindID = finder.CreateAsyncFind(device, 0, this); finder.StartAsyncFind(DeviceFindID); } public DeviceList GetDeviceList() { return DeviceList; } public void CancelFindDeviceAsync() { finder.CancelAsyncFind(DeviceFindID); DeviceListClear(); DeviceFindID = 0; } void DeviceListClear() { lock (this) { while (DeviceList.Count != 0) { Marshal.ReleaseComObject(DeviceList[0]); DeviceList.RemoveAt(0); } } }
最後に肝心のUPnPDeviceFinder I/F関連。
#region IUPnPDeviceFinderCallback /// <summary> /// デバイス追加イベントハンドラ /// </summary> /// <param name="lFindData">検索データ</param> /// <param name="pDevice">デバイスオブジェクト</param> public void DeviceAdded(int lFindData, UPnPDevice pDevice) { try { UPnPDevice dev = (UPnPDevice)Marshal.GetUniqueObjectForIUnknown(Marshal.GetIUnknownForObject(pDevice)); lock (this) { DeviceList.Add(dev); } AddEvent(dev); } catch (Exception) { } finally { Marshal.ReleaseComObject(pDevice); } } /// <summary> /// デバイス削除イベントハンドラ /// </summary> /// <param name="lFindData">検索データ</param> /// <param name="bstrUDN">デバイスUDN</param> public void DeviceRemoved(int lFindData, string bstrUDN) { try { lock (this) { foreach (UPnPDevice one in DeviceList) { if (one.UniqueDeviceName == bstrUDN) { DeviceList.Remove(one); Marshal.ReleaseComObject(one); break; } } } RemoveEvent(bstrUDN); } catch (Exception) { } } /// <summary> /// 検索完了イベントハンドラ /// </summary> /// <param name="lFindData">検索データ</param> public void SearchComplete(int lFindData) { try { ComplateEvent(); } catch (Exception) { } } #endregion
ポイントは、Marshal.GetIUnknownForObjectでIUnknownを取得して、Marshal.GetUniqueObjectForIUnknownを利用してIUnknownからオブジェクトを取得します。
こうすると、参照カウンタを残したまま、スレッドが終了しても例外が発生しません。
Marshal.GetUniqueObjectForIUnknownでスレッド非依存のオブジェクトを作ると思えばよいかと。
とりあえず、今回はここまで。