【问题标题】:Crash With DllImport C#使用 DllImport C# 崩溃
【发布时间】:2016-10-13 15:56:29
【问题描述】:

帮派,

我正在使用用(看起来像)MFC C++ 编写的第三方 API。我的应用程序是 C# 中的 WinForms 应用程序。 API 的公司有一个 C# 演示应用程序,我一字不差地使用了他们的代码。相关代码长这样……

API 的 C++ 函数:

int PASCAL CppFunction(BYTE *pbAdapterID, BYTE *pbTargetID, char *pcVendor, char *pcProduct, char *pcRelease, int iMessageBox)
{
    // The function definition is all I can see in the API's documentation
}

DllImport 声明:

[DllImport("api.dll")]
extern public static int CppFunction(ref byte pbAdapter, ref byte pbTargetID, string pcVendor, string pcProduct, string pcRelease, int iMessageBox);

我的 C# 逻辑:

public void CallCppFunction()
{
    try
    {
        int result = 0;
        string strVendorBuffer = string.Empty;
        string strProductBuffer = string.Empty;
        string strReleaseBuffer = string.Empty;
        string strSerial = string.Empty;
        byte bAdapt = 0;
        byte bTarg = 0;

        result = CppFunction(ref bAdapt, ref bTarg, strVendorBuffer, strProductBuffer, strReleaseBuffer, 0);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

这是存在于他们的 C# 示例中的代码。运行时,即使在 try/catch 中,它也会因“FatalExecutionEngineError”而崩溃。

我不太了解 PInvoke,但是这些变量不是必须作为非托管类型编组吗?任何帮助将不胜感激。

【问题讨论】:

  • 我很确定我知道答案,但你需要告诉我 CppFunction 应该首先做什么。
  • 这是一个猜测。这可能是由于您的字符串处理(您没有指定如何编组它们,使用[MarshalAs])。如果输出的是字符串参数,应该使用StringBuilder而不是string(带有一些预分配的存储空间)
  • 如果不知道界面是什么,没有人可以说。 C++ 函数原型没有定义接口。 ref byte 几乎可以肯定是错误的。但它应该是什么?我们怎么可能知道所提供的信息?我们可以猜测,但你真的会通过猜测来写代码吗?
  • 您可能不需要使用 dllImport。您可能只需要添加 dll 作为参考,并在模块顶部添加适当的“使用”语句。
  • @jdweng 真的。你认为这是一个托管 DLL?

标签: c# c++ pinvoke marshalling dllimport


【解决方案1】:

我在这里进行了疯狂的猜测,但我很确定这将是正确的答案

让我们看一下函数定义:

int PASCAL CppFunction(
    BYTE *pbAdapterID, 
    BYTE *pbTargetID, 
    char *pcVendor, 
    char *pcProduct, 
    char *pcRelease, 
    int iMessageBox);

以及对应的DllImport声明:

[DllImport("api.dll")]
extern public static int CppFunction(
    ref byte pbAdapter, 
    ref byte pbTargetID, 
    string pcVendor, 
    string pcProduct, 
    string pcRelease,
    int iMessageBox);

那里有很多参数,其中一些引起了我的注意:

    char *pcVendor, 
    char *pcProduct, 
    char *pcRelease,

它们不是通过引用传递的,它们是否被库修改了?

注意:这就是为什么我要求您提供有关该功能的详细信息。

我就是这样,你不能编组字符串,因为数据类型字符串是不可变的,你必须使用StringBuilder类,只要确保StringBuilder有足够的Capacity

int result = 0;
StringBuilder strVendorBuffer = new StringBuilder(1024); // up to 1024 chars
StringBuilder strProductBuffer = new StringBuilder(1024); // up to 1024 chars
StringBuilder strReleaseBuffer = new StringBuilder(1024); // up to 1024 chars
StringBuilder strSerial = string.Empty;
byte bAdapt = 0;
byte bTarg = 0;

result = CppFunction(ref bAdapt, ref bTarg, strVendorBuffer, strProductBuffer, strReleaseBuffer, 0);

【讨论】:

  • PASCAL 和 stdcall 一样,是 pinvoke 的默认调用约定
  • 别猜了。这就是提问者是东西的原因。太多的猜测。 ref byte args 肯定是错误的。猜测缓冲区大小也没有意义。那不会有好的结局。字符串在参数中更合理。猜测是非常糟糕的建议。
  • 很抱歉有人给你投了反对票。不幸的是,我发布的是我从供应商那里得到的,所以我们不得不猜测。但是,我尝试用 StringBuilders 替换字符串并且它有效。谢谢!!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-06
  • 2012-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多