100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > C# 调用C/C++动态链接库 结构体中的char*类型

C# 调用C/C++动态链接库 结构体中的char*类型

时间:2023-08-23 08:52:41

相关推荐

C# 调用C/C++动态链接库 结构体中的char*类型

用C#掉用C++的dll直接import就可以之前有不同的类型对应,当要传递结构体的时候就有点麻烦了,这里有一个结构体里边有char*类型,这个类型在C#中调用没法声明,传string是不行的默认string是对应const char*,传stringbuilder得指定大小,不然是没法传的,

查了好久,最后只能用unsafe代码来实现了

用C/C++写一个标准的动态链接库:

头文件,定义了三个接口函数,

#pragma once#define TESTDLL _declspec(dllexport)#ifndef PKI_DATA_ST#define PKI_DATA_STtypedef struct PKI_DATA_st {int size;char *value;}PKI_DATA, *PKI_PDATA;#endifextern "C"{TESTDLL double Add(double a, double b);TESTDLL double Subtract(double a, double b);TESTDLL int TestDn(PKI_DATA cert, PKI_DATA* dn);}

简单实现:

#include "stdafx.h"#include "TestClass.h"double Add(double a, double b){printf("Add \n");return a + b;}double Subtract(double a, double b){printf("Subtract");return a - b;}int TestDn(PKI_DATA cert, PKI_DATA* dn){printf("TestDn cert.value: %s\n", cert.value);dn->size = 10;//dn->value = "helloworld";//这个种写法需要转换dn->value =(char*) TEXT("helloworld");//这种写法在选择unicode字符集时C#是可以直接接受的return 1;}

C#调用,路径可以用当前程序的相对路径,也可以是系统的环境变量中的路径,声明import

[DllImport("USBLoad.dll", EntryPoint = "Add", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]static extern double Add(double a,double b);[DllImport("USBLoad.dll", EntryPoint = "TestDn", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]static extern int TestDn(PKI_DATA cert, ref PKI_DATA dn);[DllImport("USBLoad.dll", EntryPoint = "Subtract", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]static extern double Subtract(double a, double b);

声明结构体:

internal unsafe struct PKI_DATA{internal int size;internal char* value;}

调用:记得属性中勾上允许不安全代码

[HandleProcessCorruptedStateExceptions]//这个可以捕获C++中的异常

[SecurityCritical] static unsafe void TestUSBLoad()//加unsafe{try{var a = Add(10, 10);w(a);a = Subtract(15, 10);w(a);PKI_DATA cert = new PKI_DATA();cert.size = 4;char[] test = "test".ToCharArray();string s = "";fixed (char * v=test)//传值初始化。需要fixed关键字{cert.value = v;PKI_DATA dn = new PKI_DATA();TestDn(cert, ref dn);var encode = Encoding.ASCII;var c = dn.value;List<char> list = new List<char>();for (int i=0;i<dn.size;++i,++c){list.Add(*c);}

//兼容ansi编码,先转bytevar bytes = Encoding.Unicode.GetBytes(list.ToArray());s = Encoding.ASCII.GetString(bytes);//转码w(new string(dn.value));//设置unicode字符集的情况下可以直接这么写就能得到要字符}s = s.Replace("\0","");//转码的时候后边会有很多\0,移除w(string.Format("[{0}]",s));}catch (Exception ex){w(ex.Message);}}

顺便研究了下C++的动态调用、

如果是静态调用需要头文件和lib库,直接引用头文件,加入lib的链接路径,引用后可以直接用,编译就可以了

动态调用用loadlibrary,记得freelibrary,这些函数在windows.h中,这里建的是QT的项目所以引用的是qt_windows.h:

#include "qt_windows.h"#define DLLPATH (LPCTSTR)"USBLoad.dll"#ifndef PKI_DATA_ST#define PKI_DATA_STtypedef struct PKI_DATA_st {int size;char *value;}PKI_DATA, *PKI_PDATA;#endif

//声明入口函数typedef double(*Add_Func)(double, double);typedef int(*TestDn)(PKI_DATA, PKI_DATA*);int main(int argc, char *argv[]){QCoreApplication a(argc, argv);qDebug() << "hello world!";HINSTANCE m_hModule = LoadLibrary(DLLPATH);//加载dllif (m_hModule == NULL){printf("LoadLibrary %s failed,err %d\n", DLLPATH, GetLastError());goto END;}Add_Func add;add = (Add_Func)GetProcAddress(m_hModule,"Add");//获取方法地址,后边调用double c = add(10, 11);printf("Add_Func: %lf \n", c);TestDn test;test = (TestDn)GetProcAddress(m_hModule, "TestDn");PKI_DATA cert;PKI_DATA dn;cert.size = 4;cert.value = (char*)"test";int res = test(cert, &dn);printf("TestDn:%s\n", dn.value);END:if (m_hModule != NULL){FreeLibrary(m_hModule);m_hModule = NULL;printf("FreeLibrary\n");}return a.exec();}

关于怎么把char*转码到正确的编码格式下的C#string,

char* cchar=data IntPtr value = new IntPtr(cchar);var s = Marshal.PtrToStringAnsi(value);

可以用上边的方法,看fcl的源码可以看到上边的方法实质是把char*强转为sbyte*,然后直接new string,这样可以避免C++和C#的byte差异

marshal还有touni的方法,只转unicode的方法,实质是直接用char* new string,

char*和sbyte*两种初始化参数string类型都接受。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。