在运行带有几个PInvokes的Silverlight 5.0应用程序5分钟后,我收到以下错误:
Attempted to read or write protected memory
可能,我在某处发生了内存泄漏。
问题是如何调试?我根本没有得到堆栈跟踪,所以我无法准确找出有问题的代码。
通过多次注释代码并重新运行应用程序,我认为我设法找到问题,但我无法弄清楚问题。
潜在的违规代码:
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相关错误有哪些好的策略?或者有没有办法强制堆栈跟踪输出? (我尝试在Debug->异常中启用所有异常)
答案 0 :(得分:2)
您在WndProc
结构中传递的wndClassEx
委托正在被垃圾收集。将委托传递给非托管代码时,委托只会在调用非托管函数时保持活动状态。一旦调用结束,就不再在Silverlight应用程序中引用它,因此垃圾收集会将其视为已死并将收集它。
This example of creating a window class in Silverlight在静态方法中缓存WndProc
委托,以便不会收集垃圾。
另请参阅P / Invoke上的这个(较旧但仍然有效)MSDN article。
答案 1 :(得分:1)
@ 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;
}