【发布时间】:2014-03-07 21:47:10
【问题描述】:
我正在包装一个 C DLL(我只有 .h 的)并且我被“试图读取或写入受保护的内存”所困。
信息:
- Windows 7 64 位
- DLL 是 64 位的
- C# 应用程序和包装器是 64 位的
导出定义:
#if defined(_WIN32) && !defined(__SYMBIAN32__)
#define EXP_API __cdecl
#else
#if !defined(__SYMBIAN32__)
#define EXP_API
#else
#define EXP_API EXPORT_C
#endif
#endif
这是 C 标头结构:
typedef struct bufferstrm bufferstrm_tt;
struct bufferstrm
{
uint32_t (EXP_API * bytes_usage)(bufferstrm_tt *bs);
uint8_t * (EXP_API * get_stuff)(bufferstrm_tt *bs, uint32_t length);
struct implstrm* some_struct;
};
int32_t EXP_API strmParser(struct bufferstrm_tt *bs);
我的 C# 包装器:
[DllImport("Parser.dll", EntryPoint = "strmParser", CallingConvention = CallingConvention.Cdecl)]
public static extern int strmParser(ref bufferstrm bs);
// Delegates
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint BUFSTRM_bytes_usage(ref bufferstrm bs);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr BUFSTRM_get_stuff(ref bufferstrm bs, uint length);
[StructLayout(LayoutKind.Sequential)]
public struct bufferstrm
{
public BUFSTRM_bytes_usage bytes_usage;
public BUFSTRM_get_stuff get_stuff;
public IntPtr some_struct;
}
[StructLayout(LayoutKind.Sequential)]
public struct implstrm
{
public uint dummy;
}
public static uint test_bytes_usage(ref bufferstrm bs)
{
return 0;
}
public static IntPtr test_get_stuff(ref bufferstrm bs, uint length)
{
return IntPtr.Zero;
}
如果我这样使用它: [尝试读取或写入受保护的内存]
bufferstrm bs = new bufferstrm();
bs.bytes_usage = BUFSTRM_bytes_usage(test_bytes_usage);
bs.get_stuff = BUFSTRM_get_stuff(test_get_stuff);
implstrm testStruct = new implstrm();
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(testStruct)));
Marshal.StructureToPtr(testStruct, ptr, false);
bs.some_struct = ptr;
int hr = strmParser(ref bs);
如果我不设置回调,它只会返回预测的 hr 值(缺少东西)。
有人知道我做错了什么吗?
谢谢!
编辑:
启用“非托管代码调试”我得到“访问冲突读取位置 0xffffffffffffffff”。这能告诉你们什么吗?
【问题讨论】:
-
我的第一直觉(虽然还没有仔细查看代码)是仔细检查所有委托/函数/函数指针上的调用约定匹配。
-
我已经检查过了,一切都是 CallingConvention.Cdecl,就像 DLL 头文件一样:/
-
你确定 C 头文件使用的是 cdecl 吗?来自MSDN:
On ARM and x64 processors, __cdecl is accepted and ignored by the compiler. -
我们需要有关错误的更多信息,至少需要堆栈跟踪或更多调试信息。似乎本机代码正在尝试取消引用 NULL(由于您的
IntPtr.Zero)。我建议修改您的test_get_stuff以改为返回指向某些非托管内存的有效指针(使用Marshal.AllocHGlobal分配它,并在test_bytes_usage中使用相同的大小。并且不要忘记在您使用后Marshal.FreeHGlobal完成)。 -
另外,根据您的编辑(尝试在 0xffffffff... 又名 -1 读取),本机代码可能不安全,因为它使用 (length-1) 来索引最后一个您提供的数据项(长度为 0)。按照我的建议尝试一个非空数组。