包装在c#中实现回调的本机库

时间:2012-03-19 17:09:02

标签: c# events delegates callback

我有一个Visual Studio 2008 C#.NET 3.5类,它提供对本机库的访问。该库有一个Register方法,允许用户指定将在某个事件上激活的回调。我在下面提供了一个C#实现:

internal class MyLibHandle : SafeHandleZeroOrMinusOneIsInvalid { /*...*/ }

internal static class NativeMethods
{
    public delegate void OnSomeEventDelegate (FOO foo, IntPtr user);

    [DllImport("MyLib.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.U1)]
    public static extern bool MyLib_Register(MyLibHandle handle, OnSomeEventDelegate callback, IntPtr user);
}

public class MyWrappedLib : IDisposable
{
    private MyLibHandle handle_;

    private event EventHandler<OnSomeEventArgs> some_event_int_;

    public event EventHandler<OnSomeEventArgs> SomeEvent
    {
        add
        {
            if (some_event_int_ == null)
            {
                if (!NativeMethods.MyLib_Register(handle_, ReceivedSomeEvent, IntPtr.Zero))
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            some_event_int_ += value;
        }
        remove
        {
            some_event_int_ -= value;
            if (some_event_int_ == null)
            {
                if (!NativeMethods.MyLib_DeRegister(handle_, -1))
                    throw new Win32Exception(Marshal.GetLastWin32Error());
            }
        }
    }

    private void ReceivedSomeEvent(FOO foo, IntPtr user)
    {
        OnSomeEvent(new OnSomeEventArgs() { foo = foo });
    }

    protected virtual void OnBeacon(OnSomeEventArgs args)
    {
        EventHandler<OnSomeEventArgs> evt = some_event_int_;
        if (evt != null)
            evt(this, args);
    }
}

这样可行,但我收到了一些不祥的声音警告

warning : CA1065 : Microsoft.Design : 'MyWrappedLib.SomeEvent.add(EventHandler<OnSomeEventArgs>)' creates an exception of type 'Win32Exception', an exception type that should not be raised in this type of method.

warning : CA2122 : Microsoft.Security : 'MyWrappedLib.SomeEvent.add(EventHandler<OnSomeEventArgs>)' calls into 'Marshal.GetLastWin32Error()' which has a LinkDemand. By making this call, 'Marshal.GetLastWin32Error()' is indirectly exposed to user code.

处理这种情况的推荐方法是什么?我应该创建一个方法来处理事件订阅而不是传统的添加;去掉;存取?

由于

1 个答案:

答案 0 :(得分:1)

对于事件访问器,允许以下异常类型:

  • System.InvalidOperationException 以及所有衍生品(包括 的 System.ObjectDisposedException

  • System.NotSupportedException 及所有衍生产品

  • ArgumentException 和衍生物

为什么native方法返回false? 是否与某些外部条件有关, 或者是传递无效参数的结果?

所以我将Win32Exception包装为InvalidOperationException的内部, 或者ArgumentException,具体取决于您的情况。

第二次警告(CA2122)是关于安全的 - 它警告Marshal.GetLastWin32Error执行安全检查, 而你的代码没有。此安全检查仅执行一次, 在第一次通话。之后的所有呼叫都未验证访问权限, 出于性能原因。所以从理论上讲,第一次通话可以通过 受信任的用户,并且不会限制所有其他呼叫。

您需要使用以下属性装饰您的事件处理程序:

  

[SecurityPermission(SecurityAction.InheritanceDemand,Flags = SecurityPermissionFlag.UnmanagedCode)]

(它将检查代码是否有权调用托管代码) 如果您的申请中不担心安全问题,请禁止或警告此警告。

this question中详细说明了详细信息。