用C#调C++库函数返回字符串,由于C++本身方法之间调用返回字符串都是一般都是申明void或int返回的方法,然后通过char变量带出返回值。在C++调用这种之前自己先初始化char空间,然后调用的方法把返回值放入char*。
类似下面的,通过oneLineMerge返回处理后的串。调用地方自己先申请足够的空间,然后传递char*
/// <summary>
/// 把多个连续字符合并为一个
/// </summary>
/// <param name="source">源串</param>
/// <param name="oneChar">要合并的字符</param>
/// <param name="retStr">输出串</param>
void MergeCharToOne(char* source, char oneChar, char* retStr);
char oneLine[1024] = { 0 };
char oneLineMerge[1024] = { 0 };
//合并多个连续空格
MergeCharToOne(oneLine, ' ', oneLineMerge);
对应char*带返回值的如果直接用C#的string类型对接会报内存访问错误,或者内存溢出直接程序退出。对于传入参数,C++方法内部不修改的用C#的string对应没问题。
比如我这里有一个医保二维码转身份证的接口对方方法申明如下:
void NationEcTrans(char* url, char* input, char* output);
如果我用C#的P-Invok按下面对都不行。
//这种无法得到output
[DllImport("NationECCode.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void NationEcTrans(string url, string input, string output);
//这种程序直接退出
[DllImport("NationECCode.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void NationEcTrans(string url, string input,out string output);
//这种程序直接退出
[DllImport("NationECCode.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void NationEcTrans(string url, string input,ref string output);
按网上资料都是让char*对string的。其实这是有误解的,传入参数可以正常,带出字符串不行。这里就涉及到托管内存和非托管内存。string是C#的托管内存,传递给C++他是无法写入的,如果C++方法尝试修改内存就会异常了。所以对返回串要申请非托管内存。然后传入内存地址过去。
C++返回串参数C#用IntPtr对应
[DllImport("NationECCode.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void NationEcTrans(string url, string input, IntPtr output);
调用代码如下,先申请非托管内存然后把内存首地址传入,调用返回从申请内存提取数据,然后释放申请内存。
/// <summary>
/// 得到身份证
/// </summary>
/// <returns></returns>
public static string GetIdNo()
{
string input = "{\"data\":{\"orgId\":\"XXXXX\",\"businessType\":\"01101\",\"operatorId\":\"909\",\"operatorName\":\"insu\",\"officeId\":\"A09\",\"officeName\":\"其他\",\"deviceType\":\"\"},\"transType\":\"ec.query\",\"orgId\":\"H14110201536\"}";
//申请足够接返回串的空间
IntPtr outPut = System.Runtime.InteropServices.Marshal.AllocHGlobal(2048);
NationEcTrans("http://10.30.20.249:80/localcfc/api/hsecfc/localQrCodeQuery", input, outPut);
//{"code":0,"data":{"authNo":null,"birthday":null,"chnlId":null,"ecIndexNo":"AAAAAA","ecQrCode":null,"ecToken":"140000ecpuhch57k710100007f0000eff4b702","email":null,"gender":null,"idNo":"CCCCCCCCCC","idType":"01","insuOrg":"140100","nationality":null,"userName":"Tom"},"message":"成功","orgId":"H14110201536"}
//从申请空间转换得到串
string retStr = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(outPut);
//释放空间
System.Runtime.InteropServices.Marshal.FreeHGlobal(outPut);
string startStr = "\"idNo\":\"";
string idNo = "";
if (retStr.Contains(startStr))
{
int startIndex = retStr.IndexOf(startStr);
int endIndex = retStr.IndexOf("\"", startIndex + startStr.Length);
idNo = retStr.Substring(startIndex+ startStr.Length+1, endIndex - startIndex- startStr.Length);
}
else
{
idNo = "-1^" + retStr;
}
return idNo;
}
其实和C++自己调用方法通过char带出返回值一样。给出一个非托管内存首地址给调用方。这就是C#调用C++返回字符串对应类型的一个经验。理解C/C++自己调用就好理解了。而C++自己返回字符串为啥一般申明返回值不直接为string或char的原因主要是方法内部变量是局部变量,方法返回后就回收了。字符串是复杂类型,所以不适合返回char*。C#应该是托管环境处理了返回引用类型,一般都是各种引用类型直接返回了。