从 C# windows 应用程序调用 C dll 会导致 svchost.exe 崩溃

Posted

技术标签:

【中文标题】从 C# windows 应用程序调用 C dll 会导致 svchost.exe 崩溃【英文标题】:Calling a C dll from C# windows application causes the svchost.exe to crash 【发布时间】:2017-08-04 09:42:00 【问题描述】:

我创建了一个C DLL,以便可以在我的C# 应用程序中使用它。 我在 C++ 测试应用程序上测试了 DLL,它运行良好,但在 C# 应用程序中无法运行。 由于某种原因,我无法构建DLL 的调试版本,因此我也无法在调试模式下运行C# 应用程序。DLL 调试配置找不到include directories,在发布模式下,它工作得很好! 我需要说的是,我在下面给出了一个特定的方法,它会导致崩溃,从DLL 调用其他方法很好并且可以按预期工作。 这是主要的实现: 标题定义:

//use this function to classify an image
CDLL_API const char* Classify(const char* img_path, int N = 2);

.cpp 实现

CDLL_API const char* Classify(const char * img_path, int N)
    
        auto classifier = reinterpret_cast<Classifier*>(GetHandle());
        std::vector<PredictionResults> result = classifier->Classify(std::string(img_path), N);
        std::string str_info = "";
        std::stringstream ss;
        for (size_t i = 0; i <result.size(); ++i)
        
            auto label = result[i].label;
            auto acc = result[i].accuracy;
            ss << "label=" << label << ",acc=" << acc << "|";
        
        return ss.str().c_str();
    

C#代码:

[DllImport(@"CDll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern string Classify([MarshalAs(UnmanagedType.LPStr)]string img_path,int N = 2);

//...
        var s = Classify(txtFilePath.Text, 2);
        MessageBox.Show(s);

所以我完全不知道什么是真正的原因。

【问题讨论】:

您使用CallingConvention.Cdecl而不是CallingConvention.StdCall的任何特殊原因? @stuartd:这实际上是我几年前写的一段代码,我不记得我为什么选择 Cdecl 而不是 StdCall!顺便说一句,我测试时 StdCall 也有同样的问题 【参考方案1】:

我看到您在 C# PInvoke 声明中指定了对Cdecl (CallingConvention = CallingConvention.Cdecl) 的调用约定;因为这也是 C++ 代码中的默认调用约定,所以在这种情况下您不应该有任何调用约定不匹配。不过,请注意 C 接口 DLL 的通用调用约定是 __stdcall

我看到的问题是您从 C 接口 API 返回字符串的方式

CDLL_API const char* Classify(const char * img_path, int N)

    ...
    return ss.str().c_str();

(顺便说一句,我假设 ss 类似于 std::ostringstream 对象。)

您使用输出字符串流(调用其str 方法)构建一个字符串,然后您会得到一个调用c_str 的原始C 样式字符串指针。但是当函数退出时,字符串对象被销毁,所以C风格的原始字符串指针不再有效。

要将字符串从 C 接口 DLL API 返回到 C#,您可以考虑以下选项之一:

    从 C 接口 DLL 返回 BSTR 字符串。使用 SysAllocString 从原始 C 样式字符串指针创建 BSTR 对象。请注意,BSTRs “自然”存储 Unicode UTF-16 编码的字符串,因此请确保将您的字符串转换为这种编码。 CLR 能够很好地管理BSTR 字符串,因此您不必注意释放字符串内存:这将是 CLR 的工作。

    向 C 接口 DLL 函数添加几个参数:指向缓冲区的指针缓冲区大小。这将是一个输出字符串缓冲区,由调用者(例如 C#)分配,从 DLL 导出的 C 接口 API 会将结果字符串写入调用者提供的缓冲区。这就是例如GetWindowText Win32 API 确实如此(在 C# 端,输出字符串缓冲区可以由 StringBuilder 对象表示)。

【讨论】:

【参考方案2】:

C#中的string类型与C中的const char *不兼容,你必须使用StringBuilder

 [DllImport("aCDLL.dll")]
 public extern static void getabuilder(StringBuilder abuilder);

在 C dll 中:

 extern "C" void __declspec(dllexport) __stdcall getabuilder(char *abuilder);

如果您不喜欢 StringBuilder,可以将字符串字符存储在 C# 中初始化的 byte 数组中并传递给 C 函数:

 [DllImport("aCDLL.dll")]
 public extern static void getastring(byte[] data, ref int datalength);

在 C 中:

 extern "C" void __declspec(dllexport) __stdcall getastring(const char *data, int *datalength);

【讨论】:

以上是关于从 C# windows 应用程序调用 C dll 会导致 svchost.exe 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

C++ DLL 中的 C# 表单应用程序

有啥方法可以调试从 C# DllImport 调用的 c++ dll?

如何从本机 C(++) DLL 调用 .NET (C#) 代码?

从 C# 调用 C++ dll 函数时出现问题

从 C++ 调用 C# dll

无法在 C# Windows 应用程序下写入 C++ dll 项目中的文件