使用Marshal.Copy从IntPtr复制到Double[]时出现Access Violation Error如何解决
代码错误原因
- 参数语义完全不匹配:你C代码中第二个参数
double* pSamples的语义是「调用方提前分配好对应长度的double数组内存,把数组首地址传入,C负责往这段已分配的内存写值」。但你C#的DllImport声明中将该参数写为out IntPtr pSamples,相当于给C传入的是IntPtr变量的内存地址(即二级指针),C把这个二级指针的值当成double数组的首地址直接写入,本质是往随机的非法内存地址写数据,直接触发访问冲突。 - 基础语法错误:
- DllImport声明中返回值拼写错误,应为
void而非voi - C#中指针类型拼写错误,应为
IntPtr而非Intptr,变量名Sample和后续Marshal.Copy调用时的Samples拼写不一致
- DllImport声明中返回值拼写错误,应为
修复方案
方案1(推荐:C#分配托管内存,无内存泄漏风险)
不需要手动调用Marshal.Copy,运行时会自动处理封送,C++代码不需要修改,仅修改C#代码即可:
// 修改DllImport声明,直接传入double数组,加[Out]特性告知运行时需要把C++侧修改的内容回写到托管数组 [DllImport("LibOperation.dll", CallingConvention = CallingConvention.Cdecl)] static public extern void FillDoubleValues(ulong _NumSamplesToCapture, [Out] double[] pSamples); void Main() { ulong NumSamplesToCapture = 5000; double[] ys1 = new double[NumSamplesToCapture]; // 直接传入托管数组即可 FillDoubleValues(NumSamplesToCapture, ys1); // 直接使用ys1即可 }
方案2(C++分配非托管内存,C#读取后手动释放)
如果你的业务逻辑要求必须由C分配内存,需要同时修改C和C#代码:
修改后的C++代码
// 注意第二个参数改为二级指针,必须用CoTaskMemAlloc分配内存,C#的Marshal才能正常读取和释放 __declspec(dllexport) void FillDoubleValues(IN ULONG64 _NumSamplesToCapture, OUT double** pSamples) { *pSamples = (double*)CoTaskMemAlloc(_NumSamplesToCapture * sizeof(double)); for (int i = 0; i < _NumSamplesToCapture; i++) { (*pSamples)[i] = (double)rand() / RAND_MAX; } }
修改后的C#代码
[DllImport("LibOperation.dll", CallingConvention = CallingConvention.Cdecl)] static public extern void FillDoubleValues(ulong _NumSamplesToCapture, out IntPtr pSamples); void Main() { ulong NumSamplesToCapture = 5000; IntPtr pSamples = IntPtr.Zero; FillDoubleValues(NumSamplesToCapture, out pSamples); double[] ys1 = new double[NumSamplesToCapture]; Marshal.Copy(pSamples, ys1, 0, (int)NumSamplesToCapture); // 必须手动释放C++分配的非托管内存,否则会出现内存泄漏 Marshal.FreeCoTaskMem(pSamples); // 后续正常使用ys1即可 }
内容的提问来源于stack exchange,提问作者Pruthvi Patel
相关产品推荐
相关产品推荐

