2005年12月07日

名前付きイベント

なんとなくふと思い立って、というかまあぶっちゃけ起動中アプリケーションに対しての処理についてというスレッドでレスをつけていて思ったんですが、そう言えばプロセス間通信に名前付きイベントオブジェクトって使えるよなー、と。

.NET FrameworkではAutoResetEvent・ManualResetEventとしてWindowsのイベント機構が実装されていますが、何故か名前なしのイベントしかサポートされていません。これではプロセス間の調停機構には使うことができません。Mutexは名前付きの物をサポートしていますが、用途が違います。

ただ状態を渡すことができないので、使い道は果てしなく限られそうですが。

実装は簡単です。ほとんど基底クラスであるWaitHandle任せと言っても過言ではありません。基底クラスのHandle(.NET 2.0ではSafeWaitHandle)プロパティを使用すれば自前でハンドルを管理する必要すらありません。

どうせ実装するんだったら名前付きパイプとかの方が実際的な気がしますが、まあ簡単だったからということで一応。通知できるMutexみたいなノリで。短いコードなので不要な気もしますが一応XMLドキュメント付きC#のソースもどうぞ。

C#版。

using System;
using System.Runtime.InteropServices;
using System.Threading;
#if !V10 && !V11
using Microsoft.Win32.SafeHandles;
#endif

public class NamedAutoResetEvent : WaitHandle {
    [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
    private static extern IntPtr CreateEvent(IntPtr security, bool isManualReset,
                                              bool initialState, string name);
    [DllImport("kernel32.dll")]
    private static extern void SetLastError(int errorCode);
    [DllImport("kernel32.dll")]
    private static extern bool SetEvent(IntPtr handle);
    [DllImport("kernel32.dll")]
    private static extern bool ResetEvent(IntPtr handle);

    public NamedAutoResetEvent(bool initialState, string name, out bool createdNew) {
        const int alreadyExists = 0xB7;
        IntPtr handle = CreateEvent(IntPtr.Zero, false, initialState, name);
        int error = Marshal.GetLastWin32Error();
        if (handle == IntPtr.Zero) {
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        }
        //.NET 2.0ではMicrosoft.Win32.SafeWaitHandleを使うようになった。
#if !V10 && !V11
        base.SafeWaitHandle = new SafeWaitHandle(handle, true);
#else
        base.Handle = handle;
#endif
        createdNew = !(error == alreadyExists);
    }
    public bool Set() {
#if !V10 && !V11
        return SetEvent(base.SafeWaitHandle.DangerousGetHandle());
#else
        return SetEvent(base.Handle);
#endif
    }
    public bool Reset() {
#if !V10 && !V11
        return ResetEvent(base.SafeWaitHandle.DangerousGetHandle());
#else
        return ResetEvent(base.Handle);
#endif
    }
}

VB.NET版。

Imports System
Imports System.Runtime.InteropServices
Imports System.Threading
#If Not(V = 10 Or V = 11) Then
Imports Microsoft.Win32.SafeHandles
#End If

Public Class NamedAutoResetEvent
    Inherits WaitHandle
    Declare Auto Function CreateEvent Lib "kernel32.dll" ( _
        ByVal security As IntPtr, ByVal isManualReset As Boolean, _
        ByVal initialState As Boolean, ByVal name As String) As IntPtr
    Declare Function SetEvent Lib "kernel32.dll" ( _
        ByVal handle As IntPtr) As Boolean
    Declare Function ResetEvent Lib "kernel32.dll" ( _
        ByVal handle As IntPtr) As Boolean
    Declare Sub SetLastError Lib "kernel32.dll" (ByVal errorCode As Integer)

    Public Sub New(ByVal initialState As Boolean, ByVal name As String, 
                   ByRef createdNew As Boolean)
        Const alreadyExists As Integer = &HB7
        Dim handle As IntPtr = CreateEvent(IntPtr.Zero, False, initialState, name)
        Dim errorCode As Integer = Marshal.GetLastWin32Error()
        If handle.Equals(IntPtr.Zero) Then
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error())
        End If
        '.NET 2.0ではMicrosoft.Win32.SafeWaitHandleを使うようになった。
#If Not(V = 10 Or V = 11) Then
        MyBase.SafeWaitHandle = New SafeWaitHandle(handle, True)
#Else
        MyBase.Handle = handle
#End If
        createdNew = Not(errorCode.Equals(alreadyExists))
    End Sub
    Public Function [Set]() As Boolean
#If Not(V = 10 Or V = 11) Then
        Return SetEvent(MyBase.SafeWaitHandle.DangerousGetHandle())
#Else
        Return SetEvent(MyBase.Handle)
#End If
    End Function
    Public Function Reset() As Boolean
#If Not(V = 10 Or V = 11) Then
        Return ResetEvent(MyBase.SafeWaitHandle.DangerousGetHandle())
#Else
        Return ResetEvent(MyBase.Handle)
#End If
    End Function
End Class
posted by Hongliang at 23:18| Comment(3) | TrackBack(0) | .NET | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
こんにちは。

過去に作ったWIN32デーモンプログラムで、停止させるのにWIN32名前付きイベントを使っていたのですが、C#に移植する段になって名前付きイベントがC#にないことがわかって途方に暮れていました。

あっちへうろうろこっちへうろうろ調べて回って、ようやくここの記事を見つけました。
この方法なら、過去のソースを単純移植するだけで済みそうですので、流用させていただきます。
ありがとうございました。

なお、本当に必要だったのは名前付きイベントなので、

1)NamedAutoResetEventクラスをNamedEventクラスに変名し、コンストラクタをprotectedにしてパラメタにisManualResetを追加する。

2)NamedEventクラスから継承されたNamedAutoResetEventクラスを設け、基底クラスのコンストラクタのisManualResetには固定値falseを与える。

3)NamedEventクラスから継承されたNamedManualResetEventクラスを設け、基底クラスのコンストラクタのisManualResetには固定値trueを与える。

と、このような手順で改造しました。
これならAutoとManualでロジック部分を重複して持たずに済みます。
あとは、不要なパラメタを省略できるようなコンストラクタのバリエーションを追加していきたいと思います。
Posted by ネラル at 2007年11月10日 14:02
ども。
まあ実のところ .NET 2.0 で EventWaitHandle が追加されて普通に名前つきイベントを使えるようになったんですけどね。
Posted by Hongliang at 2007年11月12日 07:24
後、ソメイヨシノの開花は平年の4月4日より3、4日
Posted by トリーバーチ 財布 at 2012年07月30日 22:22
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック

ここ(hongliang.seesaa.net)で公開しているものについて、利用は自由に行って頂いて構いません。改変、再頒布もお好きになさって下さい。利用に対しこちらが何かを要求することはありません。

ただし、公開するものを使用、または参考したことによって何らかの損害等が生じた場合でも、私はいかなる責任も負いません。

あ、こんなのに使ったってコメントを頂ければ嬉しいです。

×

この広告は1年以上新しい記事の投稿がないブログに表示されております。