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



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

它对int很有效。不幸的是,当我尝试发送另一个数据类型时,我会收到一个转换错误,例如float 4.2f变为1字符串"fourtytwo"变为-15219011360

我的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++代码,如下所示:

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); 
}
}
}

你知道如何正确地将float、字符串从C#封送到C++并返回吗?

谢谢!

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

有更好的方法。

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

  2. 如果你想要对象,请使用COM。声明一个从IUnknown继承的接口,在C++中实现它(我通常使用ATL),并导出一个全局函数来创建该对象的实例(ATL中有两行,CComObject<T>::CreateInstance,后面跟着AddRef)。无需注册,键入库,只需实现IUnknown(但如果您想从多个线程使用它们,请参阅此)

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

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

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

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

最新更新