zoukankan      html  css  js  c++  java
  • 【VC++积累】之四、动态链接库

    本文要说的是动态链接库  dll   和静态链接库  lib

    动态链接库是一种资源的集合,包括函数,变量,类,资源等都可以从动态链接库中来导出!

    静态链接库的代码就可以直接放到exe文件中,动态链接库是被exe文件动态的加载或者卸载。                静态链接库不能包含其他的动态链接库和静态链接库,而动态链接库是可以的。


    在本文我们会用两种方式来写动态链接库文件,即:SDK  API编写和  MFC 编写。

    SDK中


    1、静态链接库

    这里看一下静态库的调用方式:

    //调用静态库
    #include"静态库的头文件" 
    #pragma comment(lib, "静态库")

    ok下面我们来编写一个简单的静态链接库的文件:

    /*
    lib.h文件
    */
    #pragma once//防止重复包含
    
    //声明函数,加上extern “C"是为了避免VC 将add的名字改编
    extern "C" int add(int a, int b);

    /*
    lib.cpp文件
    */
    #include "stdafx.h"
    #include "lib.h"
    
    int add(int a, int b)
    {
    	return a+b;
    }

    下面是调用测试的代码:

    #include<iostream>
    #include"../lib/lib.h"
    #pragma comment(lib, "../debug/lib.lib")
    
    using namespace std;
    
    int main(void)
    {
    	int m = add(2, 3);
    	cout << m << endl;
    	system("pause");
    	return 0;
    }

    2、动态链接库

    动态链接库的入口点和其他的应用程序就不一样了,下面我们来比较看一下:

    CUI控制台程序(不是DOS):main
    GUI用户界面程序:WinMain
    DLL程序入口点函数:DllMain    不过,当你的dll就是导出资源,那么可以没有DllMain

    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
    					 )
    {
        return TRUE;
    }

    第一个参数是一个句柄,第三个参数保留没有意义。

    来看第二个参数,他指明了dll被调用的原因,他有如下四个值:

    1、DLL_PROCESS_ATTACH:
    当DLL被进程 第一次 调用时,导致DllMain函数被调用,同时ul_reason_for_call的值为DLL_PROCESS_ATTACH,如果同一个进程后来再次调用此DLL时,操作系统只会增加DLL的使用次数,不会再用DLL_PROCESS_ATTACH调用DLL的DllMain函数。
    2、DLL_PROCESS_DETACH:
    当DLL被从进程的地址空间解除映射时,系统调用了它的DllMain,传递的ul_reason_for_call值是DLL_PROCESS_DETACH。
    ★如果进程的终结是因为调用了TerminateProcess,系统就不会用DLL_PROCESS_DETACH
    来调用DLL的DllMain函数。这就意味着DLL在进程结束前没有机会执行任何清理工作。
    3、DLL_THREAD_ATTACH:
    当进程创建一线程时,系统查看当前映射到进程地址空间中的所有DLL文件映像,并用值DLL_THREAD_ATTACH调用DLL的DllMain函数。 新创建的线程负责执行这次的DLL的DllMain函数,只有当所有的DLL都处理完这一通知后,系统才允许线程开始执行它的线程函数。
    4、DLL_THREAD_DETACH:
    如果线程调用了ExitThread来结束线程(线程函数返回时,系统也会自动调用ExitThread),系统查看当前映射到进程空间中的所有DLL文件映像,并用DLL_THREAD_DETACH来调用DllMain函数,通知所有的DLL去执行线程级的清理工作。

    注意:如果线程的结束是因为系统中的一个线程调用了TerminateThread,系统就不会用值DLL_THREAD_DETACH来调用所有DLL的DllMain函数。


    下面要说的就是dll的导出函数:

    两种方式:*.def 文件或 __declspec(dllexport) 关键字:

    1、def文件

    看一下实例

    LIBRARY   "DLLTest"
    EXPORTS
       add   @1
       fun   @2
    其中,LIBRARY,他将def文件标识为属于dll。

    EXPORTS 是列出名称   也能列出你导出函数的序号。 看上面的代码你能理解。

    要注意,在这里你要加注释就不是//的方式了,而是分号:  ;  。


    2、_declspec(dllexport)导出

    extern "C" _declspec(dllexport) int add(int a, int b);

    这里的dllexport  你可以把它换成dllimport,这个意思是你要进行函数的导入。


    OK,下面我们来看实例代码:

    // dll.cpp : 定义 DLL 应用程序的导出函数。
    //
    
    #include "stdafx.h"
    
    int add(int a, int b)
    {
    	return a+b;
    }
    
    ;def文件
    LIBRARY "dll"
    EXPORTS
      add @1
    
    或者用第二种方式:

    #pragma once
    
    extern "C" _declspec(dllexport)int add(int a, int b);


    我们用depends 来查看一下这个动态链接库导出的函数:



    我们编写了dll文件,把函数导出了,但是我们导出的函数必须有人去调用, 现在我们来看一下 如何调用dll文件。

    调用dll有两种方式: 隐式连接   显示连接

    1、隐式链接

    主要是由编译器对dll进行加载和卸载。如果程序结束时如果还有其他应用程序使用该DLL,那么系统会使DLL的使用计数减1,当DLL的使用计数降为0时,会将DLL从内存中删除。

    使用方法:

    //#include "头文件"
    #pragma comment(lib, "***.lib")
    _declspec(dllimport) 函数声明


    看一下测试的具体代码:

    #include<iostream>
    
    //隐式链接
    //#include "../dll/标头.h"   //当使用dllexport时使用
    #pragma comment(lib, "..\\Debug\\dll.lib")
    _declspec(dllimport)int dec(int a, int b);//当使用def文件时使用
    _declspec(dllimport)int add(int a, int b);
    ///////////////////////////////////////
    
    using namespace std;
    
    int main(void)
    {
    	int m = 0;
    	m = dec(6, 3);
    	cout << m << endl;
    	m = add(6, 4);
    	cout << m << endl;
    	system("pause");
    	return 0;
    }



    2、显示连接

    他主要是由程序员来加载和卸载。

    这里我们要用到几个函数:

    LoadLibrary(...):该 API 用于加载指定的DLL;
    GetProcAddress(...):该 API 用于获取DLL中导出函数的指针, 即导出函数的入口点;
    FreeLibrary(...):该 API 用于卸载指定的DLL。


    下面看一下如何来使用它们:

    typedef int(*DEC_FUNC)(int a, int b);
    
    HMODULE  hMod = LoadLibrary("链接库");
    if(hMod)
    {
    	DEC_FUNC dec_fp = (DEC_FUNC)GetProcAddress(hMod, "函数名");
    	if(dec_fp)
    	{
    		dec_fp();//使用函数
    	}
    	FreeLibrary(hMod);
    }

    DEC_FUNC函数指针应该不模式了吧,在【C/C++学习】之七、指向函数的指针 有介绍。


    我们看一下测试代码:

    //#include<iostream>
    //
    ////隐式链接
    ////#include "../dll/标头.h"   //当使用dllexport时使用
    //#pragma comment(lib, "..\\Debug\\dll.lib")
    //_declspec(dllimport)int dec(int a, int b);//当使用def文件时使用
    //_declspec(dllimport)int add(int a, int b);
    /////////////////////////////////////////
    //
    //using namespace std;
    //
    //int main(void)
    //{
    //	int m = 0;
    //	m = dec(6, 3);
    //	cout << m << endl;
    //	m = add(6, 4);
    //	cout << m << endl;
    //	system("pause");
    //	return 0;
    //}
    
    #include<iostream>
    #include<Windows.h>
    using namespace std;
    
    typedef int(*DEC_FUNC)(int a, int b);
    
    int main(void)
    {
    	HMODULE hFun = LoadLibrary("..//Debug//dll.dll");
    	if(hFun)
    	{
    		DEC_FUNC dec_func = (DEC_FUNC)GetProcAddress(hFun, "dec"); //按照函数名称
    		DEC_FUNC add_func = (DEC_FUNC)GetProcAddress(hFun, MAKEINTRESOURCEA(1));//按照函数的导出序号,在def文件
    
    		if(add_func)
    		{
    			cout << add_func(2, 4) << endl;
    		}
    
    		if(dec_func)
    		{
    			cout << dec_func(3, 1) << endl;
    		}
    		FreeLibrary(hFun);
    	}
    	system("pause");
    }


    MFC中

    在MFC中,DLL 的入口点也是DLLMain函数。

    在MFC中,当exe文件退出的时候,dll会调用ExitInstance函数,当exe初始化调用dll的时候,dll会默认调用InitInstance函数。

    1、静态链接

    与mfc库静态链接,这里会将mfc类库的代码直接编译生成DLL文件中,调用这种DLL的接口的时候,MFC使用DLL的资源,这样就不需要模块状态的切换。但是用这种方式生成的dll比较大。

    2、动态链接

    调用DLL的exe文件同时连接到mfc库,用的时候就来链接, exe文件的资源句柄加载资源末班,当dll和exe程序中有相同的id资源的时候,必须进行模块的切换。  而静态是不需要模块状态切换的。


    下面来看那一下如何进行模块的切换

    有三种方法:1、AFX_MANAGE_STATE(AfxGetStaticModuleState());   //执行到这里,把资源的主句柄转换到dll中

          2、 HINSTANCE hSaveInst = AfxGetResourceHandle();//取得当前应用程序句柄  存到hSaveInst中
    AfxSetResourceHandle(theApp.m_hInstance);//设置成在dll中找资源
    ... ... //执行语句;
    AfxSetResourceHandle(hSaveInst);//设置回来

                           3、 放到可执行程序中

                                    HINSTANCE hExeInst = GetModuleHandle(NULL);//取得指定模块的句柄  传递NULL 返回当前exe的实例句柄
    HINSTANCE hDLLInst = GetModuleHandle(_T("MFCDLL.dll"));//取得dll的实例句柄
    ASSERT(hExeInst && hDLLInst);//断言
    AfxSetResourceHandle(hDLLInst);//设置成在dll中找资源
    ... ... //执行语句;
    AfxSetResourceHandle(hExeInst);//重新设置回来


    我们的实例是在dll中弹出一个对话框。

    下面的代码是我们在dll文件中弹出对话框的函数:

    void showdlg()
    {
    	//模块切换
    	//******************************************
    	AFX_MANAGE_STATE(AfxGetStaticModuleState());  //在DLL 中找资源
    	//******************************************
    	CDialog dlg(IDD_DIALOG1);
    	dlg.DoModal();
    }
    
    
    //函数的导出
    ; MFCDLL.def : 声明 DLL 的模块参数。
    
    LIBRARY
    
    EXPORTS
        ; 此处可以是显式导出
    	showdlg @1

    在测试文件中:

    #pragma comment(lib, "..//Debug/MFCDLL.lib")
    _declspec(dllimport) void showdlg();
    
    void CmfcceshiDlg::OnBnClickedButton2()
    {
    	// TODO: 在此添加控件通知处理程序代码
    	showdlg();
    }
    
    
    void CmfcceshiDlg::OnBnClickedButton1()
    {
    	// TODO: 在此添加控件通知处理程序代码
    	CAboutDlg dlg;
    	dlg.DoModal();
    }
    



    2012/10/4

    jofranks于南昌


  • 相关阅读:
    用css3弹性盒子模型实现九宫格布局
    css布局之左中右结构总结
    内联块inline-block的垂直间隙问题
    Jquery选择器 讲解
    U大师装系统
    陈伟:软件趋势——云物移大智
    asp网站发布步骤总结
    MYfirst
    ASP.net 利用From实现上传文件
    【ASP.NET Core CentOS 发布】(四)在CentOS上安装.NET Core运行时、部署到CentOS
  • 原文地址:https://www.cnblogs.com/java20130723/p/3211393.html
Copyright © 2011-2022 走看看