从.Net

时间:2020-10-09 15:38:34

标签: c# c++ .net

现在我有一个c ++ DLL项目,用于.Net中较低级功能的包装。

C ++代码:

string testA(uint a) {
    cout << a;
    return "egrt";
}
bool testB(uint a) {
    cout << a;
    return false;
}

C ++标头:

#pragma once

#ifdef API_EXPORTS
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif

typedef unsigned int uint;

extern "C" API string testA(uint a);
extern "C" API bool testB(uint a);

C#代码:

[DllImport(...)] extern static string testA(uint a);
[DllImport(...)] extern static bool testB(uint a);

static void Main() {
    testA(10);
    testB(13);
}

由于某种原因,testA中的参数不是我给出的数字,但是在testB中,它可以正常工作。

那是正常行为吗?我究竟做错了什么?我该如何避免呢?

如果需要,我很乐意提供有关解决方案设置的更多详细信息。

1 个答案:

答案 0 :(得分:3)

DllImport的默认调用约定为stdcall,但C / C ++中的默认调用约定通常为cdecl。您描述的症状很容易由两种语言之间的调用约定不匹配引起,因此请确保两组代码都同意使用哪种调用约定。

此外,您通常不能跨DLL边界(绝对不是在语言之间)安全地传递C ++ std::string(或任何其他基于类的字符串类型)。跨越语言界限时,仅使用便携式POD类型非常重要。大多数语言都与C兼容,因此请仅使用C可以使用的类型(因此,在这种情况下,对字符串使用char* / wchar_t*)。

还有一些内存管理问题也必须考虑在内。内存的分配方式,负责释放内存的人员以及内存的分配方式等。在这种情况下,如果C / C ++ DLL返回char*字符串,并且C#自动将数据编组为本地string ,则C#将尝试使用char*释放CoTaskMemFree()字符串,因此DLL需要使用char*分配CoTaskMemAlloc()字符串,例如:

char* __stdcall testA(uint32_t a) {
    cout << a;
    const char *str = "egrt";
    char *result = (char*) CoTaskMemAlloc(strlen(str)+1);
    if (result)
        strcpy(result, str);
    return result;
}

bool __stdcall testB(uint32_t a) {
    cout << a;
    return false;
}
#pragma once

#include <cstdint>

#ifdef API_EXPORTS
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif

extern "C" API char* __stdcall testA(uint32_t a);
extern "C" API bool __stdcall testB(uint32_t a);
[DllImport(..., CharSet=CharSet.Ansi)]
extern static string testA(uint a);

[DllImport(...)]
extern static bool testB(uint a);

static void Main() {
    testA(10);
    testB(13);
}

如果这不是DLL的选择,则必须将char*字符串整理为IntPtr,并且DLL将必须导出一个附加函数供C#调用。正确释放char*字符串,例如:

char* __stdcall testA(uint32_t a) {
    cout << a;
    const char *str = "egrt";
    char *result = ...; // allocated some other way...
    if (result)
        strcpy(result, str);
    return result;
}

void __stdcall freeA(void *p) {
    cout << p;
    // deallocate p as needed...
}

bool __stdcall testB(uint32_t a) {
    cout << a;
    return false;
}
#pragma once

#include <cstdint>

#ifdef API_EXPORTS
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif

extern "C" API char* __stdcall testA(uint32_t a);
extern "C" API void __stdcall freeA(void *p);
extern "C" API bool __stdcall testB(uint32_t a);
[DllImport(...)]
extern static IntPtr testA(uint a);

[DllImport(...)]
extern static void freeA(IntPtr p);

[DllImport(...)]
extern static bool testB(uint a);

static void Main() {
    IntPtr p = testA(10);
    // string s = Marshal.PtrToStringAnsi(p);
    freeA(p);
    testB(13);
}
相关问题