2008年4月1日火曜日

[.NET]MailSlot(その3)

GDD Blog: [.NET]MailSlot(その2)」で掲載しましたMailSlot操作クラスですが、ReadFileで受信待機をします。そのため、
  • 何かしら情報を受信しないと終わらない
  • すべての受信が終わるまで、受信メッセージを取得する方法が無い

という問題がありました。そこで

  1. 待機ループし、終了フラグを監視する
  2. イベントを発行する機構を実装する
  3. IDisposableをインプリメント

することにしました。



以下、メールスロットを送受信するクラスです。
/// <summary>
/// メールスロットの操作を行います。
/// </summary>
public class MailSlot : IDisposable
{
    #region Win32 API宣言

    [DllImport("kernel32.dll")]
    static extern SafeFileHandle CreateMailslot(
        string lpName,          //メールスロット名
        uint nMaxMessageSize,   //最大メッセージサイズ
        uint lReadTimeout,      //み取りタイムアウトの間隔
        IntPtr lpSecurityAttributes);   //継承オプション

    [DllImport("kernel32.dll")]
    static extern bool GetMailslotInfo(
        SafeFileHandle hMailslot,   //メールスロットのハンドル
        ref uint lpMaxMessageSize,    //最大メッセージサイズ
        ref uint lpNextSize,          //次のメッセージのサイズ
        ref uint lpMessageCount,      //メッセージ数
        ref uint lpReadTimeout);      //読み取りタイムアウトの間隔


    [DllImport("kernel32.dll")]
    static extern bool SetMailslotInfo(
        SafeFileHandle hMailslot,   //メールスロットのハンドル
        uint lReadTimeout);         //読み取りタイムアウトの間隔

    [DllImport("kernel32.dll"SetLastError = true)]
    [returnMarshalAs(UnmanagedType.Bool)]
    static extern bool CloseHandle(SafeFileHandle hMailslot);

    [DllImport("kernel32.dll"CharSet = CharSet.Auto)]
    private static extern SafeFileHandle CreateFile(
        string lpFileName,              // ファイル名 
        DesiredAccess dwDesiredAccess,  // アクセスモード 
        ShareMode dwShareMode,          // 共有モード 
        int lpSecurityAttributes,       // セキュリティ記述子 
        CreationDisposition dwCreationDisposition,  // 作成方法 
        FlagsAndAttributes dwFlagsAndAttributes,    // ファイル属性 
        IntPtr hTemplateFile);          // テンプレートファイルのハンドル 

    #endregion

    #region 列挙体

    private enum DesiredAccess : uint
    {
        GENERIC_READ = 0x80000000,
        GENERIC_WRITE = 0x40000000,
        GENERIC_EXECUTE = 0x20000000
    }

    private enum ShareMode : uint
    {
        FILE_SHARE_READ = 0x00000001,
        FILE_SHARE_WRITE = 0x00000002,
        FILE_SHARE_DELETE = 0x00000004
    }

    private enum CreationDisposition : uint
    {
        CREATE_NEW = 1,
        CREATE_ALWAYS = 2,
        OPEN_EXISTING = 3,
        OPEN_ALWAYS = 4,
        TRUNCATE_EXISTING = 5
    }

    private enum FlagsAndAttributes : uint
    {
        FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
        FILE_ATTRIBUTE_ENCRYPTED = 0x00004000,
        FILE_ATTRIBUTE_HIDDEN = 0x00000002,
        FILE_ATTRIBUTE_NORMAL = 0x00000080,
        FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000,
        FILE_ATTRIBUTE_OFFLINE = 0x00001000,
        FILE_ATTRIBUTE_READONLY = 0x00000001,
        FILE_ATTRIBUTE_SYSTEM = 0x00000004,
        FILE_ATTRIBUTE_TEMPORARY = 0x00000100
    }


    private enum MailSlotStatus : uint
    {
        MAILSLOT_NO_MESSAGE = 0xffffffff,
    }


    #endregion

    /// <summary>
    /// 受信イベントハンドラーのdelegate
    /// </summary>
    /// <param name="sender">送信元</param>
    /// <param name="e">イベントオブジェクト</param>
    public delegate void ReceiveEventHandler(object senderReceiveEventArgs e);

    //イベントハンドラー
    public event ReceiveEventHandler receive;


    private SafeFileHandle slotHandle = null;
    /// <summary>
    /// メールスロットスロットハンドル
    /// </summary>
    public SafeFileHandle SlotHandle
    {
        get { return slotHandle; }
        set { slotHandle = value; }
    }

    private bool receiveLoop = true;
    /// <summary>
    /// 受信ループ
    /// </summary>
    public bool ReceiveLoop
    {
        get { return receiveLoop; }
        set { receiveLoop = value; }
    }


    /// <summary>
    /// コンストラクタ
    /// </summary>
    public MailSlot()
    {
    }


    /// <summary>
    /// メールスロットオープン
    /// </summary>
    /// <param name="slot">メールスロット名</param>
    /// <param name="maxMessageSize">最大メッセージ数</param>
    /// <param name="readTimeout">タイムアウト</param>
    public void Open(string slotNameuint maxMessageSizeuint readTimeout)
    {
        // メールスロットをオープンし、ハンドルをプロパティに保存
        SlotHandle = CreateMailslot(slotNamemaxMessageSizereadTimeout, (IntPtr)0);
    }

    /// <summary>
    /// メールスロットクローズ
    /// </summary>
    public void Close()
    {
        // SlotHandleのnull判定がマルチスレッドでも正しく動作するようロックする
        lock (this)
        {
            if (SlotHandle != null)
            {
                //メールスロットを開放する
                CloseHandle(SlotHandle);
                SlotHandle = null;
            }
        }
    }


    /// <summary>
    /// メールスロット受信待ち
    /// </summary>
    public void ReadMailSlot()
    {
        try
        {
            //受信待機フラグがたっている間ループする
            while (ReceiveLoop)
            {

                //メールスロットの件数をチェックする
                uint msgMaxCnt = 1;
                uint nextSize = 0;
                uint msgCnt = 0;
                uint timeout = 0;

                //メールスロット情報取得
                if (!GetMailslotInfo(SlotHandleref msgMaxCntref nextSizeref msgCntref timeout))
                {
                    System.Diagnostics.Debug.WriteLine("GetMailslotInfo失敗!!");
                    continue;
                }

                //メールスロット読み取り 
                if (msgCnt > 0 && nextSize != (uint)MailSlotStatus.MAILSLOT_NO_MESSAGE)
                {
                    // スロットを読み取る
                    FileStream fs = new FileStream(SlotHandleFileAccess.Read);
                    byte[] buf = new byte[msgMaxCnt];
                    int len = fs.Read(buf0buf.Length);

                    //イベント発火
                    ReceiveEventArgs e = new ReceiveEventArgs(Encoding.UTF8.GetString(buf0len));
                    OnReceive(e);

                    //処理終了フラグの確認
                    if (e.ReceiveEnd)
                    {
                        ReceiveLoop = false;
                    }
                    //fs.Close();
                    //break;

                }
                else
                {
                    //待機する
                    Thread.Sleep(500);
                }
            }

        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.Message);
            System.Diagnostics.Debug.WriteLine(ex.StackTrace);
            throw ex;
        }
    }

    /// <summary>
    /// 受信イベント
    /// </summary>
    /// <param name="e">イベントオブジェクト</param>
    protected virtual void OnReceive(ReceiveEventArgs e)
    {
        if (receive != null)
        {
            receive(thise);
        }
    }

    #region IDisposable メンバ

    /// <summary>
    /// 資源の解放
    /// </summary>
    public void Dispose()
    {
        Close();
    }

    #endregion

    /// <summary>
    /// メッセージの送信
    /// </summary>
    /// <param name="slot">送信先スロット名</param>
    /// <param name="message">送信する文字列</param>
    public static void WriteMailSlot(string slotstring message)
    {

        try
        {
            //メールスロットを開く
            SafeFileHandle fileHandle = CreateFile(slot,
                    DesiredAccess.GENERIC_READ | DesiredAccess.GENERIC_WRITE,
                    ShareMode.FILE_SHARE_READ | ShareMode.FILE_SHARE_WRITE,
                    0,
                    CreationDisposition.OPEN_EXISTING,
                    FlagsAndAttributes.FILE_ATTRIBUTE_NORMAL,
                    (IntPtr)0);

            // 指定の文字列を出力する
            using (FileStream fs = new FileStream(fileHandleFileAccess.Write))
            {
                byte[] msg = Encoding.UTF8.GetBytes(message);
                fs.Write(msg0msg.Length);
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.Message);
            System.Diagnostics.Debug.WriteLine(ex.StackTrace);
            throw ex;
        }
    }

}


以下、受信イベントオブジェクトクラスです。
/// <summary>
/// 受信イベントオブジェクト
/// </summary>
public class ReceiveEventArgs : EventArgs
{
    private string message;
    /// <summary>
    /// 受信メッセージ
    /// </summary>
    public string Message
    {
        get { return message; }
    }

    private bool receiveEnd = false;
    /// <summary>
    /// 受信終了フラグ
    /// </summary>
    public bool ReceiveEnd
    {
        get { return receiveEnd; }
        set { receiveEnd = value; }
    }

    /// <summary>
    /// コンストラクタ
    /// </summary>
    /// <param name="msg">メッセージ</param>
    public ReceiveEventArgs(string msg)
    {
        message = msg;
    }

}


使うほうはこんな感じ。

/// <summary>
/// 受信ボタン押下
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnMLReceive2_Click(object senderEventArgs e)
{
    using (MailSlot slot = new MailSlot())
    {
        //イベントを設定後、スロットをオープンし受信ループに入る
        slot.receive += new MailSlot.ReceiveEventHandler(slot_receive2);
        slot.Open(txtMQPath.Text5120xffff);
        slot.ReadMailSlot();

        // スロットをクローズする
        slot.Close();
    }
}

/// <summary>
/// 受信イベント
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void slot_receive2(object senderReceiveEventArgs e)
{
    //受信したメッセージを画面に表示
    txtMQRecieve.AppendText(e.Message);
    txtMQRecieve.AppendText(Environment.NewLine);

    //以下の文字列が着たら受信待機をやめる
    if (e.Message == "TERMINATE")
    {
        //MailSlot.ReadMailSlotが終了する
        e.ReceiveEnd = true;
    }
}

0 件のコメント: