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

时间:2019-01-08 11:38:55

标签: c# c++ marshalling

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

它对于int很好用。不幸的是,当我尝试发送另一种数据类型时,我遇到了一个转换错误,例如 float 4.2f变为1 字符串“ 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 ++代码的C#Interop Proxy类如下:

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 ++并返回吗?

谢谢!

1 个答案:

答案 0 :(得分:3)

C ++没有标准的ABI。即使在双方都使用相同语言的情况下,跨DLL使用C ++类也不是一个好主意。

有更好的方法。

  1. 使用全局函数,cdecl或stdcall替换您的__thiscall类方法,无论您喜欢哪个(但请注意,C#和C ++具有不同的默认值,如果您不做任何事情,C ++将使用cdecl,C#将作为stdcall)。您可以像现在一样在第一个参数C#中的IntPtr中传递类的“ this”指针。另外,如果您要编写extern "C"或使用模块定义文件,它们将具有易于理解的名称。

  2. 如果需要对象,请使用COM。声明一个从IUnknown继承的接口,用C ++实现(我通常使用ATL),并导出一个全局函数以创建该对象的实例(ATL中的两行,CComObject<T>::CreateInstance后跟AddRef) 。无需注册,类型库,您只需实现IUnknown(但是如果要从多个线程使用它们,则可以使用see this

更新:字符串确实更难。将[MarshalAs(UnmanagedType.LPTStr)]应用于自变量。将[return: MarshalAs(UnmanagedType.LPTStr)]应用于函数。在DllImport中指定PreserveSig=true。最后,修改C ++代码以返回字符串的副本,即先依次调用strlenCoTaskMemAlloc(不要忘记'\0')和strcpy

更简单的字符串处理方式如下:

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

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