Table of Contents
1.Types of DLLs
C++生的DLL,按是否托管维度,可以分为两大类:
1、C++创建的非托管dll库:需要用静态方法调用;非托管模式从功能上来说,只支持函数调用,直接调用C++类库中的公共方法,在被导出的函数前面一定要添加额extern “C来指明导出函数的时候使用C语言方式编译和链接的,这样保证函数定义的名字相同,否则如果默认按C++方式导出,那个函数名字就会变得乱七八糟,别人的程序就无法找到入口点了。
2、C++ 使用CLR创建的托管dll库,生成托管C++dll库
1.1 非托管C++dll库
非托管C++dll库往下细分,vs C++ 可以生成几种形式的类库,不同的类库对应不同的调用方式。
1.vs c++COM方式标准的DLL类库(unmanaged):dll只包含了函数的集合和函数入口点。对外暴露多个函数,供外部程序调用。
使用api标准,可被任何程序调用。包含了函数所在的DLL文件和文件中函数位置的信息(入口),代码由运行时加载在进程空间中的DLL提供,称为动态链接库dynamic link library.dll是运行时用到的。
2.vs c++ 将一个class写成DLL(unmanaged),导出整个类:导出的是整个class。
3.MFC的DLL类库(unmanaged):
1.2 托管C++dll库
4.CLR DLL类库(managed):
2.非托管C++dll库的另外一种存在形式
5.标准的LIB类库:
静态类库,可被任何程序调用。包含函数代码本身,在编译时直接将代码加入程序当中,称为静态链接库static link library.lib是编译时用到的.
Create a C++ DLL (Unmanaged)
Build (Make the DLLs)
Create a C# Project (Managed)
Copy the DLLs to your C# Project
Using the DLL
1.新建一个产生dll的工程-只生成dll文件
DllMain介绍
DllMain:跟exe有个main或者WinMain入口函数一样,DLL也有一个入口函数,就是DllMain.虽然DLL不能自己运行,可是Windows在加载DLL的时候,需要一个入口函数,就如同EXE的main一样,否则系统无法引用DLL。所以根据编写规范,Windows必须查找并执行DLL里的一个函数DllMain作为加载DLL的依据,这个函数不作为API导出,而是内部函数。
2.新建一个调用dll的工程
Under Configuration Properties, select General, and in the dropdown next to Configuration Type, select Dynamic Library (.dll). Select Apply, and then select OK.
This is a complete step, you can refer to it.
新建src
将项目移动到src
删除 .vs
将方案另存为 ->根目录
分别添加头文件和cpp文件,不要直接添加类。
Tutorial: Debug C# and C++ in the same debugging session(c#调式 标准的DLL)
由于C#编绎出来的DLL不是计算机所能直接识别的二进制指令码,需要CLR进行再解释,说到这,我想有些朋友应该知道C#项目需要引用C++编写的DLL时,可以直接引用DLLMPORT来实现调用。而反向的话,C++项目却不能简单靠引用来使用C#编写的DLL。由于C++项目默认配置是没有公共语言运行支持的,因此我们需要更改一些配置,来实现C++项目对C#编写DLL的调用。
C#调用C++的dll总归可以有两种方法:
很多时候在项目中需要通过C++调用C#的dll,或者反过来调用。首先明白一个前提:C#是托管型代码。C++是非托管型代码。
托管型代码的对象在托管堆上分配内存,创建的对象由虚拟机托管。(C# )
非托管型代码对象有实际的内存地址,创建的对象必须自己来管理和释放。(C++)
1、非托管C++创建的dll库,需要用静态方法调用;
2、直接使用CLR,生成托管C++dll库。
C++、C#交互有三种方式:
1 COM,C# app with C++ libs(C#主 C++从), 在C++这边声明参数的时候,BYTE就是C#的byte,BSTR就是C#的string,想做出参就加*,BYTE*就是C#的ref byte,BSTR* 就是 ref string,但是如果要传byte[]就麻烦了,千万别琢磨着转成string用BSTR传,怎么都不对,只有用BitConverter两个字节两个字节的转在C++这边才能保持正确,但是太慢了,转几M的东西要几十分钟,所以果断VARIANT,在C#端会翻译成object,用C#Marshal分配一段unmanaged的内存,用IntPtr直接传过来就好了,C++端会看到参数类型是VT_INT,直接用VARIANT里INT就是地址,又快又方便。
write COM visible interface and classes in C++ and expose them through COM
import a type library in C#
2 P-INVOKE,参数比较简单,直接上网搜一下C++和C#变量对应表就好了,很多,这里和COM有一点不同,比如BYTE*会直接翻译为IntPtr,方便很多。(对于是用COM还是P-INVOKE,见仁见智吧)。
c#
using System;
using System.Runtime.InteropServices;
public class Program
{
// Import user32.dll (containing the function we need) and define
// the method corresponding to the native function.
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);
public static void Main(string[] args)
{
// Invoke the function as a regular managed method.
MessageBox(IntPtr.Zero, "Command-line message box", "Attention!", 0);
}
}
3 C++/CLI,没什么好说的了,直接Marshal转换各种类型就OK了,唯一麻烦的就是String转换的时候只能用char* 不能用const char*
create a c++ CLR Class Library (.NET Core) project
Create a simple managed (.NET Core) project
Add References c++ CLR Class Library (.NET Core) project
this simple managed (.NET Core) project app to call the DLL
C++/CLI projects targeting .NET Core 3.x
NET Programming with C++/CLI (Visual C++)
c++和c#数据类型转换(C++与C#的基本类型对照)
//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++: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
//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
C++生成dll示举例
https://mariusbancila.ro/blog/2019/12/18/cpp-cli-projects-targeting-net-core-3-x/
Create C/C++ DLLs in Visual Studio