本文主要是介绍C#调用C++写的dll,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
C#调用C++的DLL搜集整理的所有数据类型转换方式(转)
2011-04-01 13:49:13| 分类: C++,C#|举报|字号 订阅
//C++中的DLL函数原型为
//extern "C" __declspec(dllexport) bool 方法名一(const char* 变量名1, unsigned char* 变量名2)
//extern "C" __declspec(dllexport) bool 方法名二(const unsigned char* 变量名1, char* 变量名2)
//C#调用C++的DLL搜集整理的所有数据类型转换方式,可能会有重复或者多种方案,自己多测试
//c++:HANDLE(void *) ---- c#:System.IntPtr
//c++:Byte(unsigned char) ---- c#:System.Byte
//c++:SHORT(short) ---- c#:System.Int16
//c++:WORD(unsigned short) ---- c#:System.UInt16
//c++:INT(int) ---- c#:System.Int16
//c++:INT(int) ---- c#:System.Int32
//c++:UINT(unsigned int) ---- c#:System.UInt16
//c++:UINT(unsigned int) ---- c#:System.UInt32
//c++:LONG(long) ---- c#:System.Int32
//c++:ULONG(unsigned long) ---- c#:System.UInt32
//c++:DWORD(unsigned long) ---- c#:System.UInt32
//c++:DECIMAL ---- c#:System.Decimal
//c++:BOOL(long) ---- c#:System.Boolean
//c++:CHAR(char) ---- c#:System.Char
//c++:LPSTR(char *) ---- c#:System.String
//c++:LPWSTR(wchar_t *) ---- c#:System.String
//c++:LPCSTR(const char *) ---- c#:System.String
//c++:LPCWSTR(const wchar_t *) ---- c#:System.String
//c++:PCAHR(char *) ---- c#:System.String
//c++:BSTR ---- c#:System.String
//c++:FLOAT(float) ---- c#:System.Single
//c++:DOUBLE(double) ---- c#:System.Double
//c++:VARIANT ---- c#:System.Object
//c++:PBYTE(byte *) ---- c#:System.Byte[]
//c++:BSTR ---- c#:StringBuilder
//c++:LPCTSTR ---- c#:StringBuilder
//c++:LPCTSTR ---- c#:string
//c++:LPTSTR ---- c#:[MarshalAs(UnmanagedType.LPTStr)] string
//c++:LPTSTR 输出变量名 ---- c#:StringBuilder 输出变量名
//c++:LPCWSTR ---- c#:IntPtr
//c++:BOOL ---- c#:bool
//c++:HMODULE ---- c#:IntPtr
//c++:HINSTANCE ---- c#:IntPtr
//c++:结构体 ---- c#:public struct 结构体{};
//c++:结构体 **变量名 ---- c#:out 变量名 //C#中提前申明一个结构体实例化后的变量名
//c++:结构体 &变量名 ---- c#:ref 结构体 变量名
//c++:WORD ---- c#:ushort
//c++:DWORD ---- c#:uint
//c++:DWORD ---- c#:int
//c++:UCHAR ---- c#:int
//c++:UCHAR ---- c#:byte
//c++:UCHAR* ---- c#:string
//c++:UCHAR* ---- c#:IntPtr
//c++:GUID ---- c#:Guid
//c++:Handle ---- c#:IntPtr
//c++:HWND ---- c#:IntPtr
//c++:DWORD ---- c#:int
//c++:COLORREF ---- c#:uint
//c++:unsigned char ---- c#:byte
//c++:unsigned char * ---- c#:ref byte
//c++:unsigned char * ---- c#:[MarshalAs(UnmanagedType.LPArray)] byte[]
//c++:unsigned char * ---- c#:[MarshalAs(UnmanagedType.LPArray)] Intptr
//c++:unsigned char & ---- c#:ref byte
//c++:unsigned char 变量名 ---- c#:byte 变量名
//c++:unsigned short 变量名 ---- c#:ushort 变量名
//c++:unsigned int 变量名 ---- c#:uint 变量名
//c++:unsigned long 变量名 ---- c#:ulong 变量名
//c++:char 变量名 ---- c#:byte 变量名 //C++中一个字符用一个字节表示,C#中一个字符用两个字节表示
//c++:char 数组名[数组大小] ---- c#:MarshalAs(UnmanagedType.ByValTStr, SizeConst = 数组大小)] public string 数组名; ushort
//c++:char * ---- c#:string //传入参数
//c++:char * ---- c#:StringBuilder//传出参数
//c++:char *变量名 ---- c#:ref string 变量名
//c++:char *输入变量名 ---- c#:string 输入变量名
//c++:char *输出变量名 ---- c#:[MarshalAs(UnmanagedType.LPStr)] StringBuilder 输出变量名
//c++:char ** ---- c#:string
//c++:char **变量名 ---- c#:ref string 变量名
//c++:const char * ---- c#:string
//c++:char[] ---- c#:string
//c++:char 变量名[数组大小] ---- c#:[MarshalAs(UnmanagedType.ByValTStr,SizeConst=数组大小)] public string 变量名;
//c++:struct 结构体名 *变量名 ---- c#:ref 结构体名 变量名
//c++:委托 变量名 ---- c#:委托 变量名
//c++:int ---- c#:int
//c++:int ---- c#:ref int
//c++:int & ---- c#:ref int
//c++:int * ---- c#:ref int //C#中调用前需定义int 变量名 = 0;
//c++:*int ---- c#:IntPtr
//c++:int32 PIPTR * ---- c#:int32[]
//c++:float PIPTR * ---- c#:float[]
//c++:double** 数组名 ---- c#:ref double 数组名
//c++:double*[] 数组名 ---- c#:ref double 数组名
//c++:long ---- c#:int
//c++:ulong ---- c#:int
//c++:UINT8 * ---- c#:ref byte //C#中调用前需定义byte 变量名 = new byte();
//c++:handle ---- c#:IntPtr
//c++:hwnd ---- c#:IntPtr
//c++:void * ---- c#:IntPtr
//c++:void * user_obj_param ---- c#:IntPtr user_obj_param
//c++:void * 对象名称 ---- c#:([MarshalAs(UnmanagedType.AsAny)]Object 对象名称
//c++:char, INT8, SBYTE, CHAR ---- c#:System.SByte
//c++:short, short int, INT16, SHORT ---- c#:System.Int16
//c++:int, long, long int, INT32, LONG32, BOOL , INT ---- c#:System.Int32
//c++:__int64, INT64, LONGLONG ---- c#:System.Int64
//c++:unsigned char, UINT8, UCHAR , BYTE ---- c#:System.Byte
//c++:unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR , __wchar_t ---- c#:System.UInt16
//c++:unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT ---- c#:System.UInt32
//c++:unsigned __int64, UINT64, DWORDLONG, ULONGLONG ---- c#:System.UInt64
//c++:float, FLOAT ---- c#:System.Single
//c++:double, long double, DOUBLE ---- c#:System.Double
//Win32 Types ---- CLR Type
//Struct需要在C#里重新定义一个Struct
//CallBack回调函数需要封装在一个委托里,delegate static extern int FunCallBack(string str);
//unsigned char** ppImage替换成IntPtr ppImage
//int& nWidth替换成ref int nWidth
//int*, int&, 则都可用 ref int 对应
//双针指类型参数,可以用 ref IntPtr
//函数指针使用c++: typedef double (*fun_type1)(double); 对应 c#:public delegate double fun_type1(double);
//char* 的操作c++: char*; 对应 c#:StringBuilder;
//c#中使用指针:在需要使用指针的地方 加 unsafe
//unsigned char对应public byte
/*
* typedef void (*CALLBACKFUN1W)(wchar_t*, void* pArg);
* typedef void (*CALLBACKFUN1A)(char*, void* pArg);
* bool BIOPRINT_SENSOR_API dllFun1(CALLBACKFUN1 pCallbackFun1, void* pArg);
* 调用方式为
* [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
* public delegate void CallbackFunc1([MarshalAs(UnmanagedType.LPWStr)] StringBuilder strName, IntPtr pArg);
*
*
*/
方法有不少,这里记录其中的一个方法。编译、调用通过了的。期间遇到的问题是C#调用时传递string类型的参数和返回值会报错。TargetInvocationException异常,值不在范围内,内存不可访问等等。
解决方法是;在c++的DLL中将string类型的参数返回值改为LPTSTR类型。在C#程序中使用StringBuilder类型来传入参数和接受返回值。
下面是代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; //1. 打开项目“Tzb”,打开类视图,右击“Tzb”,选择“添加”-->“类”,类名设置为“dld”, //即dynamic loading dll 的每个单词的开头字母。 //2. 添加所需的命名空间及声明参数传递方式枚举: using System.Runtime.InteropServices; // 用 DllImport 需用此 命名空间 using System.Reflection; // 使用 Assembly 类需用此 命名空间 using System.Reflection.Emit; // 使用 ILGenerator 需用此 命名空间 namespace WpfApplication1 { //在“public class dld”上面添加如下代码声明参数传递方式枚举: /// /// 参数传递方式枚举 ,ByValue 表示值传递 ,ByRef 表示址传递 /// public enum ModePass { ByValue = 0x0001, ByRef = 0x0002 } public class DLD { //3. 声明LoadLibrary、GetProcAddress、FreeLibrary及私有变量hModule和farProc: /// /// 原型是 :HMODULE LoadLibrary(LPCTSTR lpFileName); /// /// < param name="lpFileName" / >DLL 文件名 /// 函数库模块的句柄 [DllImport("kernel32.dll")] static extern IntPtr LoadLibrary(string lpFileName); /// /// 原型是 : FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName); /// /// < param name="hModule" / > 包含需调用函数的函数库模块的句柄 /// < param name="lpProcName" / > 调用函数的名称 /// 函数指针 [DllImport("kernel32.dll")] static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); /// /// 原型是 : BOOL FreeLibrary(HMODULE hModule); /// /// < param name="hModule" / > 需释放的函数库模块的句柄 /// 是否已释放指定的 Dll [DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)] static extern bool FreeLibrary(IntPtr hModule); /// /// Loadlibrary 返回的函数库模块的句柄 /// private IntPtr hModule = IntPtr.Zero; /// /// GetProcAddress 返回的函数指针 /// public IntPtr farProc = IntPtr.Zero; //4. 添加LoadDll方法,并为了调用时方便,重载了这个方法: /// /// 装载 Dll /// /// < param name="lpFileName" / >DLL 文件名 public void LoadDll(string lpFileName) { hModule = LoadLibrary(lpFileName); if (hModule == IntPtr.Zero) throw (new Exception(" 没有找到 :" + lpFileName + ".")); } // 若已有已装载Dll的句柄,可以使用LoadDll方法的第二个版本: public void LoadDll(IntPtr HMODULE) { if (HMODULE == IntPtr.Zero) throw (new Exception(" 所传入的函数库模块的句柄 HMODULE 为空 .")); hModule = HMODULE; } //5. 添加LoadFun方法,并为了调用时方便,也重载了这个方法,方法的具体代码及注释如下: /// /// 获得函数指针 /// /// < param name="lpProcName" / > 调用函数的名称 public void LoadFun(string lpProcName) { // 若函数库模块的句柄为空,则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")); // 取得函数指针 farProc = GetProcAddress(hModule, lpProcName); // 若函数指针,则抛出异常 if (farProc == IntPtr.Zero) throw (new Exception(" 没有找到 : " + lpProcName + " 这个函数的入口点 ")); } /// /// 获得函数指针 /// /// < param name="lpFileName" / > 包含需调用函数的 DLL 文件名 /// < param name="lpProcName" / > 调用函数的名称 public void LoadFun(string lpFileName, string lpProcName) { // 取得函数库模块的句柄 hModule = LoadLibrary(lpFileName); // 若函数库模块的句柄为空,则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 没有找到 :" + lpFileName + ".")); // 取得函数指针 farProc = GetProcAddress(hModule, lpProcName); // 若函数指针,则抛出异常 if (farProc == IntPtr.Zero) throw (new Exception(" 没有找到 :" + lpProcName + " 这个函数的入口点 ")); } //6. 添加UnLoadDll及Invoke方法,Invoke方法也进行了重载: /// /// 卸载 Dll /// public void UnLoadDll() { FreeLibrary(hModule); hModule = IntPtr.Zero; farProc = IntPtr.Zero; } /// /// 调用所设定的函数 /// /// < param name="ObjArray_Parameter" / > 实参 /// < param name="TypeArray_ParameterType" / > 实参类型 /// < param name="ModePassArray_Parameter" / > 实参传送方式 /// < param name="Type_Return" / > 返回类型 /// 返回所调用函数的 object public object Invoke(object[] ObjArray_Parameter, Type[] TypeArray_ParameterType, ModePass[] ModePassArray_Parameter, Type Type_Return) { // 下面 3 个 if 是进行安全检查 , 若不能通过 , 则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")); if (farProc == IntPtr.Zero) throw (new Exception(" 函数指针为空 , 请确保已进行 LoadFun 操作 !")); if (ObjArray_Parameter.Length != ModePassArray_Parameter.Length) throw (new Exception(" 参数个数及其传递方式的个数不匹配 .")); // 下面是创建 MyAssemblyName 对象并设置其 Name 属性 AssemblyName MyAssemblyName = new AssemblyName(); MyAssemblyName.Name = "InvokeFun"; // 生成单模块配件 AssemblyBuilder MyAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( MyAssemblyName, AssemblyBuilderAccess.Run); ModuleBuilder MyModuleBuilder = MyAssemblyBuilder.DefineDynamicModule("InvokeDll"); // 定义要调用的方法 , 方法名为“ MyFun ”,返回类型是“ Type_Return ” //参数类型是“ TypeArray_ParameterType ” MethodBuilder MyMethodBuilder = MyModuleBuilder.DefineGlobalMethod( "Init", MethodAttributes.Public | MethodAttributes.Static, Type_Return, TypeArray_ParameterType); // 获取一个 ILGenerator ,用于发送所需的 IL ILGenerator IL = MyMethodBuilder.GetILGenerator(); int i; for (i = 0; i < ObjArray_Parameter.Length; i++) {// 用循环将参数依次压入堆栈 switch (ModePassArray_Parameter[i]) { case ModePass.ByValue: IL.Emit(OpCodes.Ldarg, i); break; case ModePass.ByRef: IL.Emit(OpCodes.Ldarga, i); break; default: throw (new Exception(" 第 " + (i + 1).ToString() + " 个参数没有给定正确的传递方式 .")); } } if (IntPtr.Size == 4) {// 判断处理器类型 IL.Emit(OpCodes.Ldc_I4, farProc.ToInt32()); } else if (IntPtr.Size == 8 ) { IL.Emit(OpCodes.Ldc_I8, farProc.ToInt64()); } else { throw new PlatformNotSupportedException(); } IL.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, Type_Return, TypeArray_ParameterType); IL.Emit(OpCodes.Ret); // 返回值 MyModuleBuilder.CreateGlobalFunctions(); // 取得方法信息 MethodInfo MyMethodInfo = MyModuleBuilder.GetMethod("Init"); return MyMethodInfo.Invoke(null, ObjArray_Parameter);// 调用方法,并返回其值 } //Invoke方法的第二个版本,它是调用了第一个版本的: /// /// 调用所设定的函数 /// /// < param name="IntPtr_Function" / > 函数指针 /// < param name="ObjArray_Parameter" / > 实参 /// < param name="TypeArray_ParameterType" / > 实参类型 /// < param name="ModePassArray_Parameter" / > 实参传送方式 /// < param name="Type_Return" / > 返回类型 /// 返回所调用函数的 object public object Invoke(IntPtr IntPtr_Function, object[] ObjArray_Parameter, Type[] TypeArray_ParameterType, ModePass[] ModePassArray_Parameter, Type Type_Return) { // 下面 2 个 if 是进行安全检查 , 若不能通过 , 则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")); if (IntPtr_Function == IntPtr.Zero) throw (new Exception(" 函数指针 IntPtr_Function 为空 !")); farProc = IntPtr_Function; return Invoke(ObjArray_Parameter, TypeArray_ParameterType, ModePassArray_Parameter, Type_Return); } } }
/*******调用方法******/ private void button1_Click(object sender, RoutedEventArgs e) { DLD newDLL = new DLD(); newDLL.LoadFun("E:\\workspaces\\WpfApplication1\\Debug\\DLL.dll", "Init"); StringBuilder MyStringBuilder = new StringBuilder("Hello World!"); object[] obj = new object[] { MyStringBuilder }; Type[] ty = new Type[] { typeof(StringBuilder) }; ModePass[] mode = new ModePass[] { ModePass.ByValue }; Type Type_Return = typeof(StringBuilder); StringBuilder j = (StringBuilder)newDLL.Invoke(obj, ty, mode, Type_Return); } /********c++DLL中的函数*******/ extern "C" __declspec(dllexport) LPTSTR Init(LPTSTR a); LPTSTR Init(LPTSTR a) { strcat((char *)a, "added"); return a; }
c#如何调用c++的类
(2009-07-14 03:30:16)
转载▼
将c++类生成一个 .dll 文件
//-------------头文件
#pragma once
class testclass
{
public:
testclass(void);
~testclass(void);
public:
WCHAR* GetName(void);
};
//-------------CPP
#include "StdAfx.h"
#include "testclass.h"
testclass::testclass(void)
{
}
testclass::~testclass(void)
{
}
WCHAR* testclass::GetName(void)
{
return NULL;
}
//--------------接口头文件
#pragma once
#include "testclass.h"
class Insertface
{
public:
Insertface(void);
~Insertface(void);
public:
void* CreateTextClass(void);
WCHAR* GetName(void *textClass);
BOOL DeleteTextClass(void *textClass);
};
//----------------------CPP
#include "StdAfx.h"
#include "Insertface.h"
Insertface::Insertface(void)
{
}
Insertface::~Insertface(void)
{
}
extern "C" _declspec(dllexport) void* Insertface::CreateTextClass(void)
{
testclass *p = new testclass();
return (void *)p;
}
extern "C" _declspec(dllexport) WCHAR* Insertface::GetName(void *textClass)
{
return ((testclass *)textClass)->GetName();
}
extern "C" _declspec(dllexport) BOOL Insertface::DeleteTextClass(void *textClass)
{
delete (testclass *)textClass;
return TRUE;
}
C++的优势在于高效灵活,C#的优势在于简单易用,两者结合起来对项目开发来说是件好事,而且C++不容易反编译,也保障了代码的安全性,如果一些核心算法使用C#编写,那么保密就是一个问题。
C++生成的DLL一般只提供函数接口,不能直接用C#调用C++写的类,这样非常不方便,于是经过半天的反复测试,终于确定了一套可行的方案,下面就是整个流程的一个范例。
(1)编写C++类Mask,类前面的extern "C" class _declspec(dllexport)这么一串修饰符是必备的,不然无法导出类。
[cpp] view plaincopyprint?
- //Mask类头文件mask.h
- #pragma once
-
- extern "C" class _declspec(dllexport) Mask
- {
- public:
- Mask(char* inputFile,int* maskValue,int maskLength);
- virtual void Run(void);
- ~Mask(void);
-
- private:
- char* _inputFile;
- int* _maskValue;
- int _maskLength;
- };
(2)Mask类内部实现,这里使用char*和int*这两个有代表性的参数,C#向C++进行参数的传递是很难弄的一步。
[cpp] view plaincopyprint?
- //Mask类实现文件mask.cpp
- Mask::Mask(char* inputFile ,int* maskValue,int maskLength)
- {
- _inputFile=new char[strlen(inputFile)+1];
- strcpy(_inputFile,inputFile);
-
- _maskValue=new int[maskLength];
- _maskLength=maskLength;
- for(int i=0;i<masklength;i++) < span="">
- _maskValue[i]=maskValue[i];
- }
-
- void Mask::Run(void)
- {
- }
-
- Mask::~Mask(void)
- {
- if (_inputFile)
- {
- delete [] _inputFile;
- _inputFile=NULL;
- }
- if (_maskValue)
- {
- delete [] _maskValue;
- _maskValue=NULL;
- }
- }
可以设置生成类型为DLL,然后将debug目录下的dll文件和lib文件找到,留待下一步使用。
(3)编写C++.NET类MaskCLR,C++.NET我一直认为就是个摆设,没想到也能派上用场,难得啊,也不完全是个废物。MaskCLR可以调用Mask类,注意函数的参数已经变成String ^和int*,现在是.NET环境,使用指针没那么方便了。
[cpp] view plaincopyprint?
- //MaskCLR类头文件,用来包装Mask类MaskCLR.h
- #pragma once
- #include " mask.h" //这个就是上面的Mask头文件
-
- public ref class MaskCLR
- {
- private:
- char* _inputFile;
- int* _maskValue;
- int _maskLength;
-
- public:
- MaskCLR(String ^ inputFile,int* maskValue,int maskLength);
- virtual void Run(void) override;
- };
(4)MaskCLR类内部实现,首先要处理参数类型问题,将String ^类型转为char*。在MaskCLR:: Run函数内部调用Mask类,Mask是DLL导出的类。
[cpp] view plaincopyprint?
- // MaskCLR类内部实现MaskCLR.cpp
- #include "MaskCLR.h"
-
- MaskCLR::MaskCLR(String ^ inputFile,int* maskValue,int maskLength)
- {
- _inputFile=GlobeFunction::StringToChar(inputFile);
- _maskValue=maskValue;
- _maskLength=maskLength;
- }
-
- void MaskCLR:: Run(void)
- {
- Mask mask(_inputFile, _maskValue,_maskLength);
- mask.Run();
- }
(5)将String ^类型转为char*,可以利用StringToHGlobalAnsi这个.NET自带的函数。
[csharp] view plaincopyprint?
- char* GlobeFunction::StringToChar(String ^str)
- {
- return (char*)(Marshal::StringToHGlobalAnsi(str)).ToPointer();
- }
(6)最终生成了两个DLL文件,一个是原始C++编写的,另一个是托管C++编写的,托管dll只是个外壳,最终还是调用原始dll,因此这两个dll要放在一起。下面终于进入C#环境了,首先将托管dll添加引用到C#工程中,C++.NET和C#可以直接互用。
(7)由于有个int*类型参数,在C#里指针属于不安全代码,因此使用unsafe关键字将涉及到指针的代码包括起来,在工程属性里设置允许使用不安全代码。定义int指针需要使用stackalloc关键字,创建一个int数组,对数组赋值后,将指针传递给类函数。
[csharp] view plaincopyprint?
- //c#调用托管dll中的MaskCLR类form1.cs
- unsafe
- {
- int* value = stackalloc int[1];
- value[0] = 0;
- MaskCLR mask = new MaskCLR("D:\\临时\\mask8.tif", value, 1);
- mask.Run();
- }
(8)至此大功告成,可以用C#很方便的调用C++写的类了,如果觉得有用,不要忘了给我留言啊!!O(∩_∩)O~
这篇关于C#调用C++写的dll的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!