一、开发环境

       Visual Studio 2012(其他版本亦可)

二、生成库文件(dll和lib文件)的过程

1、新建Win32项目,代码使用的是工厂方法模式,点击确认按钮

点击“下一步”

应用程序类型勾选“DLL”,点击“完成”

2、代码中可看到自动生成了dllmain.cpp文件,里面的DllMain函数是主入口函数

打开项目属性页,如有必要,可将字符集设为多字节字符集

在C/C++ 预处理器中,可以看到VS自动添加的宏定义(名称与项目名称一致),可用之进行dll导出或导入

3、源代码

【注】__declspec(dllexport)是导出库文件,__declspec(dllimport)是导入库文件

Product.h

#pragma once

#ifdef FACTORYMETHODMODE_EXPORTS
#define FACTORYMETHODMODE_API __declspec(dllexport)
#else
#define FACTORYMETHODMODE_API __declspec(dllimport)
#endif

// 抽象产品类
class FACTORYMETHODMODE_API Product
{
public:
    virtual ~Product();
    virtual void Operation() const = 0;
};

// 具体产品类A
class FACTORYMETHODMODE_API ConcreteProductA : public Product
{
public:
	~ConcreteProductA();
    void Operation() const override;
};

// 具体产品类B
class FACTORYMETHODMODE_API ConcreteProductB : public Product
{
public:
	~ConcreteProductB();
    void Operation() const override;
};

Product.cpp

#include "stdafx.h"
#include "Product.h"
#include <iostream>

Product::~Product()
{
	std::cout << "Product destruction.\n";
}

ConcreteProductA::~ConcreteProductA()
{
	std::cout << "ConcreteProductA destruction.\n";
}

void ConcreteProductA::Operation() const
{
    std::cout << "ConcreteProductA Operation\n";
}

ConcreteProductB::~ConcreteProductB()
{
	std::cout << "ConcreteProductB destruction.\n";
}

void ConcreteProductB::Operation() const
{
	std::cout << "ConcreteProductB Operation\n";
}

Creator.h

#pragma once

#ifdef FACTORYMETHODMODE_EXPORTS
#define FACTORYMETHODMODE_API __declspec(dllexport)
#else
#define FACTORYMETHODMODE_API __declspec(dllimport)
#endif

#include "Product.h"

// 抽象创建者类
class FACTORYMETHODMODE_API Creator
{
public:
	Creator();
    virtual ~Creator();
    
    // 工厂方法
    virtual Product* FactoryMethod() = 0;
    
    void SomeOperation();

protected:
	Product	*m_product;
};

// 具体创建者A
class FACTORYMETHODMODE_API ConcreteCreatorA : public Creator
{
public:
	ConcreteCreatorA();
	~ConcreteCreatorA();

    Product* FactoryMethod() override;
};

// 具体创建者B
class FACTORYMETHODMODE_API ConcreteCreatorB : public Creator
{
public:
	ConcreteCreatorB();
	~ConcreteCreatorB();

    Product* FactoryMethod() override;
};

Creator.cpp

#include "stdafx.h"
#include "Creator.h"
#include <iostream>

Creator::Creator()
	: m_product(nullptr)
{
}

Creator::~Creator()
{
	std::cout << "Creator destruction.\n";

	if (m_product)
	{
		delete m_product;
		m_product = nullptr;
	}
}

void Creator::SomeOperation()
{
    Product* product = this->FactoryMethod();

	if (product) {
		product->Operation();
	}
}


ConcreteCreatorA::ConcreteCreatorA()
	: Creator()
{
}

ConcreteCreatorA::~ConcreteCreatorA()
{
	std::cout << "ConcreteCreatorA destruction.\n";
}

Product* ConcreteCreatorA::FactoryMethod()
{
	if (nullptr == m_product) {
		m_product = new ConcreteProductA();
	}

    return m_product;
}


ConcreteCreatorB::ConcreteCreatorB()
	: Creator()
{
}

ConcreteCreatorB::~ConcreteCreatorB()
{
	std::cout << "ConcreteCreatorB destruction.\n";
}

Product* ConcreteCreatorB::FactoryMethod()
{
	if (nullptr == m_product) {
		m_product = new ConcreteProductB();
	}

    return m_product;
}

4、选择Debug或Release编译,即可生成lib和dll文件,如下图所示:

至此,动态链接和静态连接库已生成,将它们拷贝到其他工程中即可使用。

三、静态加载(隐式加载)

1、新建一个测试工程,博主建的是一个名为“UseDllTest”的控制台程序。

2、将FactoryMethodMode.lib和FactoryMethodMode.dll文件拷贝到编译目录下,需要注意的是如果UseDllTest使用Debug编译,那就拷贝FactoryMethodMode项目中Debug生成的库文件,如果UseDllTest使用Release编译,那就拷贝FactoryMethodMode项目中Release生成的库文件,否则混用的话可能有些问题。

3、将FactoryMethodMode库文件中用到的头文件拷贝到当前项目中:

4、源代码

UseDllTest.cpp

#include "stdafx.h"
#include "..\lib\Creator.h"
#include "..\lib\Product.h"

#pragma comment(lib, "FactoryMethodMode.lib")

int _tmain(int argc, _TCHAR* argv[])
{
	Creator *creatorA = new ConcreteCreatorA();
	creatorA->SomeOperation();

	Creator *creatorB = new ConcreteCreatorB();
	creatorB->SomeOperation();

	delete creatorA;
	creatorA = nullptr;

	delete creatorB;
	creatorB = nullptr;

	return 0;
}

也可以在项目属性页中添加库文件,它与在代码中添加#pragma comment(lib, "FactoryMethodMode.lib")是一样的效果

5、运行结果如下:

============================分割线==============================

以上是去年写的,在使用的时候,是采用静态加载的方式,然而有时候我们希望用动态加载(显式加载)的方式。

LoadLibrary 动态加载的工作原理

LoadLibrary 是 Windows 提供的显式加载 DLL 的 API,完整的动态加载流程通常是:

  1. 加载 DLL:调用 LoadLibrary(或 LoadLibraryW)传入 DLL 路径,系统将 DLL 加载到进程内存空间,返回 DLL 句柄(HMODULE);
  2. 获取函数地址:通过 GetProcAddress 传入 DLL 句柄和函数名,获取函数的内存地址;
  3. 调用函数:通过函数指针调用获取到的函数;
  4. 释放 DLL:调用 FreeLibrary 释放 DLL

【使用步骤】

一、开发环境

        Visual Studio 2022(用其他版本亦可)

二、生成库文件(dll和lib)的详细步骤

1、在VS中创建新项目,选择“动态链接库(DLL)”

2、点击“下一步”后,项目名称中填写“MathFormular”(因为示例中写的是几个简单的数学公式),点击“创建”按钮,工程中自动生成dllmain.cpp文件,它是dll 主函数入口

3、在项目-属性页的C/C++ 预处理器中可以看到,已自动添加名为MATHFORMULAR_EXPORTS的宏定义

4、在工程中添加名为“MathFormular”的类

MathFormular.h 代码内容

#pragma once

#ifdef MATHFORMULAR_EXPORTS
#define MATHFORMULAR_API __declspec(dllexport)
#else
#define MATHFORMULAR_API __declspec(dllimport)
#endif

extern "C" {
    MATHFORMULAR_API int add(int a, int b);
    MATHFORMULAR_API int subtract(int a, int b);
    MATHFORMULAR_API int multiply(int a, int b);
    MATHFORMULAR_API int divide(int a, int b);
}

MathFormular.cpp

#include "pch.h"
#include "MathFormular.h"
#include <iostream>

int add(int a, int b)
{
    return (a + b);
}

int subtract(int a, int b)
{
    return (a - b);
}

int multiply(int a, int b)
{
    return (a * b);
}

int divide(int a, int b)
{
    if (0 == b) {
        std::cerr << "error: Division by zero";
        return -1;
    }

    return (a / b);
}

5、编译,生成dll和lib文件,如下图所示:

用Release编译,生成的文件就在Release目录下

三、动态加载dll文件的使用过程

1、创建一个控制台应用程序

2、项目名称取为“MathFormularTest”

3、将生成的dll文件拷贝到这个目录下(如果放在其他目录,则在使用的时候使用dll的绝对路径,或者在项目的属性页中添加包含目录),需注意的是,这里只需将MathFormular.dll文件加进来即可,不需要将代码头文件和MathFormular.lib文件加进来

4、MathFormularTest.cpp 代码

#include <iostream>
#include <Windows.h>
#include <string>

typedef int (*AddFunc)(int, int);       // typedef后面的int表示函数的返回值类型,AddFunc是函数地址,它后面的两个int是函数形参类型
typedef int (*SubFunc)(int, int);
typedef int (*MultiplyFunc)(int, int);
typedef int (*Divide)(int, int);

int main()
{
    const wchar_t* dllPath = L"MathFormular.dll";
    HMODULE hDll = LoadLibrary(dllPath);

    if (NULL == hDll) {
        std::cout << "LoadLibrary失败,错误码:" << GetLastError() << std::endl;
        return -1;
    }

    AddFunc pAdd = (AddFunc)GetProcAddress(hDll, "add");
    SubFunc pSub = (AddFunc)GetProcAddress(hDll, "subtract");
    SubFunc pMultiply = (AddFunc)GetProcAddress(hDll, "multiply");
    SubFunc pDivide = (AddFunc)GetProcAddress(hDll, "divide");

    if (NULL == pAdd || NULL == pSub || NULL == pMultiply || NULL == pDivide) {
        std::cout << "GetProcAddress失败,错误码:" << GetLastError() << std::endl;
        FreeLibrary(hDll);
        return -1;
    }

    int num1 = pAdd(1, 5);
    std::cout << "1 + 5 = " << num1 << std::endl;

    int num2 = pSub(7, 12);
    std::cout << "7 - 12 = " << num2 << std::endl;

    int num3 = pMultiply(3, 6);
    std::cout << "3 × 6 = " << num3 << std::endl;

    int num4 = pDivide(21, 3);
    std::cout << "21 ÷ 3 = " << num4 << std::endl;

    FreeLibrary(hDll);
    return 0;
}

5、运行结果

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐