C# 如何在Silverlight中调试使用PInvokes导致的内存相关错误

C# 如何在Silverlight中调试使用PInvokes导致的内存相关错误,c#,.net,silverlight,winapi,pinvoke,C#,.net,Silverlight,Winapi,Pinvoke,在运行silverlight 5.0应用程序(其中包含几个Pinvoke)5分钟左右后,出现以下错误: Attempted to read or write protected memory 很可能,我的某个地方出现了内存泄漏 问题是如何调试它?我根本没有得到stacktrace,所以我无法准确地指出有问题的代码 通过注释代码并多次重新运行应用程序,我认为我成功地找到了问题所在,但我无法找出问题所在 潜在违规代码: private void InitUSBEvents() { cons

在运行silverlight 5.0应用程序(其中包含几个Pinvoke)5分钟左右后,出现以下错误:

Attempted to read or write protected memory
很可能,我的某个地方出现了内存泄漏

问题是如何调试它?我根本没有得到stacktrace,所以我无法准确地指出有问题的代码

通过注释代码并多次重新运行应用程序,我认为我成功地找到了问题所在,但我无法找出问题所在

潜在违规代码:

private void InitUSBEvents()
{
    const string clsName = "SLUsbClass";
    const string wndName = "SLUsbWindow";

    Win32.WNDCLASSEX wndClassEx = new Win32.WNDCLASSEX();

    wndClassEx.cbSize = Marshal.SizeOf(wndClassEx);
    wndClassEx.lpszClassName = clsName;
    wndClassEx.lpfnWndProc = WndProc;

    rClassAtomValue = Win32.RegisterClassEx2(ref wndClassEx);

    windowHandle = Win32.CreateWindowEx2(0, rClassAtomValue, wndName, 0, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);

    Win32Usb.RegisterKeyboardUsbEvents(windowHandle);
}

[AllowReversePInvokeCalls]
private IntPtr WndProc(IntPtr hWnd, WM msg, IntPtr wParam, IntPtr lParam)
{
    switch (msg)
    {
        case WM.INPUT:
            //Console.WriteLine("Key Event");
            break;
        default:
            return Win32.DefWindowProc(hWnd, msg, wParam, lParam);
    }

    return IntPtr.Zero;
}
PInvoke相关声明:

public class Win32
{
    [DllImport("user32.dll", SetLastError = true, EntryPoint = "CreateWindowEx")]
    public static extern IntPtr CreateWindowEx(
       WindowStylesEx dwExStyle,
       string lpClassName,
       string lpWindowName,
       WindowStyles dwStyle,
       int x,
       int y,
       int nWidth,
       int nHeight,
       IntPtr hWndParent,
       IntPtr hMenu,
       IntPtr hInstance,
       IntPtr lpParam);

    // Create a window, but accept a atom value.  
    [DllImport("user32.dll", SetLastError = true, EntryPoint = "CreateWindowEx")]
    public static extern IntPtr CreateWindowEx2(
       WindowStylesEx dwExStyle,
       UInt16 lpClassName,
       string lpWindowName,
       WindowStyles dwStyle,
       int x,
       int y,
       int nWidth,
       int nHeight,
       IntPtr hWndParent,
       IntPtr hMenu,
       IntPtr hInstance,
       IntPtr lpParam);

    [DllImport("kernel32.dll", EntryPoint = "LocalAlloc")]
    internal static extern IntPtr LocalAlloc_NoSafeHandle(
        LocalMemoryFlags uFlags, IntPtr sizetdwBytes);

    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern IntPtr LocalFree(IntPtr hMem);

    [DllImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassEx")]
    public static extern UInt16 RegisterClassEx2([In] ref WNDCLASSEX lpwcx); 

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.U2)]
    public static extern short RegisterClassEx([In] ref WNDCLASSEX lpwcx);

    [DllImport("user32.dll")]
    public static extern IntPtr DefWindowProc(IntPtr hWnd, WM uMsg, IntPtr wParam, IntPtr lParam); 
  }

  public class Win32Usb
  {

    public static bool RegisterKeyboardUsbEvents(IntPtr hWnd)
    {
        //Create an array of all the raw input devices we want to 
        //listen to. In this case, only keyboard devices.
        //RIDEV_INPUTSINK determines that the window will continue
        //to receive messages even when it doesn't have the focus.
        RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[1];

        rid[0].usUsagePage = 0x01;
        rid[0].usUsage = 0x06;
        rid[0].dwFlags = RIDEV_INPUTSINK;
        rid[0].hwndTarget = hWnd;

        return RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0]));
    }

    [StructLayout(LayoutKind.Explicit)]
    internal class RAWINPUT
    {
        [FieldOffset(0)]
        public RAWINPUTHEADER header;
        [FieldOffset(16)]
        public RAWMOUSE mouse;
        [FieldOffset(16)]
        public RAWKEYBOARD keyboard;
        [FieldOffset(16)]
        public RAWHID hid;
    }

            [StructLayout(LayoutKind.Sequential)]
    internal class RAWINPUTDEVICELIST
    {
        public IntPtr hDevice;
        [MarshalAs(UnmanagedType.U4)]
        public int dwType;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct RAWINPUTDEVICE
    {
        [MarshalAs(UnmanagedType.U2)]
        public ushort usUsagePage;
        [MarshalAs(UnmanagedType.U2)]
        public ushort usUsage;
        [MarshalAs(UnmanagedType.U4)]
        public int dwFlags;
        public IntPtr hwndTarget;
    }
  }

我也没有发现PInvoke声明有任何错误。在Silverlight上调试PInvoke相关错误的好策略有哪些?或者有没有办法强制stacktrace输出?(我已尝试在Debug->Exception中打开要抛出的所有异常)

正在垃圾收集传递给
wndClassEx
结构的
WndProc
委托。将委托传递给非托管代码时,该委托仅在调用非托管函数时保持活动状态。调用结束后,Silverlight应用程序中不再引用它,因此垃圾收集会认为它已死亡并将其收集

在静态方法中缓存
WndProc
委托,使其不会被垃圾收集


在p/Invoke上也可以看到这一点(较旧,但仍然有效)。

正在垃圾收集在
wndClassEx
结构中传递的
WndProc
委托。将委托传递给非托管代码时,该委托仅在调用非托管函数时保持活动状态。调用结束后,Silverlight应用程序中不再引用它,因此垃圾收集会认为它已死亡并将其收集

在静态方法中缓存
WndProc
委托,使其不会被垃圾收集


在p/Invoke上也可以看到这一点(较旧,但仍然有效)。

正在垃圾收集在
wndClassEx
结构中传递的
WndProc
委托。将委托传递给非托管代码时,该委托仅在调用非托管函数时保持活动状态。调用结束后,Silverlight应用程序中不再引用它,因此垃圾收集会认为它已死亡并将其收集

在静态方法中缓存
WndProc
委托,使其不会被垃圾收集


在p/Invoke上也可以看到这一点(较旧,但仍然有效)。

正在垃圾收集在
wndClassEx
结构中传递的
WndProc
委托。将委托传递给非托管代码时,该委托仅在调用非托管函数时保持活动状态。调用结束后,Silverlight应用程序中不再引用它,因此垃圾收集会认为它已死亡并将其收集

在静态方法中缓存
WndProc
委托,使其不会被垃圾收集


在p/Invoke上也可以看到这一点(较旧,但仍然有效)。

@shf301是正确的,您需要采取措施阻止收集您的代理。我不想在这里重复他的观点

但是,最重要的是,结构的定义不正确。它们将在32位下工作,但不能在64位下工作。
FieldOffset
的经验法则是,您只能在偏移量为
0
的情况下使用它。这条规则允许您编写同时适用于32位和64位的代码。在代码中,您使用了偏移量
16
,这对于32位是合适的,但对于64位则不合适。按照此模式定义您的结构:

[StructLayout(LayoutKind.Explicit)]
struct RAWINPUTUNION
{
    [FieldOffset(0)]
    public RAWMOUSE mouse;
    [FieldOffset(0)]
    public RAWKEYBOARD keyboard;
    [FieldOffset(0)]
    public RAWHID hid;
}

[StructLayout(LayoutKind.Sequential)]
struct RAWINPUT
{
    public RAWINPUTHEADER header;       
    public RAWINPUTUNION data;
}

@shf301是正确的,您需要采取措施阻止收集您的代理。我不想在这里重复他的观点

但是,最重要的是,结构的定义不正确。它们将在32位下工作,但不能在64位下工作。
FieldOffset
的经验法则是,您只能在偏移量为
0
的情况下使用它。这条规则允许您编写同时适用于32位和64位的代码。在代码中,您使用了偏移量
16
,这对于32位是合适的,但对于64位则不合适。按照此模式定义您的结构:

[StructLayout(LayoutKind.Explicit)]
struct RAWINPUTUNION
{
    [FieldOffset(0)]
    public RAWMOUSE mouse;
    [FieldOffset(0)]
    public RAWKEYBOARD keyboard;
    [FieldOffset(0)]
    public RAWHID hid;
}

[StructLayout(LayoutKind.Sequential)]
struct RAWINPUT
{
    public RAWINPUTHEADER header;       
    public RAWINPUTUNION data;
}

@shf301是正确的,您需要采取措施阻止收集您的代理。我不想在这里重复他的观点

但是,最重要的是,结构的定义不正确。它们将在32位下工作,但不能在64位下工作。
FieldOffset
的经验法则是,您只能在偏移量为
0
的情况下使用它。这条规则允许您编写同时适用于32位和64位的代码。在代码中,您使用了偏移量
16
,这对于32位是合适的,但对于64位则不合适。按照此模式定义您的结构:

[StructLayout(LayoutKind.Explicit)]
struct RAWINPUTUNION
{
    [FieldOffset(0)]
    public RAWMOUSE mouse;
    [FieldOffset(0)]
    public RAWKEYBOARD keyboard;
    [FieldOffset(0)]
    public RAWHID hid;
}

[StructLayout(LayoutKind.Sequential)]
struct RAWINPUT
{
    public RAWINPUTHEADER header;       
    public RAWINPUTUNION data;
}

@shf301是正确的,您需要采取措施阻止收集您的代理。我不想在这里重复他的观点

但是,最重要的是,结构的定义不正确。它们将在32位下工作,但不能在64位下工作。
FieldOffset
的经验法则是,您只能在偏移量为
0
的情况下使用它。这条规则允许您编写同时适用于32位和64位的代码。在代码中,您使用了偏移量
16
,这对于32位是合适的,但对于64位则不合适。按照此模式定义您的结构:

[StructLayout(LayoutKind.Explicit)]
struct RAWINPUTUNION
{
    [FieldOffset(0)]
    public RAWMOUSE mouse;
    [FieldOffset(0)]
    public RAWKEYBOARD keyboard;
    [FieldOffset(0)]
    public RAWHID hid;
}

[StructLayout(LayoutKind.Sequential)]
struct RAWINPUT
{
    public RAWINPUTHEADER header;       
    public RAWINPUTUNION data;
}

您的大部分
封送处理属性都可以删除。在我看来,它们只是增加了噪音。您的大部分
marshallas
属性都可以删除。在我看来,它们只是增加了噪音。您的大部分
marshallas
属性都可以删除。在我看来,它们只是增加了噪音。您的大部分
marshallas
属性都可以删除。在我看来,它们只是增加了噪音。我能辨别出来