java - 在 .NET 4.5 或更高版本中具有互操作回调的 C# AccessViolationException
问题描述
我正在研究 C# 中的屏幕阅读器技术,并遇到有关使用 C++ 编写的互操作库捕获事件的问题(Java Access Bridge - https://github.com/openjdk/jdk/tree/05a764f4ffb8030d6b768f2d362c388e5aabd92d/src/jdk。使用 .NET 4.5+(64 位)编译时的可访问性/windows/native/libwindowsaccessbridge )。它在 Debug 和 Release 构建配置中崩溃。然而,该功能在 .NET 4.0 中运行良好。
每当发生应触发注册回调的事件时,就会发生 AccessViolationException,从而使应用程序崩溃。我怀疑这与 .NET 4.5 中引入的垃圾收集器更改有关,但希望能够帮助您找到确切的问题。
这里有一个完整的最小可重现代码项目,它订阅了鼠标点击事件 -> https://github.com/needmoreraminbrainnow/screenreadertest。
当它使用 .NET 4.5 编译时,它会在基于 Java 的应用程序中发生 mouseclick 事件时崩溃
委托被创建为类变量以维护引用。C# 端的两个重要方法是...
[DllImport(WINDOWS_ACCESS_BRIDGE, SetLastError = true, ThrowOnUnmappableChar = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
private static extern void setMouseClickedFP(MouseClickedDelegate fp);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MouseClickedDelegate(System.Int64 vmID, IntPtr jevent, IntPtr ac);
我相信相关的 C++ 方法是......
void fireMouseClicked(long vmID, JOBJECT64 event, JOBJECT64 source);
和...
void AccessBridgeEventHandler::method(long vmID, JOBJECT64 event, JOBJECT64 source) { \
DEBUG_CODE(char debugBuf[255]); \
DEBUG_CODE(sprintf(debugBuf, fireEventDebugString, #method, event, source, vmID)); \
DEBUG_CODE(AppendToCallInfo(debugBuf)); \
if (eventFP != (FPprototype) 0) { \
eventFP(vmID, event, source); \
} else { \
DEBUG_CODE(AppendToCallInfo("[ERROR]: eventFP == 0")); \
} \
}
我能找到的与 setMouseClicked 相对应的所有内容都在 C 代码中......
void SetMouseClicked(AccessBridge_MouseClickedFP fp) {
if (theAccessBridgeInitializedFlag == TRUE) {
theAccessBridge.SetMouseClicked(fp);
}
}
并且...
#define CALL_SET_EVENT_FP(function, callbackFP) \
void WinAccessBridge::function(callbackFP fp) { \
eventHandler->function(fp, this); \
/* eventHandler calls back to winAccessBridgeDLL to set eventMask */ \
}
CALL_SET_EVENT_FP(setMouseClickedFP, AccessBridge_MouseClickedFP)
我尝试使用固定委托,GCHandle.Alloc(_mouseClickDelegate, GCHandleType.Pinned);
但运行时会导致Object contains non-primitive or non-blittable data
错误。根据对类似问题和微软文章的研究,这似乎没有必要。
我试过使用GC.KeepAlive
代表
在设置回调并使用不同的调用约定时,我还尝试了与委托编组相关的更改,但无济于事。
我还尝试使用以下结构作为回调参数类型而不是 IntPtr ...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class JOBJECT64
{
public static JOBJECT64 Zero = default(JOBJECT64);
private readonly long _value;
public JOBJECT64(long value)
{
_value = value;
}
public long Value
{
get { return _value; }
}
public static bool operator ==(JOBJECT64 x, JOBJECT64 y)
{
return x._value == y._value;
}
public static bool operator !=(JOBJECT64 x, JOBJECT64 y)
{
return x._value == y._value;
}
public override bool Equals(object obj)
{
if (obj is JOBJECT64)
{
return this == (JOBJECT64) obj;
}
return false;
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
}
如何确认代表被移动或无法访问是否存在问题?
解决方案
.NET 4.5中有一些重大变化。如果 4.0 适合您,请使用它。在它编译并发布之后,它应该不重要。
推荐阅读
- php - 调用未定义的方法 Goutte\Client::setClient()
- html - HTML5 是否允许标题(如 h1)内的块级元素(如 div)?
- google-cloud-platform - Google Cloud Vision:模型未导出
- rest - 创建一个没有冲突的 RESTful 动作
- node.js - 无法从“node_modules\@firebase\firestore\dist\rn\index.js”解析“./prebuilt.rn-f9cd27ba.js”
- typescript - 我无法通过 electron-main.dev.js/electron-main.js 的 tslint 问题构建类星体。项目设置后我从未编辑过这两个标准文件
- python - 使用 pickle 检索保存的对象时出错
- typescript - 为什么打字稿中的接口有时需要严格匹配属性,有时不需要
- pandas - 如何选择具有相同 id 并在另一列中具有所有缺失值的行
- python - cudatoolkit 和 cuda 的兼容性