内存映射文件实例——dll
(本文只是为了自己需要以帮助自己以后能很快的回顾,不适合其他人来阅读,如果您需要学习dll的编写或者dll 的2种调用方式 那么请继续google)
动态库的好处在前面说过,可以动态调用,节约内存空间,以及动态的改变dll而不需要重新编译我们调用dll 的客户端程序。
动态的库的调用有2中方式,一种是显式的一种隐式的,这里我只考虑显式的,隐式的调用跟静态库的调用类似,需要提供声明文件和库文件(lib)所以我们在隐式调用dll 的时候需要提供dll 的导出函数的声明头文件。
由于隐式调用很简单,为了防止以后忘记显式dll的调用,这里我只讨论隐式的调用:
首先我们要调用dll 是因为需要dll 提供一些功能,这些功能是通过一些函数来实现的,但是并不是所有的函数都是可以调用的,根据前面说的应该是得到类似如UnmapViewOfFile返回的指针,所以在dll 中我们必须告诉客户有哪些函数是导出的,也就是映射了那些函数的指针,这dll 中我们可以通过关键字:_declspec(dllexport),最好加上extern "C",所以一般来说是通过关键字:extern "C" _declspec(dllexport) 来表明该函数是导出的。当然也可以通过def文件在指明哪些函数是导出的,一直没用过def文件,所以以后也应该不会用def 文件。
dll文件的实现完成了,然后就是客户怎么调用dll 文件了。
因为我们是动态加载的,所以首先是调用LoadLibrary来得到dll 的文件映射句柄,然后调用GetProcAddress得到我们需要调用函数的映射指针(跟前面说的,先调用CreateFileMapping得到映射对象的句柄,然后通过MapViewOfFile得到映射对象块的指针 是一致的)。
当然有一点没说就是DllMain,如果我们在dll中实现了DllMain那么我们在加载dll 的时候就会首先调用DllMain完成一些初始化工作。
下面来看一个例子,来自《inside com》第7章的组件注册:
//
// Server registration
//
STDAPI DllRegisterServer()
{
return RegisterServer(g_hModule,
CLSID_Component1,
g_szFriendlyName,
g_szVerIndProgID,
g_szProgID) ;
}
//
// Server unregistration
//
STDAPI DllUnregisterServer()
{
return UnregisterServer(CLSID_Component1,
g_szVerIndProgID,
g_szProgID) ;
}
///
//
// DLL module information
//
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD dwReason,
void* lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
g_hModule = hModule ;
}
return TRUE ;
}
上面的STDAPI是 HRESULT extern"C" _stdcall 的组合,APIENTRY 是_stdcall 当然这里没有_declspec(dllexport),这是因为它在def文件中表明了这些函数是导出的。