实验内容
1.理解Windows同步互斥机制中的等待函数、事件内核对象、信标内核对象、互斥对象内核对象、动态链接库、DLL整体运行情况、创建DLL模块和相关函数部分。
DLL程序入口点函数为DllMain,其函数原型为:
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
其中hModule参数指向DLL本身的实例句柄;ul_reason_for_call参数指明了DLL被调用的原因,可以有以下4个取值:
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函数
具体代码如下:
Customer消费者
producer生产者
两个模块的不同在于WriteBuffer函数和ReadBuffer函数。另外这两个模块的dll库的导入采用导入表方式导入,而不是采用LoadLibrary动态加载,对应的IDE的设置应该是项目下的引用。
ShareDllLib.h
该头文件的主要作用是定义一个用于导入、导出的符号__SHAREDLLLIB
这段通过判断是否定义了__SHAREDLLLIB来定义不同的__SHAREDLLLIB符号,具体来说,当Customer模块或者Producer模块调用该头文件时,__SHAREDLLLIB被定义为extern "C" __declspec(dllimport)。而当ShareDll模块调用该头文件时,__SHAREDLLLIB被定义为extern "C" __declspec(dllexport)。
使用extern "C"的原因是在于C++会允许源码中的函数重载,但是实际上函数重载的实现机理是编译器对函数的重命名,使用了extern "C"实际上强制编译器使用统一的C规范,同时这也会导致函数无法重载。
头文件中通过调用这个符号来定义了三个需要导入、导出的符号。
运行结果如下: