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でスレッド非依存のオブジェクトを作ると思えばよいかと。

とりあえず、今回はここまで。