运行时库说明,以及与静态库、动态库、调用程序之间的关系

运行时库说明,以及与静态库、动态库、调用程序之间的关系

一、什么是运行时库

         运行时库是程序在运行时所需 要的库文件,通常运行时库是以LIBDLL形式提供的。运行时库除了给我们提供必要的库函数调用(如memcpyprintfmalloc等)之外,它提供的另一个最重要的功能是为应用程序添加启动函 数。  

         运行时库启动函数的主要功能为进行程序的初始化,对全局变量进行赋初值,加载用户程序的入口函数。

二、编译方式

         运行时库的编译方式有四种:

                   动态编译:MD(release),MDd(debug)

                   静态编译:MT(release),MTd(debug)

         此处的动态、静态编译和工程是libdll还是exe没有什么关系,是指定运行时库以什么方式让你的工程(libdllexe)调用。如果是动态编译,则大家共同使用一个运行时库dll;如果是静态编译,编译器会把运行时库lib集成进工程(libdllexe)

         1、动态编译MD(release),MDd(debug)

         MFC库和运行时库是两种库,但必须使用相同方式编译。如果使用动态编译,在没有运行环境的情况下程序会提示缺少mfc80d.dllmsvcr80d.dllmsvcp80d.dll三个动态库(vc2005debug方式):

mfc80d.dllMFC动态库

msvcr80d.dllC运行时库。提供fopenprintfC函数。

msvcp80d.dllC++运行时库,#include <iostream>后会要求此dll。提供fgetsSTL库等。

以上三个动态库可用VC工具Depends导入来查看具体提供了什么函数。

         2、静态编译MT(release),MTd(debug)

         编译器会把如下lib集成进工程:

Reusable Library

Switch

Library

Macro(s) Defined

Single Threaded

/ML

LIBC.LIB

(none)

Static MultiThread

/MT

LIBCMT.LIB

_MT

Dynamic Link (DLL)

/MD

MSVCRT.LIB

_MT and _DLL

Debug Single Threaded

/MLd

LIBCD.LIB

_DEBUG

Debug Static MultiThread

/MTd

LIBCMTD.LIB

_DEBUG and _MT

Debug Dynamic Link (DLL)

/MDd

MSVCRTD.LIB

_DEBUG, _MT, and _DLL

注:从Visual C++ 2005开始,libcp.liblibcpd.lib(老的/ML/MLd选项)已经被移除。通过/MT/MTd使用libcpmt.liblibcpmtd.lib取代。

三、与静态库、动态库、调用程序之间的关系

         以各种方式编译后的对比:

Lib

调用程序

结果

Dll

调用程序

结果

MT

MT

正常

MT

MT

正常

MT

MD

链接错误,函数重定义

MT

MD

正常

MD

MT

链接错误,函数重定义

MD

MT

正常

MD

MD

正常

MD

MD

正常

MTd

MTd

正常

MTd

MTd

正常

MTd

MDd

链接错误,函数重定义

MTd

MDd

正常

MDd

MTd

链接错误,函数重定义

MDd

MTd

正常

MDd

MDd

正常

MDd

MDd

正常

         可见,使用静态库封装时需要向调用者提供4种编译版本,或者向调用者说明本静态库是什么编译方式。

         注:DLL和调用程序任何一方进行静态编译后,dll内部new的内存指针在调用程序里delete时会报“堆被破坏”的错,所以两者必须同是动态编译才能dll内部new外部delete,原因是new/deletemalloc/free函数都是运行时库提供的,库内部保存了一个进程默认堆的句柄,同是动态编译时会使用同一个堆句柄,如果某一方是静态编译,双方就不会使用同一个堆句柄(静态编译后工程都会调用其本身的运行时库lib),外部delete时使用的堆句柄不一致导致提示堆被破坏。如遇到此问题可以使用HeapCreate系列函数,此系列函数会创建一个自定义堆,dll和工程都可以使用这个自定义堆。但建议不要这样做,应该谁申请的内存谁去释放。

         附一个HeapCreate系列函数的使用例子:

         //创建一个自定义堆

         HANDLE  myheap = HeapCreate(HEAP_GENERATE_EXCEPTIONS,4096,4096*64);

         //在自定义堆上分配内存。

         int *head = (int *)HeapAlloc(myheap,HEAP_ZERO_MEMORY|HEAP_GENERATE_EXCEPTIONS,sizeof(int));

         //释放自定义堆上分配的内存。

         HeapFree(myheap,HEAP_NO_SERIALIZE,headTemp);

         //释放自定义堆

         HeapDestroy(myheap);

         //获取当前进程默认堆句柄,使用此句柄可直接用HeapAlloc/ HeapFree操作默认堆。

         HANDLE  myheap = GetProcessHeap()

 

另附一个题外话:

         ADO等com对象在被封装到DLL后(ado对象为全局对象,函数内没有问题),调用dll的程序一定要使用动态加载dll(自己控制dll的卸载),否则会出现程序在退出时崩溃的问题。我调试中发现程序(MFC)在退出后会先隐式调用CoUninitialize()函数关闭主线程的com库,然后才卸载DLL,此时如果dll内部有全局com对象在调用release等释放com资源的操作就会引起程序崩溃。

         EXE(MFC)静态加载DLL的顺序是:

         运行时:

         1、DLL:DLL全局类对象首先被构造。

         2、DLL:初始化DLL,DllMain函数的参数ul_reason_for_call值为DLL_PROCESS_ATTACH。

     3、EXE:调用APP类构造函数。

     4、EXE:调用EXE全局类对象构造函数。

     5、EXE:执行APP类initinstance-》等一系列程序初始化操作开始,程序开始运行。

     退出时:

     1、EXE:各个类析构-》调用APP类exitInstance。

     2、EXE:EXE全局类对象析构。

     3、EXE:APP类析构。

     4、DLL:调用DllMain函数的参数ul_reason_for_call值为DLL_PROCESS_DETACH。

     5、DLL:DLL全局类对象析构。

对于com最好是每个线程的开始一次CoInitialize,退出的时候一次CoUninitialize,但如果有其他模块在调用CoUninitialize,本模块就会出问题,我又不能告诉调用者自己去CoInitialize和CoUninitialize,所以我只CoInitialize不CoUninitialize,这样不知道会有什么问题,我操作数据库会用到ADO,目前没什么问题。

 

DLL主函数

BOOL APIENTRY DllMain( HMODULE hModule,//本dll句柄

                       DWORD  ul_reason_for_call,//调用原因

                       LPVOID lpReserved//保留)

{

     switch (ul_reason_for_call)

     {

     case DLL_PROCESS_ATTACH://进程第一次调用本dll时会进入。

         break;

     case DLL_THREAD_ATTACH://每创建一个线程都会进入。

         break;

     case DLL_THREAD_DETACH://每个线程返回时会进入,异常中断、调用TerminateThread退出的线程不会进入。

         break;

     case DLL_PROCESS_DETACH://进程退出后会进入。

         break;

     }

    return TRUE;

}

 

DLL动态加载

     //载入dll

     HINSTANCE mydll = LoadLibrary("D:\\mydll.dll");

     //声明dll中函数类型,类型是一个函数指针。

     typedef bool (*dllfunType)(int a, int b);

     //取出dll中函数地址,并赋值给一个函数指针。参数是dll句柄,参数是dll中实际的函数名称。

     //函数的实际名称要看dll的函数导出方式,以C方式导出就是函数名,其他方式导出请用VC工具Depends查看。

     dllfunType dbcon = (dllfunType)GetProcAddress(mydll, "dbcon");

     //调用函数指针。

     dbcon(1,2);

     //释放dll资源

     FreeLibrary(mydll);

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值