当我从 C# 代码调用导入的 C++ 函数时,为啥会引发 AccessViolationException?

Posted

技术标签:

【中文标题】当我从 C# 代码调用导入的 C++ 函数时,为啥会引发 AccessViolationException?【英文标题】:Why is an AccessViolationException thrown when I call an imported C++ function from C# code?当我从 C# 代码调用导入的 C++ 函数时,为什么会引发 AccessViolationException? 【发布时间】:2020-03-16 16:32:05 【问题描述】:

我已经阅读了许多关于该主题的主题,但我还没有找到解决问题的方法。

在我的 C# 代码中,我尝试使用 DllImport 调用 C++ 函数。不幸的是,我没有 C++ 代码,但头文件提供了有关函数的信息:

ABCD Initialize
(
  IN FLOAT a  
  , IN DWORD b  
);  

ABCD也在头文件中定义:

#define EFGH extern "C"
...
#define ABCD EFGH __declspec(dllexport) HRESULT WINAPI

在 C# 代码中,我尝试像这样调用 Initialize 函数:

[DllImport("path.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern UInt32 Initialize([In] float a, [In] UInt32 b);
...
Initialize(50.0f, 0);

根据this site,不需要对数据类型float 进行编组,我可以使用UInt32 来代替DWORD。对于返回类型HRESULT WINAPI,我使用数据类型UInt32,据我了解,也不需要编组? (我也尝试使用其他数据类型作为参数和返回值,但总是得到相同的异常)

对于 DllImport 属性,已经测试了CharSetCallingConventions 的许多可能性,但我没有成功。在声明函数时,我还尝试了这两种变体,有和没有参数的[In] 属性。

函数调用时,抛出异常:

System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'

我在这里遗漏了什么吗?成功调用该函数的下一步是什么?

谢谢。


这是完整的 C# 代码。如前所述,我没有用于构建 .dll 文件的代码。我也不允许共享 dll。

using System;
using System.Runtime.InteropServices;

namespace MyNamespace

  class Program
  
    [DllImport("path.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    public static extern UInt32 Initialize([In] float a, [In] UInt32 b);

    static void Main(string[] args)
    
      Initialize(50.0f, 0); // <- throws System.AccessViolationException
      Console.ReadLine();
    
  

【问题讨论】:

如此处所述,该代码是正确的。我敢打赌,当您从真实代码更改某些内容时,您转录了某些内容。提交minimal reproducible example。 感谢您的评论!不幸的是,我不允许共享 .dll 文件,因此我无法提供可重现的示例。 C# 代码已接近完成,命名空间由一个带有 Main 函数的类组成,该函数仅调用 Initialize(50.0f, 0); 当然你可以分享一个可重现的例子。切掉任何敏感的东西。 @DavidHeffernan 我编辑了问题并添加了完整的 C# 代码。不幸的是,我没有用于构建 C++ dll 的代码。此外,我不允许共享 .dll 文件或其中的一部分。头文件的(我猜的)相关部分可以在问题中找到。不幸的是,我不允许发布完整的 .h 文件。希望有帮助。感谢您的宝贵时间。 @luk:我可以向您保证这里不需要[In],从而为您节省一些组合来尝试。这是 x86 或 x64 代码还是其他架构?有些人比其他人更关心调用约定。 WINAPI 应该像您的 p/invoke 已经指定的那样为您设置 stdcall。 【参考方案1】:

终于,我可以确定问题所在了。

为了检查参数的数据类型和返回值,我在dll上使用了反编译器Snowman。看起来 dll 只定义了接口,并且实现在另一个 dll 文件中,开发人员首先忘记发送了。

一旦第二个 .dll 文件存储在我的程序的运行时文件夹中,函数调用就起作用了。我还想提一下,无论有无 [In] 参数属性,该调用都适用。

虽然我不知道引发异常 AccessViolationException: Attempted to read or write protected memory... 的确切原因(我认为另一个异常可能更适合这里),但我很高兴这个问题可以得到解决。

感谢 Ben VoigtDavid Heffernan 的 cmets!

【讨论】:

以上是关于当我从 C# 代码调用导入的 C++ 函数时,为啥会引发 AccessViolationException?的主要内容,如果未能解决你的问题,请参考以下文章

调试由 C# Visual Studio 2010 插件导入的 C++ dll

为啥当我调用 DataAdapter.Update() 时,此 C# 代码会生成语法错误?

当我从 C 和 C# 过渡到 C++ 时,我可以期待有啥不同?

从 C# 调用 C++ DLib 导致错误分配异常

从 C# 调用 C++ 代码而不创建 dll?

当我从另一个视图控制器调用时,为啥我的表视图为零?