C++之动态链接库

[TOC]

动态链接库(Dynamic Link Library)DLL文件与EXE文件一样也是可执行文件,但是DLL也被称之为库,因为里面封装了各种类啊,函数啊之类的东西,就像是一个库一样,存储着很多东西,主要是用来调用的。调用方式主要分为两种:隐式(通过lib文件与头文件) 与 显式(只通过DLL文件)。

C++的入口函数

dll的入口函数,简而言之,就是只在dll被load的时候调用一次,之后都不会被调用,也无法在dll外部被调用。
并且同一个dll,被多个进程load多次,内存中也只会有此动态链接库的一个副本,也只会加载一次入口函数。(由动态链接库的属性决定)

C++的导出函数

  • C++的代码示例
1
2
3
//dll.h
extern "C" __declspec(dllexport) int __stdcall Add(int a, int b);
extern "C" __declspec(dllexport) int __stdcall Sum(int* a);
1
2
3
4
5
6
7
8
9
10
//dll.cpp
#include "pch.h" //预编译头
#include "dll.h" //头文件

__declspec(dllexport) int __stdcall Add(int a, int b) {
return a + b;
}
__declspec(dllexport) int __stdcall Sum(int* a){
return a[0] + a[1] + a[2] + a[3];
}
  • __stdcall是函数调用约定的一种,常见的函数调用约定:stdcall cdecl fastcall thiscall naked call,函数调用约定主要约束了两件事:
  1. 参数传递顺序
  2. 调用堆栈由谁(调用函数或被调用函数)清理
  • __stdcall表示
  1. 参数从右向左压入堆栈
  2. 函数被调用者修改堆栈
  3. 函数名(在编译器这个层次)自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸
    在win32应用程序里,宏APIENTRY,WINAPI,都表示_stdcall,非常常见。
  • extern “C”

    的主要作用就是为了能够正确实现C代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言(而不是C)的方式进行编译。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。此处为的是直接将函数名直接导出,在C#调用时不用在单独指明入口点。

    这个功能十分有用处,因为在C出现以前,很多代码都是C语言写的,而且很底层的库也是C语言写的,为了更好的支持原来的C代码和已经写好的C语言库,需要在C中尽可能的支持C,而extern "C"就是其中的一个策略。

    这个功能主要用在下面的情况:

  1. C++代码调用C语言代码
  2. 在C++的头文件中使用
  3. 在多个人协同开发时,可能有的人比较擅长C语言,而有的人擅长C++,这样的情况下也会有用到
  • __declspec(dllexport)与__declspec(dllimport)

    他们都是DLL内的关键字,即导出与导入。他们是将DLL内部的类与函数以及数据导出与导入时使用的。

    dllexport是在这些类、函数以及数据的申明的时候使用。用他表明这些东西可以被外部函数使用,即(dllexport)是把 DLL中的相关代码(类,函数,数据)暴露出来为其他应用程序使用。使用了(dllexport)关键字,相当于声明了紧接在(dllexport)关键字后面的相关内容是可以为其他程序使用的。

    dllimport是在外部程序需要使用DLL内相关内容时使用的关键字。当一个外部程序要使用DLL 内部代码(类,函数,全局变量)时,只需要在程序内部使用(dllimport)关键字声明需要使用的代码就可以了,即(dllimport)关键字是在外部程序需要使用DLL内部相关内容的时候才使用。(dllimport)作用是把DLL中的相关代码插入到应用程序中。

    _declspec(dllexport)与_declspec(dllimport)是相互呼应,只有在DLL内部用dllexport作了声明,才能在外部函数中用dllimport导入相关代码。

  • 通过DLL Export Viewer 查看生成的导出函数如下所示

    export_viewer

C#中通过PInvoke调用DLL

通过C#的互操作类可以导入外部函数以供程序调用,调用方式如下所示,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
using System.Runtime.InteropServices;

namespace PInvokeCSharp
{
public static class PInvoke
{
[DllImport("PInvokeDLL.dll")]
public static extern int Add(int a, int b);
[DllImport("PInvokeDLL.dll")]
public static extern int Sum(int[] a);
[DllImport("PInvokeDLL.dll")]
public static extern int Sum(int[,] a);
[DllImport("PInvokeDLL.dll")]
public static extern int Sum(IntPtr a);
}
}

C++与C#中的类型匹配

C++ C#
BOOL System.Int32
BOOLEAN System.Int32
BYTE System.UInt16
CHAR System.Int16
COLORREF System.UInt32
- -
DWORD System.UInt32
DWORD32 System.UInt32
DWORD64 System.UInt64
FLOAT System.Float
HACCEL System.IntPtr
- -
HANDLE System.IntPtr
HBITMAP System.IntPtr
HBRUSH System.IntPtr
HCONV System.IntPtr
HCONVLIST System.IntPtr
- -
HCURSOR System.IntPtr
HDC System.IntPtr
HDDEDATA System.IntPtr
HDESK System.IntPtr
HDROP System.IntPtr
- -
HDWP System.IntPtr
HENHMETAFILE System.IntPtr
HFILE System.IntPtr
HFONT System.IntPtr
HGDIOBJ System.IntPtr
- -
HGLOBAL System.IntPtr
HHOOK System.IntPtr
HICON System.IntPtr
HIMAGELIST System.IntPtr
HIMC System.IntPtr
- -
HINSTANCE System.IntPtr
HKEY System.IntPtr
HLOCAL System.IntPtr
HMENU System.IntPtr
HMETAFILE System.IntPtr
- -
HMODULE System.IntPtr
HMONITOR System.IntPtr
HPALETTE System.IntPtr
HPEN System.IntPtr
HRGN System.IntPtr
- -
HRSRC System.IntPtr
HSZ System.IntPtr
HWINSTA System.IntPtr
HWND System.IntPtr
INT System.Int32
- -
INT32 System.Int32
INT64 System.Int64
LONG System.Int32
LONG32 System.Int32
LONG64 System.Int64
- -
LONGLONG System.Int64
LPARAM System.IntPtr
LPBOOL System.Int16[]
LPBYTE System.UInt16[]
LPCOLORREF System.UInt32[]
- -
LPCSTR System.String
LPCTSTR System.String
LPCVOID System.UInt32
LPCWSTR System.String
LPDWORD System.UInt32[]
- -
LPHANDLE System.UInt32
LPINT System.Int32[]
LPLONG System.Int32[]
LPSTR System.String
LPTSTR System.String
- -
LPVOID System.UInt32
LPWORD System.Int32[]
LPWSTR System.String
LRESULT System.IntPtr
PBOOL System.Int16[]
- -
PBOOLEAN System.Int16[]
PBYTE System.UInt16[]
PCHAR System.Char[]
PCSTR System.String
PCTSTR System.String
- -
PCWCH System.UInt32
PCWSTR System.UInt32
PDWORD System.Int32[]
PFLOAT System.Float[]
PHANDLE System.UInt32
- -
PHKEY System.UInt32
PINT System.Int32[]
PLCID System.UInt32
PLONG System.Int32[]
PLUID System.UInt32
- -
PSHORT System.Int16[]
PSTR System.String
PTBYTE System.Char[]
PTCHAR System.Char[]
PTSTR System.String
- -
PUCHAR System.Char[]
PUINT System.UInt32[]
PULONG System.UInt32[]
PUSHORT System.UInt16[]
PVOID System.UInt32
- -
PWCHAR System.Char[]
PWORD System.Int16[]
PWSTR System.String
REGSAM System.UInt32
SC_HANDLE System.IntPtr
- -
SC_LOCK System.IntPtr
SHORT System.Int16
SIZE_T System.UInt32
SSIZE_ System.UInt32
TBYTE System.Char
- -
TCHAR System.Char
UCHAR System.Byte
UINT System.UInt32
UINT32 System.UInt32
UINT64 System.UInt64
- -
ULONG System.UInt32
ULONG32 System.UInt32
ULONG64 System.UInt64
ULONGLONG System.UInt64
USHORT System.UInt16
- -
WORD System.UInt16
WPARAM System.IntPtr