Dll原理和使用

原创 2007年10月09日 09:24:00


Dll原理和使用

 

  • 引言
  • 调用方式
  • MFC中的DLL
  • DLL入口函数
  • 关于约定
  • 关于DLL的函数
  • 模块定义文件(.DEF)
  • DLL程序和调用其输出函数的程序的关系

。。。


引言

 

比较大的应用程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件系统的工作。可能存在一些模块的功能较为通用,在构造其它软件系统时仍会被使用。在构造软件系统时,如果将所有模块的源代码都静态编译到整个应用程序EXE文件中,会产生一些问题:一个缺点是增加了应用程序的大小,它会占用更多的磁盘空间,程序运行时也会消耗较大的内存空间,造成系统资源的浪费;另一个缺点是,在编写大的EXE程序时,在每次修改重建时都必须调整编译所有源代码,增加了编译过程的复杂性,也不利于阶段性的单元测试

 

Windows系统平台上提供了一种完全不同的较有效的编程和运行环境,你可以将独立的程序模块创建为较小的 DLL(Dynamic Linkable Library)文件,并可对它们单独编译和测试。在运行时,只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中。这种方式不仅减少了 EXE文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用程序使用。Windows自己就将一些主要的系统功能以 DLL模块的形式实现。

 

一般来说,DLL是一种磁盘文件,以.dll.DRV.FON.SYS和许多以.EXE为扩展名的系统文件都可以是DLL。它由全局数据服务函数资源组成,在运行时被系统加载到进程的虚拟空间中,成为调用进程的一部分。如果与其它DLL之间没有冲突,该文件通常映射到进程虚拟空间的同一地址上。DLL模块中包含各种导出函数,用于向外界提供服务。DLL可以有自己的数据段,但没有自己的堆栈,使用与调用它的应用程序相同的堆栈模式;一个DLL在内存中只有一个实例;DLL实现了代码封装性;DLL的编制与具体的编程语言及编译器无关。

Win32环境中,每个进程都复制了自己的读/写全局变量。如果想要与其它进程共享内存,必须使用内存映射文件或者声明一个共享数据段。DLL模块需要的堆栈内存都是从运行进程的堆栈中分配出来的。Windows在加载DLL模块时将进程函数调用与DLL文件的导出函数相匹配。Windows操作系统对 DLL的操作仅仅是把DLL映射到需要它的进程的虚拟地址空间里去。DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有.


调用方式

                                                      

1、静态调用方式:编译系统完成对DLL的加载和应用程序结束时DLL卸载的编码(如还有其它程序使用该DLL,则WindowsDLL的应用记录减1,直到所有相关程序都结束对该DLL的使用时才释放它),简单实用,但不够灵活,只能满足一般要求。

 

隐式的调用:需要把产生动态连接库时产生的.LIB文件加入到应用程序的工程中,想使用DLL中的函数时,只须说明一下。隐式调用不需要调用LoadLibrary()FreeLibrary()。程序员在建立一个DLL文件时,链接程序会自动生成一个与之对应的 LIB导入文件。该文件包含了每一个DLL导出函数的符号名和可选的标识号,但是并不含有实际的代码。LIB文件作为DLL的替代文件被编译到应用程序项目中。

当程序员通过静态链接方式编译生成应用程序时,应用程序中的调用函数与LIB文件中导出符号相匹配,这些符号或标识号进入到生成的EXE文件中。LIB文件中也包含了对应的DLL文件名(但不是完全的路径名),链接程序将其存储在EXE文件内部。

当应用程序运行过程中需要加载DLL文件时,Windows根据这些信息发现并加载DLL,然后通过符号名或标识号实现对DLL函数的动态链接。所有被应用程序调用的DLL文件都会在应用程序EXE文件加载时被加载在到内存中。可执行程序链接到一个包含DLL输出函数信息的输入库文件(.LIB文件)。操作系统在加载使用可执行程序时加载DLL。可执行程序直接通过函数名调用DLL的输出函数,调用方法和程序内部其他的函数是一样的。

 

2、动态调用方式:是由编程者用API函数加载和卸载DLL来达到调用DLL的目的,使用上较复杂,但能更加有效地使用内存,是编制大型应用程序时的重要方式。

显式的调用:是指在应用程序中用LoadLibraryMFC提供的AfxLoadLibrary显式的将自己所做的动态连接库调进来,动态连接库的文件名即是上面两个函数的参数,再用GetProcAddress()获取想要引入的函数。自此,你就可以象使用如同本应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前,应该用FreeLibraryMFC提供的AfxFreeLibrary释放动态连接库。直接调用Win32 LoadLibary函数,并指定DLL的路径作为参数。LoadLibary返回HINSTANCE参数,应用程序在调用 GetProcAddress函数时使用这一参数。GetProcAddress函数将符号名或标识号转换为DLL内部的地址。程序员可以决定DLL文件何时加载或不加载,显式链接在运行时决定加载哪个DLL文件。使用DLL的程序在使用之前必须加载(LoadLibrary)加载DLL从而得到一个 DLL模块的句柄,然后调用GetProcAddress函数得到输出函数的指针,在退出之前必须卸载DLL(FreeLibrary)

 

Windows将遵循下面的搜索顺序来定位DLL

1.包含EXE文件的目录

2.进程的当前工作目录

3Windows系统目录

4Windows目录

5.列在Path环境变量中的一系列目录


MFC中的DLL

 

aNon-MFC DLL:指的是不用MFC类库结构,直接用C语言写的DLL,其输出的函数一般用的是标准C接口,并能被非MFCMFC编写的应用程序所调用。

bRegular DLL:和下述的Extension DLLs一样,是用MFC类库编写的。明显的特点是在源文件里有一个继承CWinApp的类。其又可细分成静态连接到MFC动态连接到MFC上的。

静态连接到MFC的动态连接库只被VC的专业般和企业版所支持。该类DLL应用程序里头的输出函数可以被任意Win32程序使用,包括使用MFC的应用程序。输入函数有如下形式:

extern "C" EXPORT YourExportedFunction( );

如果没有extern "C"修饰,输出函数仅仅能从C++代码中调用。

DLL应用程序从CWinApp派生,但没有消息循环。

动态链接到MFC的规则DLL应用程序里头的输出函数可以被任意Win32程序使用,包括使用MFC的应用程序。但是,所有从DLL输出的函数应该以如下语句开始:

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

此语句用来正确地切换MFC模块状态。

Regular DLL能够被所有支持DLL技术的语言所编写的应用程序所调用。在这种动态连接库中,它必须有一个从CWinApp继承下来的类,DLLMain函数被MFC所提供,不用自己显式的写出来。

cExtension DLL:用来实现从MFC所继承下来的类的重新利用,也就是说,用这种类型的动态连接库,可以用来输出一个从MFC所继承下来的类。它输出的函数仅可以被使用MFC且动态链接到MFC的应用程序使用。可以从 MFC继承你所想要的、更适于你自己用的类,并把它提供给你的应用程序。你也可随意的给你的应用程序提供MFCMFC继承类的对象指针。 Extension DLL使用MFC的动态连接版本所创建的,并且它只被用MFC类库所编写的应用程序所调用。Extension DLLs Regular DLLs不一样,它没有一个从CWinApp继承而来的类的对象,所以,你必须为自己DLLMain函数添加初始化代码结束代码

规则DLL相比,有以下不同

1、它没有一个从CWinApp派生的对象;

2、它必须有一个DLLMain函数;

3DLLMain调用AfxInitExtensionModule函数,必须检查该函数的返回值,如果返回0DLLMmain也返回0

4、如果它希望输出CRuntimeClass类型的对象或者资源(Resources),则需要提供一个初始化函数来创建一个CDynLinkLibrary对象。并且,有必要把初始化函数输出;

5、使用扩展DLLMFC应用程序必须有一个从CWinApp派生的类,而且,一般在InitInstance里调用扩展DLL的初始化函数


DLL入口函数

 

1、每一个DLL必须有一个入口点,DLLMain是一个缺省的入口函数。DLLMain负责初始化 (Initialization)结束(Termination) 工作,每当一个新的进程或者该进程的新的线程访问DLL时,或者访问DLL的每一个进程或者线程不再使用DLL或者结束时,都会调用DLLMain。但是,使用TerminateProcessTerminateThread结束进程或者线程,不会调用DLLMain

DLLMain的函数原型:

BOOL APIENTRY DLLMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID
lpReserved)
{
        switch(ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        .......
        case DLL_THREAD_ATTACH:
        .......
        case DLL_THREAD_DETACH:
        .......
        case DLL_PROCESS_DETACH:
        .......
        return TRUE;
        }
}

参数:

hMoudle:是动态库被调用时所传递来的一个指向自己的句柄(实际上,它是指向_DGROUP段的一个选择符)

ul_reason_for_call:是一个说明动态库被调原因的标志。当进程或线程装入或卸载动态连接库的时候,操作系统调用入口函数,并说明动态连接库被调用的原因。它所有的可能值为:

DLL_PROCESS_ATTACH: 进程被调用;

DLL_THREAD_ATTACH: 线程被调用;

DLL_PROCESS_DETACH: 进程被停止;

DLL_THREAD_DETACH: 线程被停止;

lpReserved:是一个被系统所保留的参数。

2_DLLMainCRTStartup

为了使用"C"运行库(CRTC Run time Library)DLL版本(多线程),一个DLL应用程序必须指定_DLLMainCRTStartup为入口函数,DLL的初始化函数必须是DLLMain

_DLLMainCRTStartup完成以下任务:当进程或线程捆绑(Attach)DLL时为"C"运行时的数据(C Runtime Data)分配空间和初始化并且构造全局"C++"对象,当进程或者线程终止使用DLL(Detach)时,清理C Runtime Data并且销毁全局"C++"对象。它还调

DLLMainRawDLLMain函数。

RawDLLMainDLL应用程序动态链接到MFC DLL时被需要,但它是静态的链接到DLL应用程序的。在讲述状态管理时解释其原因。


关于约定

 

动态库输出函数的约定有两种:调用约定名字修饰约定

1)调用约定(Calling convention):决定函数参数传送时入栈出栈顺序,由调用者还是被调用者把参数弹出栈,以及编译器用来识别函数名字的修饰约定。

函数调用约定有多种,这里简单说一下:

1__stdcall调用约定相当于16位动态库中经常使用的PASCAL调用约定。在32位的VC++5.0 PASCAL调用约定不再被支持(实际上它已被定义为__stdcall。除了__pascal外,__fortran__syscall也不被支持),取而代之的是__stdcall调用约定。两者实质上是一致的,即函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明)。

_stdcallPascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。

2C调用约定(即用__cdecl关键字说明)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。

_cdeclCC++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。

3__fastcall调用约定是""如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECXEDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。

_fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。

4thiscall仅仅应用于"C++"成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。

5naked call采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESIEDIEBXEBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。

naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。

关键字 __stdcall__cdecl__fastcall可以直接加在要输出的函数前,也可以在编译环境的Setting.../C/C++ /Code Generation项选择。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/Gz /Gd/Gr。缺省状态为/Gd,即__cdecl

要完全模仿PASCAL调用约定首先必须使用__stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是WINAPI宏,Windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在WIN32中,它被定义为 __stdcall。使用WINAPI宏可以创建自己的APIs

2)名字修饰约定

1、修饰名(Decoration name)

"C"或者"C++"函数在内部(编译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的,如在模块定义文件里头指定输出"C++"重载函数、构造函数、析构函数,又如在汇编代码里调

"C"""C++"函数等。

修饰名由函数名、类名、调用约定、返回类型、参数等共同决定。

2名字修饰约定调用约定编译种类(CC++)不同变化。函数名修饰约定随编译种类和调用约定的不同而不同,下面分别说明。

aC编译时函数名修饰约定规则:

__stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个"@"符号和其参数的字节数,格式为_functionname@number

__cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname

 

__fastcall调用约定在输出函数名前加上一个"@"符号,后面也是一个"@"符号和其参数的字节数,格式为@functionname@number

它们均不改变输出函数名中的字符大小写,这和PASCAL调用约定不同,PASCAL约定输出的函数名无任何修饰且全部大写。

bC++编译时函数名修饰约定规则:

__stdcall调用约定:

1、以"?"标识函数名的开始,后跟函数名;

2、函数名后面以"@@YG"标识参数表的开始,后跟参数表;

3、参数表以代号表示:

X--void

D--char

E--unsigned char

F--short

H--int

I--unsigned int

J--long

K--unsigned long

M--float

N--double

_N--bool

....

PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复;

4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;

5、参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。

其格式为"?functionname@@YG*****@Z""?functionname@@YG*XZ",例如

          int Test1char *var1,unsigned long-----"?Test1@@YGHPADK@Z"
          void Test2
()                       -----"?Test2@@YGXXZ"

__cdecl调用约定:

规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"

__fastcall调用约定:

规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YI"

VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用.


关于DLL的函数

 

动态链接库中定义有两种函数:导出函数(export function)内部函数(internal function)。导出函数可以被其它模块调用,内部函数在定义它们的DLL程序内部使用。

输出函数的方法有以下几种:

1、传统的方法

在模块定义文件的EXPORT部分指定要输入的函数或者变量。语法格式如下:

entryname[=internalname] [@ordinal[NONAME]] [DATA] [PRIVATE]

其中:

entryname是输出的函数或者数据被引用的名称;

internalnameentryname;

@ordinal表示在输出表中的顺序号(index)

NONAME仅仅在按顺序号输出时被使用(不使用entryname);

DATA表示输出的是数据项,使用DLL输出数据的程序必须声明该数据项为_declspec(DLLimport)

上述各项中,只有entryname项是必须的,其他可以省略。

对于"C"函数来说,entryname可以等同于函数名;但是对"C++"函数(成员函数、非成员函数)来说, entryname是修饰名。可以从.map映像文件中得到要输出函数的修饰名,或者使用DUMPBIN /SYMBOLS得到,然后把它们写在.def文件的输出模块。DUMPBINVC提供的一个工具。

如果要输出一个"C++"类,则把要输出的数据和成员的修饰名都写入.def模块定义文件。

 

2、在命令行输出

对链接程序LINK指定/EXPORT命令行参数,输出有关函数。

 

3、使用MFC提供的修饰符号_declspec(DLLexport)

在要输出的函数、类、数据的声明前加上_declspec(DLLexport)的修饰符,表示输出。 __declspec(DLLexport)C调用约定、C编译情况下可以去掉输出函数名的下划线前缀。extern "C"使得在C++中使用C编译方式成为可能。在"C++"下定义"C"函数,

需要加extern "C"关键词。用extern "C"来指明该函数使用C编译方式。输出的"C"函数可以从"C"代码里调用。

 

例如,在一个C++文件中,有如下函数:

    extern "C" {void  __declspec(DLLexport) __cdecl Test(int var);}

其输出函数名为:Test

 

MFC提供了一些宏,就有这样的作用。

AFX_CLASS_IMPORT__declspec(DLLexport)
 
AFX_API_IMPORT
__declspec(DLLexport)
 
AFX_DATA_IMPORT
__declspec(DLLexport)
 
AFX_CLASS_EXPORT
__declspec(DLLexport)
 
AFX_API_EXPORT
__declspec(DLLexport)
 
AFX_DATA_EXPORT
__declspec(DLLexport)
 
AFX_EXT_CLASS
#ifdef _AFXEXT
                       AFX_CLASS_EXPORT
               #else
                       AFX_CLASS_IMPORT
 
AFX_EXT_API
#ifdef _AFXEXT
                        AFX_API_EXPORT
            #else
                        AFX_API_IMPORT
 
AFX_EXT_DATA
#ifdef _AFXEXT
                         AFX_DATA_EXPORT
             #else
                         AFX_DATA_IMPORT

AFX_EXT_CLASS这样的宏,如果用于DLL应用程序的实现中,则表示输出(因为_AFX_EXT被定义,通常是在编译器的标识参数中指定该选项/D_AFX_EXT);如果用于使用DLL的应用程序中,则表示输入(_AFX_EXT没有定义)。

要输出整个的类,对类使用_declspec(_DLLexpot);要输出类的成员函数,则对该函数使用_declspec(_DLLexport)。如:

class AFX_EXT_CLASS CTextDoc : public CDocument
{
        ...
}

extern "C" AFX_EXT_API void WINAPI InitMYDLL();

这几种方法中,最好采用第三种,方便好用;其次是第一种,如果按顺序号输出,调用效率会高些;最次是第二种。


模块定义文件(.DEF)

 

模块定义文件(.DEF)是一个或多个用于描述DLL属性的模块语句组成的文本文件,每个DEF文件至少必须包含以下模块定义语句:

* 第一个语句必须是LIBRARY语句,指出DLL的名字;

* EXPORTS语句列出被导出函数的名字;将要输出的函数修饰名罗列在EXPORTS之下,这

个名字必须与定义函数的名字完全一致,如此就得到一个没有任何修饰的函数名了。

* 可以使用DESCRIPTION语句描述DLL的用途(此句可选)

* ";"对一行进行注释(可选)


DLL程序和调用其输出函数的程序的关系

 

1DLL与进程、线程之间的关系

DLL模块被映射到调用它的进程的虚拟地址空间。

DLL使用的内存从调用进程的虚拟地址空间分配,只能被该进程的线程所访问。

DLL的句柄可以被调用进程使用;调用进程的句柄可以被DLL使用。

DLL使用调用进程的栈。

 

2、关于共享数据段

DLL定义的全局变量可以被调用进程访问;DLL可以访问调用进程的全局数据。使用同一DLL的每一个进程都有自己的DLL全局变量实例。如果多个线程并发访问同一变量,则需要使用同步机制;对一个DLL的变量,如果希望每个使用DLL的线程都有自己的值,则应该使用线程局部存储(TLSThread Local Strorage)

在程序里加入预编译指令,或在开发环境的项目设置里也可以达到设置数据段属性的目的.必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。

 

以下为转载

Visual Leak Detector - Enhanced Memory Leak Detection for Visual C++
By
Dan Moulding

A memory leak detector for Visual C++ packaged in an easy to use library

Sample Image - screenshot.png

What's New

2 May 2005 - VLD 0.9i has been posted. This release fixes several bugs. It also includes support for Windows x64. The libraries available for download are still 32-bit only. To take advantage of the 64-bit support, you'll need to build the 64-bit libraries from the source. Bugs fixed in this release are:

  • VLD does not report memory leaks that are the result of a failure to free memory allocated via a call to realloc().
  • In multithreaded programs, if the program's main thread terminates before other threads in the same process, then VLD may cause an access violation while checking for memory leaks.
  • If VLD cannot find the source file and line number information for a program address, the last known file and line number will be repeated in the call stack section of the memory leak report. The correct behavior should be for VLD to print "File and line number not available" for that call stack entry.

Introduction

This memory leak detector is superior, in a number of ways, to the memory leak detection provided natively by MFC or the Microsoft C runtime library. First, built-in leak detection for non-MFC applications is not enabled by default. It can be enabled, but implementing it is not very intuitive and (for Visual C++ 6.0 at least) it doesn't work correctly unless you fix a couple of bugs in the C runtime library yourself. And even when it is working correctly its capabilities are somewhat limited. Here is a short list of capabilities that Visual Leak Detector provides that the built-in leak detection does not:

  • Provides a complete stack trace for each leaked block, including source file and line number information when available.
  • Provides complete data dumps (in hex and ASCII) of leaked blocks.
  • Customizable level of detail in the memory leak report via preprocessor macro definitions. Using macros allows you to make these customizations via your project's build settings, instead of having to modify any source code.

Okay, but how is Visual Leak Detector better than the dozens of other leak detectors here?

  • Visual Leak Detector is now packaged as an easy-to-use library. Just copy the library to the appropriate directory and include a single header.
  • In addition to providing stack traces with source files, line numbers and function names, Visual Leak Detector also provides data dumps.
  • It works with both C and C++ programs (compatible with both malloc/free and new/delete).
  • It is well documented, so it is easy to modify it to suit your needs if you so desire.

In addition to the features mentioned, it also provides most of the "standard" leak detection features. A memory leak report is dumped to the debug output window. From there you can see a summary of each leak detected and double-clicking a source file/line number in the debug output window will take you to that file and line in the editor.

How To Use Visual Leak Detector

Earlier versions of Visual Leak Detector (VLD) had a design that required absolutely no modification of the source files being debugged. But to acheive this, VLD had to be compiled into your program from source. It turned out that building VLD from source was, for most people, the hardest part about using it.

So to make life easier, VLD is now packaged as a library. This means that there is no longer a need to build VLD from source. But it also means that there is now a single header file that needs to be included in one of your own source files that tells your program to link with the VLD library. But beyond that, you don't need to make any other changes to your sources. In the end, this is a much easier way to use VLD because it eliminates all of the complexity involved with building VLD from source.

To use VLD with your project, follow these simple steps:

  1. Copy the VLD library (*.lib) files to your Visual C++ installation's "lib" subdirectory.
  2. Copy the VLD header (vld.h) file to your Visual C++ installation's "include" subdirectory.
  3. In one of your source files -- preferably the one with your program's main entry point -- add the line #include <vld.h>. It's probably best to add this #include before any of the other #includes in your file (with one important exception), but is not absolutely necessary. In most cases, the specific order will not matter. The one exception is "stdafx.h". If your file includes stdafx.h, then vld.h should be included just after it.
  4. Windows 2000 users will also need to copy dbghelp.dll to the directory where the executable being debugged resides. This would also apply to users of earlier versions of Windows, but I can't guarantee that VLD even works on versions prior to Windows 2000.

When you build debug versions of your program, by including the vld.h header file, your program will be linked with VLD. When you run your program under the Visual C++ debugger, VLD will display a memory leak report in the debugger's output window when your program quits.

If memory leaks were detected, double-clicking on a source file/line number within the memory leak report will jump to that line of source in the editor window. In this way, you can easily navigate the code that led up to the memory allocation that resulted in a memory leak.

When you build release versions of your program, VLD will not be linked into the executable. So it is safe to leave the #include <vld.h> in your source file when doing release builds.

Configuring Visual Leak Detector

There are a few optional preprocessor macros that you can define to contol the level of detail provided in memory leak reports.

  • VLD_MAX_TRACE_FRAMES: By default, Visual Leak Detector will trace back as far as possible the call stack for each block that is allocated. Each frame traced adds additional overhead (in CPU time and memory use) to your debug executable. If you'd like to limit this overhead, you can define this macro to an integer value. The stack trace will stop when it has traced this number of frames. The frame count includes the "useless" frames which, by default, are not displayed in the debug output window (see VLD_SHOW_USELESS_FRAMES below). Usually, there will be about 5 or 6 "useless" frames at the beginning of the call stack. Keep this in mind when using this macro, or you may not see the number of frames you expect.
  • VLD_MAX_DATA_DUMP: Define this macro to an integer value to limit the amount of data displayed in memory block data dumps. When this number of bytes of data has been dumped, the dump will stop. This can be useful if any of the leaked blocks are very large and the debug output window becomes too cluttered. You can define this macro to 0 (zero) if you want to suppress the data dumps altogether.
  • VLD_SHOW_USELESS_FRAMES: By default, only "useful" frames are printed in the call stacks. Frames that are internal to the heap or Visual Leak Detector are not shown. Define this to force all frames of the call stacks to be printed. This macro might be useful if you need to debug Visual Leak Detector itself or if you want to customize it.

How Visual Leak Detector Works

Probably the most important part of Visual Leak Detector (VLD) is the function allochook(). This function gets registered with the C runtime's debug heap as a callback function. Anytime a block of memory is allocated or freed, the debug heap will call this callback function. This is how VLD is able to keep track of allocated memory. Microsoft has provided a method for registering this type of callback function with the debug heap: _CrtSetAllocHook(). If you want to learn more about how this works, it is quite well documented in the MSDN Library. A global instance of class VisualLeakDetector is already created for you. When your program starts, this global object's constructor registers the callback function via _CrtSetAllocHook(), so that it is installed before any of your code runs*. To keep track of each allocated memory block, a simple STL map container is employed to map each block to the call stack that allocated it.

Though allochook() is critical to VLD's ability to track memory allocations, most of the heavy lifting is actually done in a couple of other functions: getstacktrace() and reportleaks(). Of the two, getstacktrace() is probably the most important, and probably also the trickiest to understand and properly implement.

If you're not familiar with assembly language or the organization of the stack, then getstacktrace() will likely be somewhat difficult to understand. In a nutshell, it makes use of Microsoft's StackWalk64() function to produce a stack trace. This function is part of the Debug Help Library which, if you're interested, you can read more about in the MSDN Library. VLD pushes the program counter from each level of the stack onto a simple STL vector container. Then, if leaks are detected, this vector is traversed to reproduce the desired stack trace.

After your program exits, the global instance of class VisualLeakDetector will be destroyed. The destructor calls the other important function, reportleaks(). This function is important because it translates the program counter addresses recorded during the stack walk into meaningful symbol names. It retrieves two pieces of information for each address: the name of the function containing that program address, and the source file and line number that generated the machine code at that address. These two pieces of information allow VLD to produce human-readable call stacks for the memory leak report. reportleaks() also obtains a pointer to the user-data section of the leaked memory block. Using that pointer, it is able to produce a formatted dump of the data contained in the memory block. Seeing the data stored in the block can sometimes be of help in isolating the source of a leak (more so than just a block number or memory address can).

Key Parts of the Source Explained

This is the VisualLeakDetector class constructor. Note the call to _CrtSetAllocHook(). This is a key part of the program which register's our callback function (allochook()) with the debug heap. The call to linkdebughelplibrary() performs an explicit dynamic link with the Debug Help Library (dbghelp.dll). Because VLD is itself a library, implicitly linking with the Debug Help Library through the import library dbghelp.lib is undesirable, because that would make the VLD library dependent on dbghelp.lib, which will not be present on many Windows computers.

// Constructor - Dynamically links with the Debug Help Library and installs the

//   allocation hook function so that the C runtime's debug heap manager will

//   call the hook function for every heap request.

//

VisualLeakDetector::VisualLeakDetector ()

{

    // Initialize private data.

    m_mallocmap  = new BlockMap;

    m_process    = GetCurrentProcess();

    m_symbolpath = NULL;

    m_thread     = GetCurrentThread();

 

    if (linkdebughelplibrary()) {

        // Register our allocation hook function with the debug heap.

        m_poldhook = _CrtSetAllocHook(allochook);

        report("Visual Leak Detector Version "VLD_VERSION" installed./n");

        m_installed = true;

    }

    else {

        report("Visual Leak Detector IS NOT installed!/n");

        m_installed = false;

    }

}

Here we see the code responsible for recording the call stack for each allocated block. This is perhaps the trickiest part of the entire program. This function relies on the architecture-specific function getprogramcounterintelx86()for retrieving the EIP register on Intel x86 processors. But the StackWalk64() function is really at the heart of this function. It is where the true work is done of tracing back through the stack and the program's machine code to find the calling function from each frame on the stack. Without StackWalk64(), this function would likely have to be a lot more complicated. pStackWalk64 is a pointer to the StackWalk64 function exported by dbghelp.dll. Likewise pSymFunctionTableAccess64 and pSymGetModuleBase64 are just pointers to the exported functions from dbghelp.dll.

The assembly code here is absolutely necessary. I've seen other examples similar to this in the past where GetThreadContext() is used to obtain the required register values. That method is totally unsafe, because GetThreadContext(), as its documentation clearly explains, may not be used to obtain a running thread's context. By definition, this means that if a thread wants to get it's own context using GetThreadContext(), it can't. Some other thread would have to obtain it while the target thread is suspended.

// getstacktrace - Traces the stack, starting from this function, as far

//   back as possible. Populates the provided CallStack with one entry for each

//   stack frame traced. Requires architecture-specific code for retrieving

//   the current frame pointer and program counter.

//

//   By default, all stack frames are traced. But the trace can be limited to

//   a maximum number of frames via _VLD_maxtraceframes.

//

//  - callstack (OUT): Pointer to an empty CallStack to be populated with

//    entries from the stack trace. Each frame traced will push one entry onto

//    the CallStack.

//

//  Return Value:

//

//    None.

//

void VisualLeakDetector::getstacktrace (CallStack *callstack)

{

    DWORD         architecture;

    CONTEXT       context;

    unsigned int  count = 0;

    unsigned long framepointer;

    STACKFRAME64  frame;

    unsigned long programcounter;

 

    // Get the required values for initialization of the STACKFRAME64 structure

    // to be passed to StackWalk64(). Required fields are AddrPC and AddrFrame.

#ifdef _M_IX86

    architecture = IMAGE_FILE_MACHINE_I386;

    programcounter = getprogramcounterintelx86();

    __asm mov [framepointer], ebp  // Get the frame pointer (aka base pointer)

#else

// If you want to retarget Visual Leak Detector to another processor

// architecture then you'll need to provide architecture-specific code to

// retrieve the current frame pointer and program counter in order to initialize

// the STACKFRAME64 structure below.

#error "Visual Leak Detector is not supported on this architecture."

#endif // _M_IX86

 

    // Initialize the STACKFRAME64 structure.

    memset(&frame, 0x0, sizeof(frame));

    frame.AddrPC.Offset    = programcounter;

    frame.AddrPC.Mode      = AddrModeFlat;

    frame.AddrFrame.Offset = framepointer;

    frame.AddrFrame.Mode   = AddrModeFlat;

 

    // Walk the stack.

    while (count < _VLD_maxtraceframes) {

        count++;

        if (!pStackWalk64(architecture, m_process, m_thread, &frame, &context,

                          NULL, pSymFunctionTableAccess64, pSymGetModuleBase64, NULL)) {

            // Couldn't trace back through any more frames.

            break;

        }

        if (frame.AddrFrame.Offset == 0) {

            // End of stack.

            break;

        }

 

        // Push this frame's program counter onto the provided CallStack.

        callstack->push_back(frame.AddrPC.Offset);

    }

}

And here is the function that retrieves the EIP register. This has to be done as a separate function because there is no way for software to directly read the EIP register. But the same value can be obtained by making a function call, and then from the called function getting the return address (which will be the exact same value as the EIP register from where the function was called).

// getprogramcounterintelx86 - Helper function that retrieves the program

//   counter (aka the EIP register) for getstacktrace() on Intel x86

//   architecture. There is no way for software to directly read the EIP

//   register. But it's value can be obtained by calling into a function (in our

//   case, this function) and then retrieving the return address, which will be

//   the program counter from where the function was called.

//

//  Note: Inlining of this function must be disabled. The whole purpose of this

//    function's existence depends upon it being a *called* function.

//

//  Return Value:

//

//    Returns the return address of the current stack frame.

//

#ifdef _M_IX86

#pragma auto_inline(off)

unsigned long VisualLeakDetector::getprogramcounterintelx86 ()

{

    unsigned long programcounter;

 

    __asm mov eax, [ebp + 4]        // Get the return address out of the current stack frame

    __asm mov [programcounter], eax // Put the return address into the variable we'll return

 

    return programcounter;

}

#pragma auto_inline(on)

#endif // _M_IX86

Next, we have a snippet that shows how the symbols are retrieved for the addresses obtained during the stack walk. Note that this code is only run if memory leaks are detected. This avoids having to do symbol lookups on-the-fly while the program is running, which would add considerable additional overhead. Not to mention that it just doesn't make sense to store (large) symbol names for later retrieval when you can store (small) addresses instead.

Here again, pSymSetOptions, pSymInitialize, pSymGetLineFromAddr64, and pSymFromAddr are all pointers to the functions exported by dbghelp.dll.

    ...

 

    IMAGEHLP_LINE64      sourceinfo;

#define MAXSYMBOLNAMELENGTH 256

#define SYMBOLBUFFERSIZE (sizeof(SYMBOL_INFO) + (MAXSYMBOLNAMELENGTH * sizeof(TCHAR)) - 1)

    unsigned char        symbolbuffer [SYMBOLBUFFERSIZE];

 

    // Initialize structures passed to the symbol handler.

    pfunctioninfo = (SYMBOL_INFO*)symbolbuffer;

    memset(pfunctioninfo, 0x0, SYMBOLBUFFERSIZE);

    pfunctioninfo->SizeOfStruct = sizeof(SYMBOL_INFO);

    pfunctioninfo->MaxNameLen = MAXSYMBOLNAMELENGTH;

    memset(&sourceinfo, 0x0, sizeof(IMAGEHLP_LINE64));

    sourceinfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);

 

    // Initialize the symbol handler. We use it for obtaining source file/line

    // number information and function names for the memory leak report.

    buildsymbolsearchpath();

    pSymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);

    if (!pSymInitialize(m_process, (char*)path.c_str(), TRUE)) {

        _RPT1(_CRT_WARN,

              "WARNING: Visual Leak Detector: The symbol handler failed to initialize (error=%d)./n"

              "    Stack traces will probably not be available for leaked blocks./n",

              GetLastError());

    }

 

    ...

 

// Iterate through each frame in the call stack.

            for (frame = 0; frame < callstack->size(); frame++) {

                // Try to get the source file and line number associated with

                // this program counter address.

                if (pSymGetLineFromAddr64(m_process, (*callstack)[frame], &displacement, &sourceinfo)) {

                    // Unless _VLD_showuselessframes has been toggled, don't

                    // show frames that are internal to the heap or Visual Leak

                    // Detector. There is virtually no situation where they

                    // would be useful for finding the source of the leak.

                    if (!_VLD_showuselessframes) {

                        if (strstr(sourceinfo.FileName, "afxmem.cpp") ||

                            strstr(sourceinfo.FileName, "dbgheap.c") ||

                            strstr(sourceinfo.FileName, "new.cpp") ||

                            strstr(sourceinfo.FileName, "vld.cpp")) {

                            continue;

                        }

                    }

                }

 

                // Try to get the name of the function containing this program

                // counter address.

                if (pSymFromAddr(m_process, (*callstack)[frame], &displacement64, pfunctioninfo)) {

                    functionname = pfunctioninfo->Name;

                }

                else {

                    functionname = "(Function name unavailable)";

                }

 

                // Display the current stack frame's information.

                if (sourceinfo.FileName) {

                    report("    %s (%d): %s/n", sourceinfo.FileName, sourceinfo.LineNumber, functionname);

                }

                else {

                    report("    0x%08X (File and line number not available): ", (*callstack)[frame]);

                    report("%s/n", functionname);

                }

            }

 

    ...

 

Finally, here is a short bit of code demonstrating how the global instance of the VisualLeakDetector class gets constructed before any user global objects. This is achieved by using the #pragma init_seg(compiler) directive.

// The one and only VisualLeakDetector object instance. This is placed in the

// "compiler" initialization area, so that it gets constructed during C runtime

// initialization and before any user global objects are constructed. Also,

// disable the warning about us using the "compiler" initialization area.

#pragma warning (disable:4074)

#pragma init_seg (compiler)

VisualLeakDetector visualleakdetector;

Building Visual Leak Detector From Source

Because Visual Leak Detector is open source, the library can be built from source if you want to tweak it to your liking. The hardest part about building the VLD libraries from source is getting your build environment correctly set up. But if you follow these instructions carefully, the process should be fairly painless.

  1. Visual Leak Detector depends on the Debug Help Library (dbghelp.dll) version 5.1 or later. Various versions of this DLL are shipped with the Windows operating systems. The latest version is always included with Debugging Tools for Windows. Version 6.3 of the DLL is included with VLD.
    • Windows XP users should already have a new enough version of dbghelp.dll in WINDOWS/system32. So, if you're running Windows XP, you don't need to do anything with the dbghelp.dll included with VLD.
    • Windows 2000 shipped with an older version of dbghelp.dll. To build VLD on Windows 2000, you must use a newer version (5.1 or newer). The best way is to use the copy of dbghelp.dll included with VLD (version 6.3). Place the included copy of the DLL in the directory where the executable you are debugging resides.
    • Windows Server 2003 users also should already have a new enough version of dbghelp.dll in WINDOWS/system32. So, if you're running Windows Server 2003, you don't need to do anything with the dbghelp.dll included with VLD.
  2. VLD also depends on the Debug Help Library header file (dbghelp.h). This header file won't exist unless you have a recent Platform SDK installed. I recommend installing the Windows XP SP2 Platform SDK. This SDK is compatible with Windows XP, Windows 2000, and Windows Server 2003. If you're debugging an application for Windows Server 2003, the Windows Server 2003 Platform SDK will probably work as well, but I haven't tried it myself so I can't guarantee it will work. Both of these SDKs can be downloaded from Platform SDK Update.
  3. Once you have the Platform SDK installed, you'll need to make Visual C++ aware of where it can find the Debug Help Library header file. To do this, add the "include" subdirectory from the Platform SDK installation directory to the include search path in Visual C++:
    • Visual C++ 7: Go to Project Properties -> C/C++ -> General -> Additional Include Directories and add the "include" subdirectory from the Platform SDK. Make sure it's at the top of the list.
    • Visual C++ 6: Go to Tools -> Options -> Directories. Select "Include files" from the "Show Directories For" drop-down menu. Add the "include" subdirectory from the Platform SDK. Make sure it's at the top of the list.
  4. VLD also depends on two other header files (dbgint.h and mtdll.h) that will only be installed if you elected to install the CRT source files when you installed Visual C++. If you didn't install the CRT sources, you'll need to re-run the Visual C++ installer and install them. If you are not sure whether you installed the CRT sources when you installed Visual C++, check to see if dbgint.h and mtdll.h exist in the CRT/src subdirectory of your Visual C++ installation directory. If those files are missing, or you don't have a CRT/src directory, then chances are you need to re-install Visual C++ with the CRT sources selected.
  5. Make sure that your Visual C++ installation's CRT/src subdirectory is in the include search path. Refer to step 3 for instructions on how to add directories to the include search path. The CRT/src subdirectory should go after the default include directory. To summarize, your include search path should look like this:
    • C:/Program Files/Microsoft Platform SDK for Windows XP SP2/Include
    • C:/Program Files/Microsoft Visual Studio/VCx/Include
    • C:/Program Files/Microsoft Visual Studio/CRT/src

In the above example, "VCx" would be "VC7" for Visual Studio .NET or "VC98" for Visual Studio 6.0. Also, the name of your Platform SDK directory might be different from the example depending on which version of the Platform SDK you have installed.

Once you've completed all of the above steps, your build environment should be ready. To build VLD, just open the vld.dsp project and do a batch build to build all six of the configurations:

  • The three debug configurations are for building versions of the library that have debugging information so that VLD itself can be conveniently debugged.
  • The three release configurations build the library for use in debugging other programs.
  • There are three configurations each: one for each method of linking with the C runtime library (single-threaded, multithreaded, and DLL). When linking the VLD library against a program, the vld.h header file detects how the program is linking to the C runtime library and selects the appropriate VLD library from the three possible choices.

The "release" builds of the VLD libraries are not like typical release builds. Despite the "release" name, they are actually meant to be linked only to debug builds of your own programs. When doing release builds of your programs, VLD will not be linked to them at all. In the context of VLD, "release" simply means the versions that are optimized and have the symbols stripped from them (to make the libraries smaller). They are the versions of the libraries that are included in the release of VLD itself (hence the "release" name). So when you are building the release libraries, you're really building the same libraries that are included in the main VLD distribution. The "debug" builds of VLD are strictly for debugging VLD itself (e.g. if you want to modify it or if you need to fix a bug in it).

Using VLD On Windows XP Professional x64

As of version 0.9i, VLD supports x64-based 64-bit Windows. However, the binaries contained in the distributed versions of VLD are 32-bit only. To take advantage of the 64-bit support, you'll need to build 64-bit versions of the libraries from source. To build the 64-bit versions, follow the instructions for building VLD from source. So long as they are built using an x64-compatible compiler in 64-bit mode, the resulting libraries will be 64-bit libraries.

Note: I have not personally tested the 64-bit extensions so they are not absolutely guaranteed to work out-of-the-box. There may be a few lingering 64-bit compiler errors that still need to be worked out. If you need 64-bit support and run into problems trying to build the source in 64-bit mode, please let me know. I'll be glad to assist in getting the 64-bit code working properly.

Known Bugs

These are known bugs in the latest release (currently 0.9i):

  • No known bugs at this time.

Solutions To Common Problems

Here's a list of problems you might run into and potential solutions:

  • "the procedure entry point SymFromAddr could not be located in the dynamic link library dbghelp.dll" when running a program with VLD

This typically only happens on Windows 2000 clients. It will happen if the Debug Help Library is out-of-date. Copy the included version of dbghelp.dll (version 6.3) to the directory where the executable you are debugging resides. If dbghelp.dll is missing for some reason, you can get it by installing Debugging Tools for Windows. I recommend installing version 6.3.

  • fatal error "C1189: #error : ERROR: Use of C runtime library internal header file." in either the file stdio.h or in the file assert.h (or possibly in some other standard header file), when building VLD from source

Visual C++ is including the wrong copies of the standard headers. Be sure that the CRT/src subdirectory of your Visual C++ installation directory is listed after the default include directory in Visual C++'s include search path.

  • Compile Error C1083: Cannot open include file: 'dbgint.h': No such file or directory, when building VLD from source

This will happen if the CRT source files are not installed. These are an optional part of the installation when you first install Visual C++. Re-run the Visual C++ installation, if needed, to install them. If the CRT sources are already installed, make sure the CRT/src subdirectory of the Visual C++ installation directory is in Visual C++'s include search path.

  • Compile Error C2059: syntax error : '...', or a lot of other compile errors in dbghelp.h, when building VLD from source

Visual C++ is including the wrong copy of dbghelp.h. Be sure that the include search path points to the Platform SDK "include" subdirectory, not the "sdk/inc" subdirectory of "Debugging Tools for Windows".

Credits

  • Thanks to Alexandre Nikolov for quickly discovering the bug in 0.9e and helping to slap together a quick fix.
  • Credit for the idea of how to make VLD's global class VisualLeakDetector object get constucted before any other user global objects goes to cmk. Thanks cmk!
  • Thanks to Nitrogenycs (aka Matthias) for discovering the VLD_MAX_DATA_DUMP infinite loop bug.
  • Thanks to hsvcs (aka Holger) for discovering the VLD_MAX_DATA_DUMP compilation bug.
  • Thanks to Don Clugston for pointing out to me that people sometimes do build optimized debug executables, and that I need to watch out for that.

References

Here are some links to useful related articles and resources:

Caveats

If you were paying close attention when I was explaining how Visual Leak Detector works, you noticed an asterisk where I was discussing how the global VisualLeakDetector object is instantiated. Well, there is in fact some fine print and here it is:

* In order to be successful at detecting leaks, VLD's code must run before any of the code being debugged. Most often this will happen without a hitch. However, there is one rare instances where this might not happen: if any global objects in the program have been placed in the "compiler" initialization area. However, user global objects are never placed in this area by default. They must be manually placed there by using the #pragma init_seg(compiler) directive. As long as you are not using this directive then VLD will not have any problem running before your code. If you are using it, then you should take whatever measures are necessary to ensure that objects from the VLD library are constructed before your first global object is constructed. If this situation applies to you, but you are not concerned about the possibility of memory leaks in your global objects' constructors, then it will not be a problem if your global objects are consructed before VLD's objects.

History

2 May 2005 - VLD 0.9i has been posted. This release fixes several bugs. It also includes support for Windows x64. The libraries available for download are still 32-bit only. To take advantage of the 64-bit support, you'll need to build the 64-bit libraries from the source. Bugs fixed in this release are:

  • VLD does not report memory leaks that are the result of a failure to free memory allocated via a call to realloc().
  • In multithreaded programs, if the program's main thread terminates before other threads in the same process, then VLD may cause an access violation while checking for memory leaks.
  • If VLD cannot find the source file and line number information for a program address, the last known file and line number will be repeated in the call stack section of the memory leak report. The correct behavior should be for VLD to print "File and line number not available" for that call stack entry.

22 April 2005 - Uploaded version 0.9h, which contains a crucial fix for an internal logic bug in version 0.9g. This version obsoletes version 0.9g. Problems fixed in version 0.9g:

  • Seemingly random access violations occur at various different lines within vldutil.cpp.
  • VLD seems to randomly fail to report some memory leaks.

22 April 2005 - Uploaded version 0.9g. This release contains a vastly improved internal search/sort algorithm that results in a significant overall performance boost.

13 April 2005 - Uploaded version 0.9f. This provides a quick fix for a flaw in 0.9e that can cause crashes. Problems fixed in version 0.9f:

  • Access Violation at line 319 in vldutil.cpp may occur when running a program linked with the VLD library.

13 April 2005 - Uploaded version 0.9e. This version should resolve compatibility problems between the pre-built libraries and Visual Studio .NET. Problems fixed in version 0.9e:

  • Linking to the VLD 0.9d libraries from the VLD distribution under Visual Studio .NET results in a number of linker "unresolved external symbol" errors. Unresolved symbols include "__declspec(dllimport) void __cdecl std::_Xran(void)" and "__declspec(dllimport) private: void __thiscall std::basic_string,class std::allocator >::_Eos(unsigned int)", among others.
  • Call stacks do not appear in the memory leak report when linking against release VLD libraries built from source with Visual Studio .NET.
  • If the preprocessor macro VLD_MAX_DATA_DUMP is defined as 0 (zero), then VLD will get stuck in an infinite loop, repeatedly printing the same information while attempting to display the memory leak report in the debugger's output window.

30 March 2005 - Uploaded version 0.9d which is the first release of VLD in library form. Packaged as a library, VLD can now be used to debug other programs without having to build VLD from source.

17 March 2005 - Uploaded version 0.9c which includes a minor bug fix. Problems fixed in version 0.9c:

  • Compile error, "error C2039: 'size' : is not a member of '_CrtMemBlockHeader'" occurs at line 644 of vld.cpp when building VLD with the VLD_MAX_DATA_DUMP preprocessor macro defined.

15 March 2005 - I've uploaded version 0.9b of the source and example projects. Problems fixed in version 0.9b are:

  • VLD fails to detect memory leaks in class constructors if the objects constructed are global objects.
  • If a debug executable is built with certain compiler optimizations turned on, specifically frame pointer omission optimization or automatic inlining, then theoretically VLD may produce incomplete or inaccurate stack traces or might fail to produce stack traces altogether.

12 March 2005 - Initial version (0.9a) posted.

About Dan Moulding

 

In real life I'm a firmware engineer. I mostly do C and assembly programming on obscure proprietary hardware. But I started my programming career doing a lot of C++. So, occassionally in my free time I enjoy dabbling in my own Windows programming projects with Visual C++ to keep my C++ skills from rotting away completely.

I also like to keep abreast of the GNU/Linux scene because, well let's face it, Windows isn't everything. I've recently found Cygwin to be a good way of getting the best of both worlds.

http://kenbeyond.blogchina.com/kenbeyond/927391.html

 

DLL动态链接库的工作原理

转载地址:http://blog.csdn.net/woshinia/article/details/7919281#comments "动态链接"这几字指明了DLLs是如何工作的。对于常规的函...
  • zzuchengming
  • zzuchengming
  • 2016年10月03日 20:19
  • 1776

Windows DLL基本原理

Windows DLL基本原理 Windows系统平台上,你可以将独立的程序模块创建为较小的DLL(Dynamic Linkable Library)文件,并可对它们单独编译和测试。在运行时,只...
  • helloworld201456
  • helloworld201456
  • 2014年06月09日 06:14
  • 873

NuGet管理dll包---与NuGet的邂逅

背景介绍    最近一直在火热开发中的高校平台项目,进行到现在底层类库已经更新到版本V1.0.1.7了,与以往软件开发不同的是这次不再需要每个开发小组一次次的去拷贝dll文件了,要做的只是在自己的运...
  • u010773667
  • u010773667
  • 2015年02月04日 09:50
  • 1065

dll文件生成使用

dll文件生成     vc编译器与gcc的编译原理是一样的,个别的函数像printf可能编译结果不大一样。程序生成的基本过程就是编译,链接。编译大体上也分三部分,预处理,编译,汇编,因为编译过程最...
  • ysu108
  • ysu108
  • 2013年05月23日 09:11
  • 4321

DLL入门浅析(2)——如何使用DLL

DLL入门浅析(2)——如何使用DLL 上文我简单的介绍了如何建立一个简单DLL,下面再我简单的介绍一下如何使用一个DLL。当一个DLL被生成后,它创建了一个.dll文件和一个.lib文件;这两个...
  • CSDNMicrosoftCSDN
  • CSDNMicrosoftCSDN
  • 2016年03月21日 16:41
  • 887

《Windows核心编程》之“DLL注入”(一)

一、背景知识 1,Private Address Space         在Windows操作系统中,每个进程都有自己私有的地址空间(private address space)。各个进程的地址空...
  • Sagittarius_Warrior
  • Sagittarius_Warrior
  • 2016年08月09日 16:29
  • 1587

DLL创建及使用方法

初次接触DLL文件的使用,在网上找了许多资料自己动手写了一些代码,多谢网络上许多大神的教程。俗话说眼过千遍不如手过一遍,现在将这些总结一下也算是对自己的一个提高吧。 1.DLL创建方法 1...
  • twtian01
  • twtian01
  • 2014年08月29日 16:27
  • 2437

DLL劫持(HiJack)原理以及实现细节

DLL劫持(HiJack)原理以及实现细节
  • u013761036
  • u013761036
  • 2016年08月21日 18:08
  • 5319

Unity3D里使用自己的dll

首先,我们需要新建一个类库项目,可以使用Visual Studio或者Monodevelop来做。我这里是使用vs2012来创建:   选择好项目类型、写好项目名称。   新生...
  • dingkun520wy
  • dingkun520wy
  • 2016年09月08日 15:35
  • 2364

[DLL劫持] 3 DLL劫持之实践 例子

该系列文章是依据本人平时对动态链接库的学习,归纳总结,所做的学习笔记。如有错误或待改善之处,请留下您宝贵的意见或建议。   先说说DLL劫持的原理吧,以下这段来自百度百科对DLL劫持原理的说明: ...
  • u010311064
  • u010311064
  • 2014年07月12日 20:24
  • 2154
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Dll原理和使用
举报原因:
原因补充:

(最多只允许输入30个字)