“试图读取或写入受保护的内存。这通常表明其他内存已损坏” DllImporting C#
Posted
技术标签:
【中文标题】“试图读取或写入受保护的内存。这通常表明其他内存已损坏” DllImporting C#【英文标题】:"Attempted to read or write protected memory. This is often an indication that other memory is corrupt" DllImporting C# 【发布时间】:2010-02-27 01:57:21 【问题描述】:我在尝试将数据编组到我的 DLL 函数并返回到 C# 代码时遇到这个奇怪的错误。我看不到我在哪里传递 null 或读取无效内存,而且这个错误非常模糊。有什么线索吗??
代码如下:
FeeCalculation函数在DLL中导出如下:
extern "C" __declspec(dllexport) void __stdcall FeeCalculation(char *cin,
char *cout, char *flimit, char *frate,
char *fwindow, char *fincrement, char *fbird,
char *fparameter, char *fvalidation, char *fcoupon);
[StructLayout(LayoutKind.Sequential)]
public struct feeAnswer
public uint fee;
public uint tax1;
public uint tax2;
public uint tax3;
public uint tax4;
public uint surcharge1;
public uint surcharge2;
public uint validationFee;
public uint couponFee1;
public uint couponFee2;
public uint couponFee3;
public uint couponFee4;
public ushort dstay;
public ushort mstay;
;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct feeRequest
public byte day;
public byte month;
public uint year;
public byte hour;
public byte minute;
public byte rate;
public byte validation;
public byte coupon1;
public byte coupon2;
public byte coupon3;
public byte coupon4;
;
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(feeRequest cin,
out feeAnswer cout, string flimit,
string frate, string fwindow, string fincrement, string fbird,
string fparameter,
string fvalidation, string fcoupon);
public Form1()
InitializeComponent();
private void button1_Click(object sender, EventArgs e)
feeRequest freq = new feeRequest();
feeAnswer fans = new feeAnswer();
string flim = "";
string frat = "";
string fwin = "";
string finc = "";
string fbir = "";
string fpar = "";
string fval = "";
string fcoup = "";
freq.day = 26;
freq.month = 2;
freq.year = 2010; //2000 ~ 2099
freq.hour = 20;
freq.minute = 47;
freq.rate = 15;
freq.validation = 1;
freq.coupon1 = 2;
freq.coupon2 = 3;
freq.coupon3 = 4;
freq.coupon4 = 5;
FeeCalculation(freq, out fans, flim, frat, fwin, finc, fbir, fpar, fval, fcoup);
在约翰的建议下:
public static extern void FeeCalculation(feeRequest cin,
out feeAnswer cout,
[MarshalAs(UnmanagedType.LPArray)]
IntPtr flimit,
[MarshalAs(UnmanagedType.LPArray)]
IntPtr frate,
[MarshalAs(UnmanagedType.LPArray)]
IntPtr fwindow,
[MarshalAs(UnmanagedType.LPArray)]
IntPtr fincrement,
[MarshalAs(UnmanagedType.LPArray)]
IntPtr fbird,
[MarshalAs(UnmanagedType.LPArray)]
IntPtr fparameter,
[MarshalAs(UnmanagedType.LPArray)]
IntPtr fvalidation,
[MarshalAs(UnmanagedType.LPArray)]
IntPtr fcoupon);
...
FeeCalculation(freq, out fans, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
【问题讨论】:
【参考方案1】:您的问题很可能是您尚未完成互操作声明。正如我之前所说,您的大多数“字符串”参数实际上都是 out byte[] 参数,(或 out struct)
所以你需要做更多这样的事情
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(feeRequest cin,
out feeAnswer cout,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] flimit,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] frate,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] fwindow,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] fincrement,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] fbird,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] fparameter,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] fvalidation,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[]fcoupon);
您可能不关心其他参数,但该函数仍会尝试写入它们,因此您必须提供虚拟缓冲区并且缓冲区必须足够大以容纳输出。 (因此您可能需要更改 SizeConst)。
如果您的函数允许指向输出的 NULL 指针,那么您可以将不想要的值声明为 IntPtr
并为这些值传递 IntPtr.Zero
。
从长远来看,你确实需要声明所有这个函数想要查看的结构并正确地传递它们。
编辑:好的,您想使用 IntPtr 或 MarshalAs/byte[] 但不能同时使用。
public static extern void FeeCalculation(feeRequest cin,
out feeAnswer cout,
IntPtr flimit,
IntPtr frate,
IntPtr fwindow,
IntPtr fincrement,
IntPtr fbird,
IntPtr fparameter,
IntPtr fvalidation,
IntPtr fcoupon);
FeeCalculation(freq, out fans, IntPtr.Zero, ...
【讨论】:
是的,我知道我必须声明它们。这只是一个测试,看看从 C# 调用我的 dll 是否适用于我正在处理的项目。我会尝试你的建议和看看它是否有效!现在该功能忽略了其余的,但风扇和频率 另一个可能的问题是结构上的包装可能与 C++ 所期望的不匹配。您可以在 cin 上尝试 pack=4 而不是 pack=1。你知道C++结构封装是什么吗? 如果您要问的是,该结构不是通过 pack pragma 打包的...我做了您建议的答案,它抱怨在 ByRef 上有 SizeConst 参数所以我把它拿出来并得到再次访问违规 将不需要的参数更改为 IntPtr,删除除第二个参数以外的修饰符,并通过 IntPtr.Zero 进行休息,现在我收到以下错误: 无法封送“参数 #10”:托管/非托管类型组合无效(Int/UInt 必须与 SysInt 或 SysUInt 配对)。【参考方案2】:为了解决这个问题,我在 .DLL 中的 memmove()
代码周围添加了一个 try / catch block
。然后,我必须确保在所有参数上都使用了ref
关键字,否则内存地址不会正确地引用到 DLL。一旦我这样做了,它现在就可以正常工作而没有访问冲突。我不需要 MarshalAs 声明,也不需要任何 pack 声明。我只是能够使用LayoutKind.Sequential
。
【讨论】:
【参考方案3】:当您使用编组处理非托管代码时,有时使用editbin! 设置堆栈大小可能会有所帮助。尝试设置 16MB 例如运行以下 editbin.exe /stack:16777216 binary_
【讨论】:
以上是关于“试图读取或写入受保护的内存。这通常表明其他内存已损坏” DllImporting C#的主要内容,如果未能解决你的问题,请参考以下文章
System.accessviolationexception 试图读取或写入受保护的内存。这通常表明其他内存已损坏