我目前正在使用来自非托管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 ++并返回吗?
谢谢!
答案 0 :(得分:3)
C ++没有标准的ABI。即使在双方都使用相同语言的情况下,跨DLL使用C ++类也不是一个好主意。
有更好的方法。
使用全局函数,cdecl或stdcall替换您的__thiscall
类方法,无论您喜欢哪个(但请注意,C#和C ++具有不同的默认值,如果您不做任何事情,C ++将使用cdecl,C#将作为stdcall)。您可以像现在一样在第一个参数C#中的IntPtr中传递类的“ this”指针。另外,如果您要编写extern "C"
或使用模块定义文件,它们将具有易于理解的名称。
如果需要对象,请使用COM。声明一个从IUnknown继承的接口,用C ++实现(我通常使用ATL),并导出一个全局函数以创建该对象的实例(ATL中的两行,CComObject<T>::CreateInstance
后跟AddRef
) 。无需注册,类型库,您只需实现IUnknown(但是如果要从多个线程使用它们,则可以使用see this)
更新:字符串确实更难。将[MarshalAs(UnmanagedType.LPTStr)]
应用于自变量。将[return: MarshalAs(UnmanagedType.LPTStr)]
应用于函数。在DllImport中指定PreserveSig=true
。最后,修改C ++代码以返回字符串的副本,即先依次调用strlen
和CoTaskMemAlloc
(不要忘记'\0'
)和strcpy
。
更简单的字符串处理方式如下:
HRESULT TryString( TestClass *instance, BSTR i, BSTR *o )
至少有CComBSTR
和_bstr_t
内置的类来处理内存管理。