精华内容
下载资源
问答
  • 2020-06-18 11:12:07

    一、前言

    1. dll(dynamic)动态链接库,包含多个程序执行的代码和数据,程序运行时是需要使用dll;
    2. lib(static)静态链接库,包含dll中各函数的入口信息,在程序编译时用到;
    3. 动态连接库可以使设计者隐藏函数的具体实现,便于增加代码的复用性;
    4. 动态链接库参与的程序中,lib文件和dll文件都要用到。

    二、dll的生成

    头文件中增加下面几行代码

    #pragma once
    #ifdef XXXX_EXPORTS                        //如果定义了XXXX_EXPORTS 变量
    #define DLL_API __declspec(dllexport)      //那么定义DLL_API为__declspec(dllexport)的别名
    #else                                      //若梅定义
    #define DLL_API __declspec(dllimport)      //定义DLL_API字段为导入
    #endif

    __declspec(dllexport)为导出标志;若要将某个类导出:

    class DLL_API 类名

    若要将某个函数导出:

    extern "C" 返回值类型 DLL_API 函数名(参数1,参数2,...)

    extern "C"表示在其他类中已经定义了该代码里内容,这里只是声明,“C”表明按照C语言方式进行编译和链接,因为C++编译时会对函数名进行修饰,用于实现函数的重载,而C里面没有这个功能,多以需要用extern “C”在而文件头声明时加以区分,以便链接时进行正确的函数名查找;

    三、dll和lib的引用

    1. 属性—>输入—>附加依赖项,出入需要的XXX.lib文件;
    2. 将lib文件放到目标工程目录下,确保该目录是在属性—>c++—>常规—>附加包含目录中,若没有,添加即可;
    3. 将dll文件放到Debug/release文件下;
    4. 在工程文件中太你家与lib名称相同的头文件,文件最上方添加:
    class __declspec(dllimport) XXX.lib

       

    若导入函数:

    extern "C" __declspec(dllimport) void 函数名(参数列表);

    导入动态连接库可以也可以如下写法:

    #include "../DLL/DLL.h"   //通过相对路径或绝对路径添加头文件
    #pragma comment (lib,"../DLL/DLL1.lib")  //添加库文件

    四、实例

    dllExample.h

    #pragma once
    #include <iostream>
    #ifdef DLL_EXPORTS
    #define DLL_API __declspec(dllexport)
    #else
    #define DLL_API __declspec(dllimport)
    #endif
     
    class DLL_API ExportInterface
    {
    public:
    	virtual void foo() = 0;
    	virtual ~ExportInterface()
    	{
    		std::cout << "call ~ExportInterface"<< std::endl;
    	}
    };
     
    extern "C" DLL_API ExportInterface* getInstance();
    extern "C" DLL_API void releaseInstance(ExportInterface* pInstance);
     
    #ifdef DLL_EXPORTS  //我们并不需要向外导出该类的定义,在外部代码编译时,也不需要包含此类的定义。
    class ExportClass : public ExportInterface
    {
    private:
    	std::string x; //由于外部代码对此不可见,此处的std::string是安全的。
    public:
    	void foo(); //函数体在dllExample.cpp中实现
    	virtual ~ExportClass()
    	{
    		std::cout << "call ~ExportClass" << std::endl;
    	}
    };
    #endif

    dllExample.cpp:

    #define DLL_EXPORTS
    #include "dlltest.h"
    #include <iostream>
     
    extern "C" DLL_API ExportInterface* getInstance()
    {
    	ExportInterface* pInstance = new ExportClass();
    	return pInstance;
    }
     
    extern "C" DLL_API void releaseInstance(ExportInterface* pInstance)
    {
    	pInstance->~ExportInterface();
    }
     
    void ExportClass::foo()
    {
    	std::cout << "call func foo" << std::endl;
    	//do something...
    	return;
    }

    调用

    #include <iostream>
    #include <windows.h>
    #include "dlltest.h"
    using namespace std;
    // 动态加载DLL
    // 函数指针,用于获取GetProcAddress返回的函数地址,并调用DLL中的函数
    typedef ExportInterface* (*DllGetInstance)(void);
    typedef void (*DllReleaseInstance)(ExportInterface*);
     
    int main()
    {
    	DllGetInstance getInstance;
    	DllReleaseInstance releaseInstance;
    	ExportInterface* pTest;
    	// 显式加载
    	HINSTANCE hInstLibrary = LoadLibrary("./TestDll.dll");
     
    	if (hInstLibrary == NULL)
    	{
    		FreeLibrary(hInstLibrary);
    		cout << "cant load dll" << endl;
    	}
    	getInstance = (DllGetInstance)GetProcAddress(hInstLibrary, "getInstance");
    	releaseInstance = (DllReleaseInstance)GetProcAddress(hInstLibrary, "releaseInstance");
    	if (getInstance == NULL || releaseInstance == NULL)
    	{
    		FreeLibrary(hInstLibrary);
    		cout << "cant get func" << endl;
    	}
    	
    	pTest = getInstance();
    	pTest->foo();
    	releaseInstance(pTest);
     
    	std::cin.get();
     
    	FreeLibrary(hInstLibrary);
     
    	std::cin.get();
    	return 0;
    }

    具体参考:

    C++通过动态链接库导出类,以及WINDOWS的静态链接库LIB和动态链接库DLL基本区别和使用_fantasysolo的博客-CSDN博客_c++ dll 导出类

    C++动态链接库(dll)与静态链接库(lib)的生成与导入_AnthonyStark的博客-CSDN博客

    更多相关内容
  • 使用VS2010编写的《Windows程序设计》中动态链接库 dll 的例子 原书中有一些错误 以及不适用于新版本VS的地方 对程序添加了很多自己的理解的注释 主要是一个dll的编写和使用 适合对动态链接库不了解的同学学习参考
  • 1.创建一个动态链接库,在该动态链接库中添加一个函数,该函数可以输出如图所示的图形; 2.在上面创建的动态链接库中添加导出类,并把绘制图形的函数添加到该类下; 3.创建一个动态链接库,在该动态链接库中添加一个...
  • C语言dll动态链接库

    2022-04-20 21:09:55
    C语言dll动态链接库 vs中dumpbin工具的使用 参考链接:https://blog.csdn.net/DoronLee/article/details/78284837   用vs生成的.obj文件、.lib库、.dll动态链接库、.exe执行文件,如果想查看其中这些文件或库...

    C语言dll动态链接库

    vs中dumpbin工具的使用

    参考链接:https://blog.csdn.net/DoronLee/article/details/78284837

      用vs生成的.obj文件、.lib库、.dll动态链接库、.exe执行文件,如果想查看其中这些文件或库包含哪些函数(比如.dll库有哪些导出函数)以及相关的信息(符号清单),可以通过vs自带的dumpbin工具来完成。

      dumpbin.exe为Microsoft COFF二进制文件转换器,它显示有关通用对象文件格式(COFF)二进制文件的信息。可以使用dumpbin检查COFF对象文件、标准COFF对象库、可执行文件和动态链接库等。

    注意:dumpbin工具只能在命令行下使用。

    在如下界面点击即可进入:

    效果图

    效果图

      输入dumpbin,即可查看相关命令。

    效果图

    其中,/EXPORTS:此选项显示从可执行文件或DLL动态链接库中导出的所有定义。

    Windows中_declspec(dllexport)和_declspec(dllimport)的使用

    参考链接:https://blog.csdn.net/fengbingchun/article/details/78825004/

      _declspec是Microsoft VC中专用的关键字,它配合一些属性对标准的C/C++进行扩充。_declspec关键字应该出现在声明的前面。

      _declspec(dllexport)用于Windows中的动态链接库中,声明导出函数、类、对象等供其它项目调用。即将函数、类、对象等声明为导出函数,供其他程序调用,作为动态链接库对外接口函数、类等。

      _declspec(dllimport)用于Windows中,从别的动态链接库中声明导入函数、类、对象等供本动态库或.exe文件使用。当你需要使用dll动态链接库中的函数时,往往不需要显示地导入函数,编译器可自动完成。不使用_declspec(dllimport)也能正确编译代码,但使用_declspec(dllimport)使得编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于dll动态链接库中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨dll边界的函数调用中。声明一个导入函数,是说这个函数是从别的dll库中导入。

      关于_declspec(dllexport)和_declspec(dllimport)的使用实例可以参照链接:https://blog.csdn.net/yaotuzhi/article/details/108037549

    生成dll动态链接库

    使用vs2022建立一个动态链接库项目,步骤如下:

    效果图
    效果图
    效果图
    效果图

    此时已经创建了一个动态链接库项目,结构如下:

    效果图

    建立一个Dll.c文件,加入如下代码:

    效果图

    _declspec(dllexport)  const char* GetModuleName()
    {
    	return "MenuDemo";
    }
    

    效果图

    生成项目,得到如下结果:

    效果图
    效果图

    此时已经生成了一个dll动态链接库。

    使用dumpbin工具检查Project1.dll文件

      我们首先cd进入生成dll文件的路径。

    效果图

      紧接着,我们使用/EXPORTS命令检查Project1.dll文件。

    效果图

    如果我们没有使用_declspec(dllexport),编译项目生成dll动态链接库。

    const char* GetModuleName()
    {
    	return "MenuDemo";
    }
    

    再次使用dumpbin工具进行检查。

    效果图

    我们发现该dll库没有任何的导出函数(类、对象等)。

    效果图

      通过导出函数名,实际上是个字符串,我们可以找到该dll动态链接库中该导出函数名(字符串)对应的逻辑地址,从而使用它。

    使用自定义dll动态链接库

    检索指定文件

    #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <Windows.h>
    
    //获取当前程序所在路径
    unsigned long GetDirectory(char* path, int len)
    {
    	//获取当前程序所在的路径(如exe执行程序)
    	//返回值为路径的长度
    	//形参第一个表示为存放数据的长度,第二个表示为存放数据的容器
    
    	//当我们要拿到当前程序所在路径的时候,我们无需传入任何值,
    	//但是为了满足形参的需求,我们传入0和NULL(仅仅表示第一个参数是整型,第二个参数是指针类型)
    
    	//以下使用GetCurrentDirectoryA的方式是错误的,有许多API为了防止接收结果的缓冲区大小不够,专门设计成这样。
    	//第一次需要传入0,NULL等毫无意义的参数从而得到返回的结果集大小,
    	//再根据返回的结果大小判断跟自己设计的缓冲区大小是否匹配或者动态申请一片空间来接收。
    	//再次调用时传入缓冲区大小如果不满足需要的缓冲区大小,那么会返回0。
    	//错误调用方式:
    	//unsigned int size = GetCurrentDirectoryA(path, len);
    	//return size == 0 ? -1 : size;
    
    	//正确调用方式
    	unsigned long size = GetCurrentDirectoryA(0, NULL);
    	//等于0,一般为path无法存放该路径或者没有权限访问该路径
    	if (GetCurrentDirectoryA(len, path) == 0)
    		return -1;
    	return size;
    }
    
    //FindAllFiles("./","*.*")
    //功能:查找当前程序路径下所有符合条件的文件
    //第一个参数就是目录的绝对路径
    //第二个参数就是要匹配的文件名,比如文件类型为dll的文件,那么就是"*.dll",即"*"符号表示任意的字符串均可。
    void FindAllFiles(const char* dir, const char* extend)
    {
    	char path[4096];
    	//sprintf指的是字符串格式化命令,主要功能是把格式化的数据写入到某个字符串中
    	//即发送格式化的数据输出到path所指向的字符串中。
    	sprintf(path, "%s/%s", dir, extend);
    	WIN32_FIND_DATA findData;
    	HANDLE hFind;//句柄
    	//FindFirstFileA,用于获得指定目录的第一个文件
    	//第一个参数用于指定搜索目录和文件类型,可以用通配符。
    	//虽然FindFirstFileA的第二个参数(用于保存搜索得到的文件信息)是需要LPWIN32_FIND_DATAA类型的,
    	//但是我们通过底层minwinbase.h文件中发现WIN32_FIND_DATA和LPWIN32_FIND_DATAA结构体的结构是相同的,
    	//使用WIN32_FIND_DATA较为方便,也可直接使用LPWIN32_FIND_DATAA
    	hFind = FindFirstFileA(path, &findData);
    
    	LARGE_INTEGER size;//用于组合文件长度
    	if (hFind == INVALID_HANDLE_VALUE)
    	{
    		printf("Filed to find file!\n");
    		return;
    	}
    
    	//FindFirstFileA和FindNextFileA可以遍历指定目录的所有文件。
    	do
    	{
    		//排除"."和".."两个结果
    		if (strcmp(findData.cFileName, ".") == 0 && strcmp(findData.cFileName, "..") == 0)
    			continue;
    		//是否为目录(文件夹)
    		//dwFileAttributes返回的是文件属性(dwFileAttributes) & 具体类型项的值(表示属于哪一类文件)只有两种情况:
    		//为非零值,即为真;为零值,即为假。
    		if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    			printf("%s %s", findData.cFileName, "\t<dir>\n");
    		else
    		{
    			//组合文件大小,之前只有32位操作系统,后来出现64位操作系统,所以设计的时候分为低32位、高32位长度
    			//组合之后成为完整的文件长度
    			size.LowPart = findData.nFileSizeLow;
    			size.HighPart = findData.nFileSizeHigh;
    			printf("%8d bytes \t %s \n", size.QuadPart, findData.cFileName);
    		}
    	} while (FindNextFileA(hFind, &findData));
    	//FindNextFileA顾名思义,用于搜索下一个文件,当不存在下一个文件时,即搜索完毕后,返回false。
    	//第一个参数:上一次FindFirstFileA或FindNextFileA得到的HANDLE。
    	//第二个参数:用于保存搜索得到的文件信息。
    }
    

    其中,_WIN32_FIND_DATA结构体如下:

    typedef struct _WIN32_FIND_DATA {
    	DWORD dwFileAttributes; //文件属性
    	FILETIME ftCreationTime; // 文件创建时间
    	FILETIME ftLastAccessTime; // 文件最后一次访问时间
    	FILETIME ftLastWriteTime; // 文件最后一次修改时间
    	DWORD nFileSizeHigh; // 文件长度高32位
    	DWORD nFileSizeLow; // 文件长度低32位
    	DWORD dwReserved0; // 系统保留
    	DWORD dwReserved1; // 系统保留
    	TCHAR cFileName[ MAX_PATH ]; // 长文件名
    	TCHAR cAlternateFileName[ 14 ]; // 8.3格式文件名
    } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
    

      dwFileAttributes项的值可能有以下结果:

    #define FILE_ATTRIBUTE_READONLY             0x00000001  
    #define FILE_ATTRIBUTE_HIDDEN               0x00000002  
    #define FILE_ATTRIBUTE_SYSTEM               0x00000004  
    #define FILE_ATTRIBUTE_DIRECTORY            0x00000010  
    #define FILE_ATTRIBUTE_ARCHIVE              0x00000020  
    #define FILE_ATTRIBUTE_DEVICE               0x00000040  
    #define FILE_ATTRIBUTE_NORMAL               0x00000080  
    #define FILE_ATTRIBUTE_TEMPORARY            0x00000100  
    #define FILE_ATTRIBUTE_SPARSE_FILE          0x00000200  
    #define FILE_ATTRIBUTE_REPARSE_POINT        0x00000400  
    #define FILE_ATTRIBUTE_COMPRESSED           0x00000800  
    #define FILE_ATTRIBUTE_OFFLINE              0x00001000  
    #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED  0x00002000  
    #define FILE_ATTRIBUTE_ENCRYPTED            0x00004000  
    #define FILE_ATTRIBUTE_INTEGRITY_STREAM     0x00008000  
    #define FILE_ATTRIBUTE_VIRTUAL              0x00010000  
    #define FILE_ATTRIBUTE_NO_SCRUB_DATA        0x00020000  
    #define FILE_ATTRIBUTE_EA                   0x00040000  
    #define FILE_ATTRIBUTE_PINNED               0x00080000  
    #define FILE_ATTRIBUTE_UNPINNED             0x00100000  
    #define FILE_ATTRIBUTE_RECALL_ON_OPEN       0x00040000  
    #define FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS 0x00400000
    

    测试用例

    int main()
    {
    	char pathDir[1024] = "";
    	int len = GetDirectory(pathDir, 1024);
    	FindAllFiles(pathDir, "*.cpp");
    	system("pause");
    	return 0;
    }
    

    效果展示

    其中,要检索的目录结构如下:

    效果图

    测试代码在main1.c文件内,且只使用Debug模式进行调试,所以检测的是执行文件所在路径。

    效果图

    获取dll动态链接库中的输出函数地址

    #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <Windows.h>
    
    
    //获取当前程序所在路径
    unsigned long GetDirectory(char* path, int len)
    {
    	//获取当前程序所在的路径(如exe执行程序)
    	//返回值为路径的长度
    	//形参第一个表示为存放数据的长度,第二个表示为存放数据的容器
    
    	//当我们要拿到当前程序所在路径的时候,我们无需传入任何值,
    	//但是为了满足形参的需求,我们传入0和NULL(仅仅表示第一个参数是整型,第二个参数是指针类型)
    
    	//以下使用GetCurrentDirectoryA的方式是错误的,有许多API为了防止接收结果的缓冲区大小不够,专门设计成这样。
    	//第一次需要传入0,NULL等毫无意义的参数从而得到返回的结果集大小,
    	//再根据返回的结果大小判断跟自己设计的缓冲区大小是否匹配或者动态申请一片空间来接收。
    	//再次调用时传入缓冲区大小如果不满足需要的缓冲区大小,那么会返回0。
    	//错误调用方式:
    	//unsigned int size = GetCurrentDirectoryA(path, len);
    	//return size == 0 ? -1 : size;
    
    	//正确调用方式
    	unsigned long size = GetCurrentDirectoryA(0, NULL);
    	//等于0,一般为path无法存放该路径或者没有权限访问该路径
    	if (GetCurrentDirectoryA(len, path) == 0)
    		return -1;
    	return size;
    }
    
    typedef const char* (*FGetModuleName)();
    typedef struct sPluginNode_
    {
    	char path[1024];
    	char name[128];
    	//HMODULE(模块句柄)是代表应用程序载入的模块,win32系统下通常是被载入模块的线性地址
    	HMODULE module;
    	FGetModuleName GetModuleName;
    }sPluginNode;
    
    
    typedef struct sPlugin_
    {
    	sPluginNode* node[1024];
    	int index;
    }sPlugin;
    
    
    sPlugin* GetALLPluginModule()
    {
    	char pathDir[1024] = "";
    	int len = GetDirectory(pathDir, 1024);
    
    	WIN32_FIND_DATA findData;
    	char path[4096];
    	sprintf(path, "%s\\plugin\\%s", pathDir, "*.dll");
    	HANDLE hFind = FindFirstFileA(path, &findData);
    	LARGE_INTEGER size;
    	if (hFind == INVALID_HANDLE_VALUE)
    	{
    		printf("Filed to find file!\n");
    		return NULL;
    	}
    
    
    	sPlugin* plugin = (sPlugin*)malloc(sizeof(sPlugin));
    	//memset(plugin, 0, sizeof(sPlugin));//该过程需要消耗O(n)的时间
    	plugin->index = 0;
    
    	do
    	{
    		if (strcmp(findData.cFileName, ".") == 0 && strcmp(findData.cFileName, "..") == 0)
    			continue;
    
    		if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    			continue;
    		else
    		{
    			sPluginNode* pluginNode = (sPluginNode*)malloc(sizeof(sPluginNode));
    			plugin->node[plugin->index++] = pluginNode;
    
    			sprintf(pluginNode->path, "%s\\plugin\\%s", pathDir, findData.cFileName);
    			sprintf(pluginNode->name, "%s", findData.cFileName);
    
    			size.LowPart = findData.nFileSizeLow;
    			size.HighPart = findData.nFileSizeHigh;
    
    			//将指定的模块加载到调用进程的地址空间中。指定的模块可能会导致其它模块被加载。
    			//参数:可以是库模块(.dll文件)或者可执行文件(.exe文件)。
    			HMODULE hModule = LoadLibraryA(pluginNode->path);
    			//GetProcAddress是一个计算机函数,功能是检索指定的动态链接库(DLL)中的输出库函数地址。
    			//第一个参数是模块句柄
    			//第二个参数是输出库函数名称
    			//返回值:如果函数调用成功,返回值是DLL中的输出函数地址,否则则返回NULL
    			FGetModuleName GetModuleName = (FGetModuleName)GetProcAddress(hModule, "GetModuleName");
    			pluginNode->module = hModule;
    			pluginNode->GetModuleName = GetModuleName;
    		}
    	} while (FindNextFileA(hFind, &findData));
    	return plugin;
    }
    

    测试用例

    int main()
    {
    	sPlugin* plugin = GetALLPluginModule();
    	for (int i = 0; i < plugin->index; i++)
    	{
    		sPluginNode* node = plugin->node[i];
    		printf("%s == %s\n", node->name, node->GetModuleName());
    	}
    	system("pause");
    	return 0;
    }
    

    效果展示

    效果图

    其中,plugin文件夹中Project1.dll源代码如下:

    _declspec(dllexport) const char* GetModuleName()
    {
    	return "MenuDemo";
    }
    

    测试代码在main1.c文件内,且只使用Debug模式进行调试,所以检测的是执行文件所在路径。

    效果图

    展开全文
  • 扩展的动态链接库的创建

             相信大家对后缀名为.dll已经很熟悉了,下图中是CALViewer软件的安装路径,可以看到该安装路径下存在很多相关的dll库,这些库称之为动态链接库,在windows中也叫应用程序扩展。

        为什么会有这些库呢,是因为在日常进行软件开发的时候,有些人会将软件的需求(即所有的功能)全部集合在一个工程目录中,这会导致整个工程看起来很臃肿,很笨重,也不利于后期的维护。解决臃肿的问题是将整个工程的需求分为多个子功能,然后可以对一些子功能进行封装,使其成为一个库的形式,在后面的工程中,如果需要用到的话就可以直接调用该库了。

        使用动态链接库的好处不仅仅只有上面的优点哦。

    1. 库可以采用多种编程语言来编写,你可以使用自己熟悉的语言来开发这个库,然后由其它语言编写的可执行程序来调用这些dll。

    2. 提供了二次开发的平台,可以采用dll形式提供一个二次开发的平台,让用户可以利用该dll调用其中实现的功能,编写符合自己业务需求的产品,从而实现二次开发。

    3. 可以节省磁盘空间和内存,如果多个应用程序需要访问同样的功能,那么可以将该功能以dll的形式提供,这样在机器上只需要存在一份该dll文件就可以了,从而节省了磁盘空间。另外,如果多个应用程序使用同一个dll,该dll的页面只需要放入内存一次,所有的应用程序就都可以共享它的页面了。这样,内存的使用将更加有效。

      例如:如图所示就是一个动态链接库被两个进程调用访问时的内存示意图,当32位进程被加载时,系统会为它先分配一个4GB的地址空间,接着分析该可执行模块,找到该程序要调用的dll,然后系统搜索这些dll,找到后就加载它们,并为它们分配虚拟的内存空间,最后将dll的页面映射到调用进程的地址空间。从图中,我们可以看到,dll的虚拟内存有代码页面和数据页面,它们被分别映射到第一个进程的代码页面和数据页面。如果这是第二个进程也启动了,并且它也需要访问该dll,那么这时只需要将该dll在虚拟内存中的代码页面和数据页面映射到第二个进程的地址空间即可。在内存中,只需要存在一份dll的代码和数据。多个进程可以共享dll的同一份代码,这样可以节省内存空间。

            下面我将用Visual Studio 2008编译平台来创建dll工程,然后将dll库挂载到项目中。(例如:创建一个计算器的dll库,该库工程名称为“TEST_DLL”,然后创建另外一个来调用该库的工程,名称为“TEST_USE”),即下面的逻辑,TEST_USE调用TEST_DLL的过程:

            对于TEST_USE基于对话框的工程创建步骤这里就不详细介绍了,按照以往的开发经验即可。下面将重点介绍TEST_DLL的开发过程。

    第一步 创建dll的mfc工程

        选择"MFC DLL"选项,然后给库工程起一个名字“TEST_DLL”,并且选择要保存的路径。

            然后点击确定,再点击下一步,然后在该界面一定要选择“带静态链接MFC的规则”,这个选项和上面的“使用共享 MFC DLL 的规则”之间的区别就是,使用共享的生成出来的dll在可执行程序中调用时,要求可执行程序所在的电脑一定要安装Visual Studio环境,不然会报错,而使用“带静态链接 MFC 的规则”就不需要电脑上具备Visual Studio的环境要求。

    选择后点击完成,就可以在界面上进行自己的功能需求编写了。

    第二步 设计库界面

    在编辑器的左边的“资源视图”可以看到我们的dll工程——TEST_DLL。

    右键点击"TEST_DLL.rc",选择“添加资源”后,Visual Studio会弹出下面的对话框:

    选择“Dialog”后选择右边的“新建”按钮后在“资源视图”可以看到有“Dialog”的目录,并且出现设计界面,我们可以在该界面上进行空间的布局:

    点击对话框然后在右边一栏点击“属性”,再将“Border”设置为None,还有Style的属性一定要设置为“Child”

    然后选择工具箱,在界面上进行控件的布局,假设我们做个简单的计算器Demo:

    双击Button,给dll添加类,设置类名为“TEST”:

    第三步 添加代码

    在类的头文件TEST.h中将TEST类名之前添加_declspec(dllexport),表示该类TEST为导出类。

    class _declspec(dllexport) TEST : public CDialog
    {
      DECLARE_DYNAMIC(TEST)
    
    public:
      TEST(CWnd* pParent = NULL);   // 标准构造函数
      virtual ~TEST();
    
    // 对话框数据
      enum { IDD = IDD_DIALOG1 };
    
    protected:
      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    
      DECLARE_MESSAGE_MAP()
    public:
      afx_msg void OnBnClickedButton1();
    };

    在TEST_DLL.h文件中添加创建TEST对话框界面的函数接口TEST *CreateTESTDialog(HWND hWndParent);的声明,该接口返回一个指针,该指针会赋值给调用该dll的TEST_USE工程操作,记住需要添加头文件#include "TEST.h"

    // TEST_DLL.h : TEST_DLL DLL 的主头文件
    //
    
    #pragma once
    
    #ifndef __AFXWIN_H__
      #error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
    #endif
    
    #include "resource.h"    // 主符号
    #include "TEST.h"
    
    // CTEST_DLLApp
    // 有关此类实现的信息,请参阅 TEST_DLL.cpp
    //
    // 声明这个接口供可执行程序调用创建该dll界面
    TEST *CreateTESTDialog(HWND hWndParent);
    
    
    class CTEST_DLLApp : public CWinApp
    {
    ...
    };

    在相应的"TEST.cpp"文件中实现该接口:

    TEST *CreateTESTDialog(HWND hWndParent)        
    {
      AFX_MANAGE_STATE(AfxGetStaticModuleState());                    //模块转换
      TEST *test;
      test = new TEST;                
      CWnd *pWndParent = CWnd::FromHandle(hWndParent);
      test->Create(IDD_DIALOG1,pWndParent);
      return test;
    }

    接下来在文件“TEST_DLL_.def”中添加需要导出的函数 CreateTESTDialog  @1  

    ; TEST_DLL.def : 声明 DLL 的模块参数。
    
    LIBRARY      "TEST_DLL"
    
    EXPORTS
        ; 此处可以是显式导出
        CreateTESTDialog  @1    

    其中,具体的控件代码编写这里就不多说了。

    第四步 导出库到可执行程序

     选择编译器中的Debug(Release)生成对应的dll文件和lib文件,点击运行按钮,然后打开dll工程所在的文件路径,可以看到生成了对应的TEST_DLL.dll和TEST_DLL.lib。

    将Debug(Release)中的dll和lib文件复制到要调用该dll的工程TEST_USE中的Debug(Release)文件夹中,此外还得把dll工程中的TEST_DLL.h和TEST.h文件复制到调用dll的工程TEST_USE中去。

    第五步 在TEST_USE工程中使用dll

    在工程TEST_USE的“TEST_USEDlg.cpp”中添加:

    
    #include "TEST_DLL.h"
    #pragma comment(lib,"../Debug/TEST_DLL.lib")

    将TEST_USE中的“TEST.h”文件中的 enum { IDD = IDD_DIALOG1 };IDD修改为TEST_USE工程中的界面IDD。接下来在TEST_USEDlg.h中定义一个TEST类的指针,用来指向dll的界面。

    
    public:
      TEST *test;

    最后在TEST_USEDlg.cpp的OnInitDialog中添加以下代码(其中m_par表示在TEST_USE工程的界面上EDIT控件的控件变量,也就是说将计算器的界面挂载到TEST_USE中的EDIT控件中显示):

    // 调用库dll工程中的界面,并且将指针返回给test
      test = CreateTESTDialog(m_par.m_hWnd);
    
      // 在TEST_USE工程中放置的控件Edit,设置窗口大小,以便dll界面可以放置到这个控件当中
      CRect rect;
      m_par.GetClientRect(&rect);
      rect.top += 22;
      rect.left +=2;
      rect.right -= 2;
      rect.bottom -=2;
    
      
      test->MoveWindow(&rect);
      test->ShowWindow(SW_SHOW);
    

    第六步 运行

    展开全文
  • 主要介绍了VC程序在Win32环境下动态链接库(DLL)编程原理,包括了dll文件的原理与具体实现过程,对于深入掌握VC程序设计具有很好的参考借鉴价值,需要的朋友可以参考下
  • 动态链接库(Dynamic Link Library 或者 Dynamic-link Library,缩写为 DLL),是微软公司在微软Windows操作系统中,实现共享函数库概念的一种方式。这些库函数的扩展名是 ”.dll"、".ocx"(包含ActiveX控制的库)...

    动态链接库

    动态链接库(Dynamic Link Library 或者 Dynamic-link Library,缩写为 DLL),是微软公司在微软Windows操作系统中,实现共享函数库概念的一种方式。这些库函数的扩展名是 ”.dll"、".ocx"(包含ActiveX控制的库)或者 “.drv”(旧式的系统驱动程序)。
    动态链接库提供了一种可以使进程调用不属于其可执行代码函数的方法,这些代码位于DLL文件中,在使用时动态被调出,且多个程序可同时调用其中的函数。
    使用动态链接库可以更为容易地将更新应用于各个模块,而不会影响该程序的其他部分。例如,有一个大型网络游戏,如果把整个数百MB甚至数GB的游戏的代码都放在一个应用程序里,日后的修改工作将会十分费时,而如果把不同功能的代码分别放在数个动态链接库中,则无需重新生成或安装整个程序就可以应用更新。

    创建DLL文件

    因为要考虑到执行效率,目前的系统大多是由C或C++编写成的,故DLL也是需要使用C或C++编写的。python这类作为一种解释型语言,和DLL这样编译后的二进制文件可以说不在一个维度上,目前只有使用python调用dll文件函数的方法,而暂时缺少编写的方法。
    C++语言的编写当然要使用宇宙第一IDE–visual studio,具体过程如下:
    首先在创建新项目中选择动态链接库
    动态链接库选择
    然后自定义项目名称后点击创建,即可见下图
    创建成功
    此时生成的DllMain为函数入口,当调用dll文件时会自动执行,其具体内容如下:

    BOOL APIENTRY DllMain( HMODULE hModule,             //指向自身的句柄
                           DWORD  ul_reason_for_call,   //调用原因
                           LPVOID lpReserved            //静态加载非NULL,动态加载为NULL
                         )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:  //进程创建时调用
        case DLL_THREAD_ATTACH:   //线程创建时调用
        case DLL_THREAD_DETACH:   //线程结束时调用
        case DLL_PROCESS_DETACH:  //进程结束时调用
            break;
        }
        return TRUE;
    }
    

    因为内容固定,也不影响我们编写程序,所以也没有深究。个人理解是用于记录调用的信息,如调用者,调用原因,和加载方式,并表示加载进程。
    #DLL编写
    将一个简单弹出消息框的函数编写入DLL,正常执行会出现如下结果:
    函数示例
    首先需要Windows.h库,然后将目标函数以C语言形式导出,默认以C++形式导出,但C++考虑重载问题,会在函数名称中加入很多符号,不便于导出。
    具体代码如下:

    #include<Windows.h> 
    extern "C" _declspec(dllexport) void def();  //以C语言形式导出
    void def() {
        MessageBox(NULL, L"dll loaded", L"Attention", MB_OK);   //PWSTR类型转换 字符串类型前加L
    }
    /* 最后参数代表按钮选项 
    MB_OK	默认,“确定”
    MB_OKCANCEL	“确定”“取消”
    MB_YESNO	"是“”否“
    MB_YESNOCANCEL	"是”“否”“取消”
    MB_RETRYCANCEL	“重试”“取消”
    MB_ABORTRETRYIGNORE	"终止”“重试”“忽略” */ 
    

    因为Dll文件是不可执行文件,故不可采用通常的执行不调试方法查看结果,要选择生成解决方案才能生成需要的dll文件,步骤如下:
    生成DLL
    生成的dll文件存储在项目文件夹的debug文件夹内:
    debug文件夹内容

    动态加载

    首先需要将dll复制到该项目的debug文件夹内,然后通过
    typedef void(*getdef)();定义一种新类型,设置中间变量来传导需要的函数。然后设置导入dll的句柄,并将需要的函数赋值给设置的中间变量。

    HMODULE hDll = LoadLibrary(TEXT("Dll1.dll"));    //句柄绑定
    	if (hDll == NULL)
    		cout << "No dll found";
    	getdef mes;
    	mes=(getdef)GetProcAddress(hDll, "def");  //函数传递
    

    此时mes函数就相当于dll中的def函数。

    静态加载

    除了需要将dll文件复制到debug文件夹外,还需要将lib文件复制到项目文件内。而后通过
    #pragma comment(lib, "Dll1.lib")导入lib文件,再输入extern "C" __declspec(dllimport) void def();来导入需要的函数。此时在本项目内可自由使用导入的函数,即可直接使用def()来执行。

    总结

    本次实验基本掌握了dll文件的编写及调用方法,dll文件的主要目的是便于项目维护,当线上应用需要更新时只需要更新对应文件即可。调用dll有动态静态两种方法,个人感觉动态方法更偏存储性能,只有需要的时候动态从dll文件中读取,不影响编译生成的exe文件,但写起来比较麻烦,借助自己设置的中间变量用于传递dll中的函数;而静态方法写起来很方便,只需声明需要的函数再进行调用即可,但是实质是将lib代码文件编入exe文件中,直接导致文件体积增加,只能说各有优劣。

    展开全文
  • 【专题】C#调用动态链接库DLL

    千次阅读 2019-02-23 09:29:40
    动态链接库(Dynamic Linked Library):将写好的函数存在库中,以供其他程序开发调用,调用方式为“动态的”。  Windows为应用程序提供了丰富的函数调用,这些函数调用都包含在动态链接库中。其中有3个最重要的DLL,...
  • Lu是一个可对字符串表达式进行动态编译和运行的动态链接库dll),是一种易于扩展的轻量级嵌入式脚本,提供自动内存管理,也可以手动管理内存。Lu有丰富的运算符和数据类型,大多数运算符可进行重载。Lu的优势在于...
  • 动态链接库DLL)为模块化应用程序提供一种方式,使得更新和重用应用程序更加方便。注意只有在其它模块调用动态链接库中的函数时,动态链接库才会发挥作用。 另外,动态链接库是代码重用的绝佳方式,我们可以不必...
  • DLL(Dynamic Link Library),动态链接库文件,又称“应用程序拓展”,是软件文件类型,扩展名是“.dll”。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件...
  • LabVIEW调用DLL动态链接库

    千次阅读 2020-02-04 21:27:01
    LabVIEW调用DLL动态链接库 示例一 通过调用DLL实现数组求和。输入一个10个元素的数组,返回全部元素之和。 1.生成DLL VS中选择创建动态链接库项目 在头文件和源文件文件夹分别创建相应的.h .cpp文件 在test.h中...
  • Qt生成调用动态链接库dll

    千次阅读 2019-01-12 22:36:41
    这个文件就是链接库,又可以分为静态链接库和动态链接库。 1. 静态链接库 链接程序从库中寻找需要的符号(函数和变量的名字),查找到就将其放入可执行文件,未查找到就报错。 使用静态库链接的程序: (1)可...
  • LabVIEW调用函数返回指针的动态链接库DLL引言创建动态链接库LabVIEW中调用DllMian.dll结束语 引言 LabVIEW通过调用库函数节点可以调用C/C++生成的动态链接库。首先在VC/CVI/Matlab等语言中设计好完成计算处理任务的...
  • C#中如何调用动态链接库DLL

    千次阅读 2016-11-29 22:42:56
    每种编程语言调用DLL的方法都不尽相同,在此只对用C#调用DLL的方法进行介绍。首先,您需要了解什么是托管,什么是非托管。一般可以认为:非托管代码主要是基于win 32平台开发的DLL,activeX的组件,托管代码是基于.net...
  • C#调用动态链接库DLL

    千次阅读 2017-06-24 09:41:25
    1.概述动态链接库(Dynamic Linked Library):将写好的函数存在库中,以供其他程序开发调用,调用方式为“动态的”。 Windows为应用程序提供了丰富的函数调用,这些函数调用都包含在动态链接库中。其中有3个最重要的...
  • C#调用C++动态链接库

    千次阅读 2018-12-25 18:37:53
    将静态链接库做成动态链接库新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants...
  • 静态和动态的区别就是在链接时如何处理库函数,也就是如此导致的执行效率与使用方式的区别。
  • 动态链接库dll中弹出对话框

    千次阅读 2016-10-29 15:28:00
    动态链接库dll中弹出对话框步骤: 1、添加Dialog资源,然后在资源视图的对话框界面右击添加类,输入类名MyDlg,使得其继承与CDialogEx。(继承CDialog应该也可以)2、在新生成的类头文件MyDlg.h中引入“resource....
  • VC++动态链接库DLL编程深入浅出

    千次阅读 2018-01-10 16:42:06
    "VC-基础:VC++动态链接库DLL编程深入浅出" 1.概论  先来阐述一下dll(dynamic linkable library)的概念,你可以简单的把dll看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上...
  • 在说动态链接库DLL之前,我们要知道什么是库,库有哪些作用,以及如何判别何时用库?首先,我们要说明一般编译器的工作步骤,我们用fortran编写程序的一般步骤为:写代码阶段-&gt; 把程序转为目标文件(*.obj)...
  • 动态链接库DLL)的创建和使用

    千次阅读 2018-04-22 22:41:50
    想着使用动态库,正好没用过,学习下。概念这里不赘述。学习过程中碰到的几点,记录下来。学习是个渐进的过程,本文也是一个逐渐完善的过程。一、Static Library标准Turbo 2.0中的C函数(scanf、pringf、memcpy等...
  • VC++动态链接库dll)编程视频教学

    万人学习 2016-12-26 12:24:02
    动态链接库的开发编译属于VC++程序员技能。 本课程学习之后能够理解动态链接库原理,学会编译静态库、动态库,学会通过lib和头文件链接动态库,学会直接通过代码访问dll中函数
  • 什么是DLL动态链接库)? &nbsp; &nbsp;DLL是一个包含可由多个程序同时使用的代码和数据的库。例如:在Windows操作系统中,Comdlg32 DLL执行与对话框有关的常见函数。因此,每个程序都可以使用该DLL中...
  • VB中创建的DLL只是COM组件,无法作为输出函数的DLL,其实这是MS非常狡猾的手段...
  • 动态链接库的建立与调用

    千次阅读 2020-05-25 10:33:03
    动态链接库(Dynamic Link Library DLL)是一个可执行模块,它包含的函数可以由Windows应用程序调用以提供所需功能,为应用程序提供服务。 1.动态链接库基础知识 大型的应用程序都是由多个模块组成的,这些模块彼此...
  • C#DLL动态链接库编程

    2011-09-25 17:26:19
    也曾感受到它的带给你程序设计和编码上的好错吧今天我想和大家探讨一个主题:如何在C#创建和调用DLL(动态链接库), 其实在很大意义上而讲,DLL让我更灵活的组织编写我们的应用程序,作为软件设计者,可一个根据它来...
  • DLL动态链接库分包引用及延迟加载 1.为什么要分包 最近项目中有应用到比较多的项目dll和第三方dll,之前是都放在exe的平级目录下的,当dll多到一定程度时,会非常的乱。有一些库已经没有用到了,但由于第三方库直接...
  • 考虑到软件复用的需要,采用 WIN32动态链接库技 术,整个 TFTP服务模块仅由一个动态链接库文件 tftp. dll组成。使用多线程技术,以适应 TFTP收发过程与应 用程序的其他过程并发执行的需要。将所述方法应用于实际的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 31,273
精华内容 12,509
关键字:

动态链接库dll设计