从 C# 到 C++ 封送数据类型的类型转换问题

type conversion problem for marshaling datatypes from C# to C++

我目前正在使用非托管 C++ 库中的一些业务逻辑开发 C# (.NET Framework 4.7.2) 应用程序。我尝试在 C# 和 C++ 之间来回传递数据(互操作)。 我可能不会使用 C++/CLI,我的项目中不允许使用公共语言运行时。

它适用于 int。不幸的是,一旦我尝试发送另一种数据类型,我就会收到转换错误,例如float 4.2f 变成 1string "fourtytwo" 变成 -1529101360.

我的 C# 代码如下所示:

// works fine, creates an instance of TestClass    
var test = TestProxy.Wrapper_Create("test"); 

// int, works fine, a = 42
var a = TestProxy.TryInt(test, 42);

// float, problem, b = 1
var b = TestProxy.TryFloat(test, 4.2f);

// string, problem, c = -159101360
var c = TestProxy.TryString(test, "fourtytwo");

我的 C# Interop Proxy class 调用本机(非托管)C++ 代码如下所示:

public static class TestProxy
    {
        private const string coreDLL = "test.core.dll";

        [DllImport(coreDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr Wrapper_Create(string name);

        [DllImport(coreDLL, EntryPoint = "?TryInt@TestClass@@XXX@X", CallingConvention = CallingConvention.ThisCall)]
        public static extern int TryInt(IntPtr instance, int n);

        [DllImport(coreDLL, EntryPoint = "?TryFloat@TestClass@@XXX@X", CallingConvention = CallingConvention.ThisCall)]
        public static extern int TryFloat(IntPtr instance, float n);

        [DllImport(coreDLL, EntryPoint = "?TryString@TestClass@@XXX@X", CallingConvention = CallingConvention.ThisCall)]
        public static extern int TryString(IntPtr instance, string n);

    }

我的原生(非托管)C++ 看起来像这样: 头文件:

#ifdef TESTCORE_EXPORTS
#define TESTCORE_API __declspec(dllexport)
#endif

#pragma once

extern "C"
{
    class TESTCORE_API TestClass
    {
    private:
        char* name;
    public:
        TestClass(char*);
        int TryInt(int);
        float TryFloat(float);
        char* TryString(char*);
    };

    TESTCORE_API TestClass* Wrapper_Create(char* name);
}

实现文件:

#include "stdafx.h"

#include "TESTCore.h"

TestClass::TestClass(char* n) 
{
    name = n;
}

int TestClass::TryInt(int n) 
{
    return n; // works fine
}

float TestClass::TryFloat(float n)
{
    return n; // something goes wrong here
}

char* TestClass::TryString(char* n)
{
    return n; // something goes wrong here
}

extern "C"
{
    TESTCORE_API TestClass * Wrapper_Create(char* name)
    {
        return new TestClass(name);
    }

    TESTCORE_API int TryInt(TestClass * instance, int n) 
    {
        if (instance != NULL) 
        {
            return instance->TryInt(n);
        }
    }

    TESTCORE_API float TryFloat(TestClass * instance, float n) 
    {
        if (instance != NULL) 
        {
            return instance->TryFloat(n);
        }
    }

    TESTCORE_API char* TryString(TestClass * instance, char* n) 
    {
        if (instance != NULL)
        {
            return instance->TryString(n); 
        }
    }
}

您知道如何正确地将浮点数、字符串从 C# 编组到 C++ 并返回吗?

谢谢!

C++ 没有标准的 ABI。跨 DLL 使用 C++ classes 很少是个好主意,即使双方使用相同的语言也是如此。

有更好的方法。

  1. 用你喜欢的全局函数、cdecl 或 stdcall 替换你的 __thiscall class 方法(但请注意 C# 和 C++ 有不同的默认值,如果你什么都不做 C++将使用 cdecl,C# 将导入为 stdcall)。您可以在 C# 中的第一个参数 IntPtr 中传递 class 的 "this" 指针,就像您现在所做的那样。此外,如果您编写 extern "C" 或使用模块定义文件,它们将具有人类可读的名称。

  2. 如果需要对象,请使用 COM。声明一个继承自IUnknown的接口,在C++中实现(我通常使用ATL),并导出一个全局函数来创建该对象的实例(ATL中2行,CComObject<T>::CreateInstance后跟[=​​14=]) .无需注册,类型库,你只需要实现 IUnknown(但是 如果你想从多个线程使用它们)

更新:字符串确实更难。将 [MarshalAs(UnmanagedType.LPTStr)] 应用于参数。将 [return: MarshalAs(UnmanagedType.LPTStr)] 应用于函数。在 DllImport 中指定 PreserveSig=true。最后,将 C++ 代码修改为 return 字符串的副本,即调用 strlen 然后 CoTaskMemAlloc (不要忘记 '[=20=]')然后 strcpy .

处理字符串更简单的方法是这样的:

HRESULT TryString( TestClass *instance, BSTR i, BSTR *o )

至少有 CComBSTR_bstr_t 内置 classes 来处理内存管理。