在介绍入口函数之前,先介绍一下几种运行时的库(Runtime Library)以及他们对应的链接选项:
1.Multi-threaded Debug (/MTd)
这个是debug版本的多线程库,静态链接的。
3.Multi-threaded Debug DLL (/MDd)
这是debug版本的多线程库,动态链接的。
4.Multi-threaded DLL (/MD)
这是release版本的多线程库,动态链接的。
debug版本的运行库增加了一些错误检查的代码,如:堆栈的溢出检查,基于cookie的安全检查,基于CheckStackVar的检查。这些额外的代码可以帮助检测编码中的非安全错误。因此debug版本的运行效率没有release版本高。后面的章节将详细介绍debug运行时的安全检查策略。
静态链接运行时与动态链接运行时的区别在于,静态链接会将整个运行时的代码链接到模块中(也就是说每个模块都有一份运行时的代码),而动态链接会以共享的方式被模块使用(也就是说每个模块都会共享一份运行时的代码)。这样会导致在不同的链接方式下,它们有自己需要注意的地方。
1.采用/MD选项
a.由于是动态链接运行时,那么最终生成的模块体积会很小
b.每个模块都共享一份运行时代码,那么堆的申请与释放很自由,A模块申请堆,B模块释放堆,这样是可以得。
2.采用/MT选项
a.由于是静态链接的,那么每个模块都会包含一份运行时代码,自然会增加其体积。但是这样可以在缺少运行时的系统中稳定的运行。
b.每个模块都会包含一份运行时代码,那么只能在自己的模块中释放自己申请的堆,否则会出现错误。
注意:多个模块的情况下,必须选择相同的选项,不可以混用。
以下是测试方案:
在A DLL中添加申请堆和释放堆得代码,在B DLL中添加同样的代码,并将它们以不同的链接方式生成(/MD,/MT)。
在主程序中加载这两个DLL,并分别获取它们的导出函数。
A DLL代码如下:
// A_DLL.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
char* MallocHeap( int n )
{
if ( n <= 0 )
{
return NULL;
}
char* pHeap = new char( n );
if ( pHeap )
{
return pHeap;
}
else
{
return NULL;
}
return NULL;
}
BOOL FreeHeap( char* pHeap )
{
if ( pHeap )
{
delete pHeap;
pHeap = NULL;
return TRUE;
}
else
{
return FALSE;
}
return FALSE;
}
B DLL代码如下:
// B_DLL.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
char* MallocHeap( int n )
{
if ( n <= 0 )
{
return NULL;
}
char* pHeap = new char( n );
if ( pHeap )
{
return pHeap;
}
else
{
return NULL;
}
return NULL;
}
BOOL FreeHeap( char* pHeap )
{
if ( pHeap )
{
delete pHeap;
pHeap = NULL;
return TRUE;
}
else
{
return FALSE;
}
return FALSE;
}
// LinkerOption.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
typedef char* ( *FunMallocHeap ) ( int );
typedef BOOL ( *FunFreeHeap ) ( char* );
int _tmain(int argc, _TCHAR* argv[])
{
HMODULE hDllA = ::LoadLibrary( L"A_DLL.dll" );
HMODULE hDllB = ::LoadLibrary( L"B_DLL.dll" );
if ( !hDllA || !hDllB )
{
return 1;
}
//获取DLL A中MallocHeap地址
FunMallocHeap pMallocHeapA = NULL;
pMallocHeapA = ( FunMallocHeap )::GetProcAddress( hDllA, "MallocHeap" );
//获取DLL B中MallocHeap地址
FunMallocHeap pMallocHeapB = NULL;
pMallocHeapB = ( FunMallocHeap )::GetProcAddress( hDllB, "MallocHeap" );
if ( !pMallocHeapA || !pMallocHeapB )
{
return 1;
}
//获取DLL A中FreeHeap地址
FunFreeHeap pFreeHeapA = NULL;
pFreeHeapA = ( FunFreeHeap )::GetProcAddress( hDllA, "FreeHeap" );
//获取DLL B中FreeHeap地址
FunFreeHeap pFreeHeapB = NULL;
pFreeHeapB = ( FunFreeHeap )::GetProcAddress( hDllB, "FreeHeap" );
if ( !pFreeHeapA || !pFreeHeapA )
{
return 1;
}
if ( argc < 3 )
{
printf( "请输入参数:(A B)(A A)前者为申请模块,后者为释放模块\r\n" );
return 3;
}
wchar_t szMallocObj[10];
wcscpy( szMallocObj, argv[1] );
wchar_t szFreeObj[10];
wcscpy( szFreeObj, argv[2] );
if ( wcscmp( szMallocObj, L"A") == 0 )//A模块申请
{
//使用A模块申请堆
char*p = NULL;
p = pMallocHeapA( 10 );
if ( wcscmp( szFreeObj, L"A") == 0 )//A模块释放
{
//使用A模块释放堆
BOOL bRet = pFreeHeapA( p );
if ( bRet )
{
::MessageBox( NULL, L"A DLL FreeHeap OK", L"Tips", MB_OK );
}
else
{
::MessageBox( NULL, L"A DLL FreeHeap Fail", L"Tips", MB_OK );
}
}
if ( wcscmp( szFreeObj, L"B") == 0 )//B模块释放
{
//使用B模块释放堆
BOOL bRet = pFreeHeapB( p );
if ( bRet )
{
::MessageBox( NULL, L"B DLL FreeHeap OK", L"Tips", MB_OK );
}
else
{
::MessageBox( NULL, L"B DLL FreeHeap Fail", L"Tips", MB_OK );
}
}
}
if ( wcscmp( szMallocObj, L"B") == 0 )//B模块申请
{
//使用B模块申请堆
char*p = NULL;
p = pMallocHeapB( 10 );
if ( wcscmp( szFreeObj, L"A") == 0 )//A模块释放
{
//使用A模块释放堆
BOOL bRet = pFreeHeapA( p );
if ( bRet )
{
::MessageBox( NULL, L"A DLL FreeHeap OK", L"Tips", MB_OK );
}
else
{
::MessageBox( NULL, L"A DLL FreeHeap Fail", L"Tips", MB_OK );
}
}
if ( wcscmp( szFreeObj, L"B") == 0 )//B模块释放
{
//使用B模块释放堆
BOOL bRet = pFreeHeapB( p );
if ( bRet )
{
::MessageBox( NULL, L"B DLL FreeHeap OK", L"Tips", MB_OK );
}
else
{
::MessageBox( NULL, L"B DLL FreeHeap Fail", L"Tips", MB_OK );
}
}
}
return 0;
}
先使用/MD链接各个模块并运行,效果如下:
A申请A释放:
A申请B释放:
B申请B释放:
B申请A释放:
现在使用/MT方式链接,并测试如下;
A申请A释放:
A申请B释放:
B申请B释放:
B申请A释放:
可见,测试结果说明了/MD和/MT的区别所在。
下一节将详细介绍这两张不同运行时的更细节的区别。