VS 2015 DLL的创建、静态调用和动态调用

DLL的创建

创建步骤

文件 -> 新建 -> 项目 -> “新建项目”对话框 -> “已安装” -> 模板 -> 其他语言 -> Vistual C++ -> Win32 控制台应用程序。

VS 2015 DLL的创建、静态调用和动态调用_第1张图片

在”Win32 应用程序向导”对话框中

  • “控制台程序类型”选择”DLL(D)”
  • “附加选项”勾选”导出符号(X)”

VS 2015 DLL的创建、静态调用和动态调用_第2张图片

VS 2015 DLL的创建、静态调用和动态调用_第3张图片

导入(导出)标记的宏定义

下列 ifdef 块是创建使从 DLL 导出更简单的宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的DLLDEMO_EXPORTS符号编译的。在使用此 DLL 的任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将DLLDEMO_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的符号视为是被导出的。

#ifdef DLLDEMO_EXPORTS
#define DLLDEMO_API __declspec(dllexport)
#else
#define DLLDEMO_API __declspec(dllimport)
#endif

导出符号标识的宏定义位于:解决方案资源管理器 -> 项目属性 -> “项目属性页”对话框 -> “配置属性” -> C/C++ -> 预处理器 -> 预处理器定义

VS 2015 DLL的创建、静态调用和动态调用_第4张图片

修改DLL项目

DllDemo.h

#ifdef DLLDEMO_EXPORTS
#define DLLDEMO_API __declspec(dllexport)
#else
#define DLLDEMO_API __declspec(dllimport)
#endif

// 此类是从 DllDemo.dll 导出的
class DLLDEMO_API CDllDemo {
public:
    CDllDemo(void);
    // TODO:  在此添加您的方法。
};

extern DLLDEMO_API int nDllDemo;
extern "C" extern DLLDEMO_API int nExternCDllDemo;

DLLDEMO_API int fnDllDemo(void);
extern "C" DLLDEMO_API int fnExternCDllDemo(void);

char DLLDEMO_API fnDefault(char, int, float);
char DLLDEMO_API __stdcall fnstdcall(char, int, float);
char DLLDEMO_API __cdecl fncdecl(char, int, float);
char DLLDEMO_API __fastcall fnfastcall(char, int, float);

dllmain.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include 
#include 

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    /*
    获取模块文件名,如果是dll的话,获取的是调用它的可执行文件路径
    WINBASEAPI 
    _Success_(return != 0)
    _Ret_range_(1, nSize) 
    DWORD WINAPI GetModuleFileNameW(
        _In_opt_ HMODULE hModule,
        _Out_writes_to_(nSize, ((return < nSize) ? (return + 1) : nSize)) LPWSTR lpFilename,
        _In_ DWORD nSize
    );
    */
    TCHAR lpFilename[MAX_PATH];
    DWORD ret = GetModuleFileName(NULL, lpFilename, MAX_PATH);
    if (ret) {
        _tprintf(_T("GetModuleFileName -> lpFilename=%s\n"), lpFilename);
    }
    else {
        printf("GetModuleFileName -> fail(%ld)", GetLastError());
    }

    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        printf("DLL_PROCESS_ATTACH\n");
        break;
    case DLL_THREAD_ATTACH:
        printf("DLL_THREAD_ATTACH\n");
        break;
    case DLL_THREAD_DETACH:
        printf("DLL_THREAD_DETACH\n");
        break;
    case DLL_PROCESS_DETACH:
        printf("DLL_PROCESS_DETACH\n");
        break;
    }
    return TRUE;
}

DllDemo.cpp

// DllDemo.cpp : 定义 DLL 应用程序的导出函数。

#include "stdafx.h"
#include "DllDemo.h"

// 这是导出变量的一个示例
DLLDEMO_API int nDllDemo = 1;
DLLDEMO_API int nExternCDllDemo = 2;

// 这是导出函数的一个示例。
DLLDEMO_API int fnDllDemo(void)
{
    return 42;
}
DLLDEMO_API int fnExternCDllDemo(void)
{
    return 142;
}

char DLLDEMO_API fnDefault(char, int, float) 
{
    return 'a';
}
char DLLDEMO_API __stdcall fnstdcall(char, int, float)
{
    return 'b';
}
char DLLDEMO_API __cdecl fncdecl(char, int, float)
{
    return 'c';
}
char DLLDEMO_API __fastcall fnfastcall(char, int, float)
{
    return 'd';
}

// 这是已导出类的构造函数。
// 有关类定义的信息,请参阅 DllDemo.h
CDllDemo::CDllDemo()
{
    return;
}

生成DLL有以下几种方案

  • 解决方案资源管理器 -> “DLL项目”右键 -> 生成(U)
  • 菜单栏 -> 生产DLL(U)(Shift + F6)

然后会在项目的根目录的Debug或Release文件夹下生成相应的文件。

dumpbin查看dll(或lib)的导出符号

dumpbin.exe 位于 :C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin

VS 2015 DLL的创建、静态调用和动态调用_第5张图片

查看编译后的DLL函数名用到的只是”EXPORTS”参数而已。函数名为name列等号的左侧。

可以看到extern “C”修饰的函数名与原函数名一致。原因可以参看《函数重载与Extern “C”》一文: http://blog.csdn.net/chy555chy/article/details/53015808。另外说明一点,使用extern “C”修饰的C++函数不能使用C++的重载特性,因为导出的函数符号只是原函数名,编译器没有将参数类型信息加到导出的函数名中,因此无法区分重载函数。

VS 2015 DLL的创建、静态调用和动态调用_第6张图片

DLL的调用

导入DLL

(1)当”DLL项目”和”可执行项目”属于同一个解决方案时。在”DLL项目”右键”生成”可以在项目的Debug目录下生成相应的DLL和LIB。然后在”可执行项目”右键”设为启动项目”,然后点击”本地Windows调试器”即可。

  • 静态调用
    直接运行会报无法打开LIB(不加后缀默认是LIB)或DLL
    这里写图片描述
    这里写图片描述
    解决方案:在”可执行项目”右键 -> 添加(D) -> 引用(R)…
    VS 2015 DLL的创建、静态调用和动态调用_第7张图片
    可以看到引用处多了相应的DLL文件
    VS 2015 DLL的创建、静态调用和动态调用_第8张图片

  • 动态调用
    如果是动态调用,此时可正常运行
    VS 2015 DLL的创建、静态调用和动态调用_第9张图片

(2)如果是外部项目的dll

  • 静态调用(有以下2种方法)

    • #param comment("lib", "path\\*lib")中指定其路径。
    • 解决方案资源管理器 -> “可执行项目”右键”属性” -> “项目属性页”对话框 -> 配置属性 -> “链接器” -> 输入 -> 附加依赖项目 -> 编辑 -> 输入lib文件的完整路径(而不是lib的文件名)
      VS 2015 DLL的创建、静态调用和动态调用_第10张图片
  • 动态调用
    LoadLibrary(_T("path\\*.dll"));中指定其路径。

DLL调用有两种方式,一种是静态调用,另外一种是动态调用

静态调用(同时需要头文件、LIB和DLL文件,缺一不可)

静态调用是一种显式的调用方式,即在编程的时候便知道了被调用的DLL中的接口函数,在编译链接的时候将DLL与工程生成的exe相关联。

#include "stdafx.h"
#include "../DllDemo/DllDemo.h"

//lib后缀可以省略,但不可以改为dll
#pragma comment(lib, "DllDemo.lib")

int main()
{
    printf("%d\n", nDllDemo);
    printf("%d\n", fnDllDemo());
    printf("%d\n", fnExternCDllDemo());
    _tsystem(_T("pause"));
    return 0;
}

动态调用(仅需要DLL,不需要头文件和LIB)

动态调用是一种隐式的调用方式,即程序运行过程中装载DLL,然后获取指定函数名称的接口函数,然后再调用之。

#include "stdafx.h"
#include 

int main() 
{
    //参考 http://blog.csdn.net/g5dsk/article/details/6680698
    HMODULE hModule = LoadLibrary(_T("DllDemo.dll"));// 虽然 MSDN Library 说这里如果指定了路径,要用 backslashes (\),不要用 forward slashes (/),但其实用二者都可以。注:如果用 \,要用 \\。
    if (hModule == NULL || hModule == INVALID_HANDLE_VALUE) {
        return -1;
    }
    /*
    WINBASEAPI FARPROC WINAPI GetProcAddress(
        _In_ HMODULE hModule,
        _In_ LPCSTR  lpProcName //这个是dump /EXPORT *.dll “name”列等号前的值
    );
    返回的是函数或变量的地址,即函数指针或指向变量地址的指针
    */
    typedef int(*TYPE_fnDllDemo) ();
    typedef int(*TYPE_fnExternCDllDemo) ();
    int *nDllDemo = (int *)GetProcAddress(hModule, "?nDllDemo@@3HA");
    TYPE_fnDllDemo fnDllDemo = (TYPE_fnDllDemo)GetProcAddress(hModule, "?fnDllDemo@@YAHXZ");
    int *nExternCDllDemo = (int *)GetProcAddress(hModule, "nExternCDllDemo");
    TYPE_fnExternCDllDemo fnExternCDllDemo = (TYPE_fnExternCDllDemo)GetProcAddress(hModule, "fnExternCDllDemo");
    if(nDllDemo != NULL)
        printf("*nDllDemo = %d\n", *nDllDemo);
    if(fnDllDemo != NULL)
        printf("fnDllDemo() = %d\n", fnDllDemo());
    if (nExternCDllDemo != NULL)
        printf("*nExternCDllDemo = %d\n", *nExternCDllDemo);
    if(fnExternCDllDemo != NULL)
        printf("fnExternCDllDemo() = %d\n", fnExternCDllDemo());
    _tsystem(_T("pause"));
    return 0;
}

运行截图

注意:DLL_PROCESS_DETACH是在关闭命令行的时候被调用,而不是不会调用。这里被system(“pause”)暂停了,并不是说程序结束。另外即使点击关闭,基本也看不到,因为打印完窗口立马就被关闭了,这些都是一瞬间完成的。

VS 2015 DLL的创建、静态调用和动态调用_第11张图片

你可能感兴趣的:(C++)