由于项目需要,*在C++中通过托管方式调用C#的类库,本来调用也很简单,但是由于C#中的数据类型和C++是不一样的,所以处理起来就比较麻烦,这不,我自己就为了理顺这个数据类型,花了好几天时间。
C#类库代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace StringDll
{
public class Class1
{
public int GetString(out string strRet)
{
strRet = "120,;,0;133,1;142,0;150";
return strRet.Length;
}
public static int DataRead(int m_TableType, ref byte[] m_InData, int m_InLen, ref byte[] m_OutData, ref int m_OutLen)
{
m_OutData = Encoding.Unicode.GetBytes("abc严峻");
string str = Encoding.Unicode.GetString(m_OutData);
return 0;
}
}
}
这部分代码加入到C++中后,查看函数原型的时候就发生了变化,第一个函数就变成了
int StringDll::Class1::GetString(System::String ^% strRet)
第二个函数变为:
static int StringDll::Class1::DataRead(int m_TableType, cli::array<unsigned char>^% m_InData, int m_InLen, cli::array<unsignded char>^% m_outData, int %m_outLen)
GetString()函数调用起来比较简单。
Class1 ^c1 = gcnew Class1;
int nRet = 0;
System::String ^str1;
nRet = c1->GetString(str1);
调用完毕后可以根据需要,调用命名空间System::Runtime::InteropServices中的类Marshal的StringToHGlobalAnsi()函数或者StringToHGlobalUni()函数转换为需要的char*或者wchar_t*字符串。
char* szRet = (char*)(void*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(str1);
wchar_t *wszRet = (wchar_t*)(void*)System::Runtime::InteropServices::Marshal::StringToHGlobalUni(str1);
DataRead()这个函数的调用,费了我很长时间,主要是没明白到底应该如何去声明或者定义合适的变量,尝试了很多方法。
什么object类型的变量啊, System.Byte类型的变量啊,试了很多种,但是总是不能作为DataRead()函数的参数,编译过程中出现了类似于"从“unsigned char [1024]”转换为“cli::array<Type,dimension> ^"之类的错误,百思不得其解。
今天再来调查的时候,仔细看了一下这个函数在C++中呈现出来的函数原型,然后想着就照着函数原型里面显示的数据类型来声明和定义变量,说做就做,OK,成功了。
cli::array<unsigned char>^ inData = gcnew cli::array<unsigned char>{};
cli::array<unsigned char>^ outData = gcnew cli::array<unsigned char>{};
int nlen = 0;
Class1::DataRead(0, inData, 0, outData, nlen);
由于在DataRead()函数中使用的是m_OutData = Encoding.Unicode.GetBytes("abc严峻"),因此在C++代码中,也还是可以使用托管代码来将这个数组转换为字符串。
str1 = System::Text::Encoding::Unicode->GetString(outData);
szRet = (char*)(void*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(str1);
OK,到此为止,遇到的拦路虎都被排除了,继续下一步。
心得:
C#中的数据类型与C++是不一样的,特别是跨语言调用的时候,往往会很伤人,但是仔细一想的话,其实不管C#的函数本身是如何定义参数的,在C++中用托管方式调用的时候,总是能够看见经过转换后的函数原型的,而这个时候需要的参数实际上已经经过了转换了,那么,我们只需要按照函数原型中呈现出来的参数类型去声明和定义变量,就是可以被这个函数使用的参数了。
追加代码:
public static int InPutString(string strIn)
{
return 0;
}
public static int InputByte(byte[] m_inData)
{
string str = Encoding.Unicode.GetString(m_inData);
return 0;
}
char* szTest = "123456";
String^ str2 = gcnew String(szTest);
Class1::InPutString(str2);
cli::array<unsigned char>^ inData1 = System::Text::Encoding::Unicode->GetBytes(str2);
Class1::InputByte(inData1);