c# - c# System.ExecutionEngineError on P/Invoke call from large arrays
问题描述
我正在用 C# 编写一个小应用程序,使用 P/Invoke 创建一个窗口。使用大型数组时,我被System.ExecutionEngineException
抛出了。PeekMessage
这很奇怪,因为在实际使用数组时不会抛出异常并且一切都很好。但是当我称之为PeekMessage
抛出时。不创建数组可以防止问题发生。当异常发生时,这将打印到控制台:
Process terminated. A callback was made on a garbage collected delegate of type 'PInvoke.User32!PInvoke.User32+WndProc::Invoke'.
at PInvoke.User32.PeekMessage(MSG*, IntPtr, WindowMessage, WindowMessage, PeekMessageRemoveFlags)
at PInvoke.User32.PeekMessage(MSG*, IntPtr, WindowMessage, WindowMessage, PeekMessageRemoveFlags)
at PInvoke.User32.PeekMessage(IntPtr, IntPtr, WindowMessage, WindowMessage, PeekMessageRemoveFlags)
at Program.Main(System.String[])
我能够从我的项目中拼凑出一个小样本,该样本重现了该错误。我正在使用 .NET Core 3.1。
using System;
using System.Drawing;
using static PInvoke.Kernel32;
using static PInvoke.User32;
static class Program
{
static IntPtr HWND;
static unsafe void Main(string[] args)
{
// creating the arrays on lines 14, 16, and 26 somehow cause the error and creating the window after line 18 doesn't cause the error.
HWND = CreateWindow();
byte[] b = new byte[100000000];
DoSomething(new int[10000]);
PeekMessage(HWND, HWND, 0, 0, PeekMessageRemoveFlags.PM_REMOVE);
}
public static void DoSomething(int[] data)
{
Color[] result = new Color[data.Length];
}
static unsafe IntPtr WndProc(IntPtr hwnd, WindowMessage msg, void* wParam, void* lParam)
{
return DefWindowProc(hwnd, msg, (IntPtr)wParam, (IntPtr)lParam);
}
static unsafe IntPtr CreateWindow()
{
var hIsnt = GetModuleHandle(null);
string classname = "test";
WNDCLASS wndclass;
fixed (char* pClassName = classname)
wndclass = new WNDCLASS()
{
lpfnWndProc = WndProc,
hInstance = hIsnt.DangerousGetHandle(),
lpszClassName = pClassName
};
RegisterClass(ref wndclass);
var hwnd = PInvoke.User32.CreateWindow(
classname,
"test",
WindowStyles.WS_CAPTION |
WindowStyles.WS_VISIBLE,
100, 100, 1280, 720,
IntPtr.Zero, IntPtr.Zero,
hIsnt.DangerousGetHandle(),
IntPtr.Zero
);
return hwnd;
}
static unsafe void HandleEvents()
{
MSG msg;
while (PeekMessage(&msg, HWND, 0, 0, PeekMessageRemoveFlags.PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
我会感谢您提供的任何帮助!
解决方案
我知道了!问题是传递给非托管代码的 WndProc 委托被 GC 释放,并且使用大型数组会导致垃圾回收。简单的解决方法是保留传递给窗口类的 WndProc 委托的引用。
推荐阅读
- shell - ${$1:-"world"} 中的“错误替换”
- mysql - BigQuery:如何对 2.5 年的用户群进行同期群/留存分析?
- powershell - 如何将 2 个属性合并到 AD 对象的单个输出中?
- reactjs - 无法从“App.js”解析“./aws-exports”
- scala - 迭代数据框时任务不可序列化,scala
- python - Google Admin SDK 延迟
- firebase - Firebase 托管重写在本地工作但不在云中工作 - 错误?
- jquery - JQuery - 调用相同的页面但使用不同的模型数据
- javascript - 在使用 Datatables (JS) 下载文件之前和之后执行代码
- arrays - 在字典中快速添加键值对