摘录自windows via c/c++(Jeffrey Richter)
1
for a DLL you must specify the /DLL switch to the linker. This switch causes the linker to emit slightly different information into the resulting DLL file image so that the operating system loader recognizes the file image as a DLL rather than an application.
在vc中你可以在的project->settings->link->project options里面加入/DLL. 这个选项的作用是在生成DLL文件时,里面加入一个代表该文件是DLL
的标志. 这个标志就是操作系统用来区别EXE还是DLL的标志. 所以说DLL文件即使扩展名叫做EXE也没关系,因为OS不是通过扩展名来判断文件类型的.
2
Once a DLL's file image is mapped into the calling process' address space, the DLL's functions are available to all the threads running within the process. In fact, the DLL loses almost all of its identity as a DLL: To the threads in the process, the DLL's code and data simply look like additional code and data that happen to be in the process' address space. When a thread calls a DLL function, the DLL function looks at the thread's stack to retrieve its passed parameters and uses the thread's stack for any local variables that it needs. In addition, any objects created by code in the DLL's functions are owned by the calling thread or process—a DLL never owns anything.
DLL一旦被载入进程的地址空间后,那么你就不必再把它当成DLL了, 就把它完全看成EXE的一部分. DLL中的函数的形参,局部变量等全是位调用该函数的那个线程的栈的.
3 *****
什么是c/c++运行时库? 其实就是一些人用c/c++语言包装windows函数来实现一些完整的功能,这样c/c++程序员在编程时可以直接调用这些函数去实现具体功能,而不用 自己调用windows提供的函数,再进行处理什么的了. 所以说任何一个库的函数我们都可以不用,都可以自己调用windows函数,然后再根据具体需要处理一下,但是这样麻烦,每一个有这样需要的人都要自己写 一遍重复的代码,所以windows加入了这些库,以达到代码重用.
理解了这一点,就可以理解, JAVA也许会有一个用JAVA写的,提供给JAVA调用的类似的库.
你在写程序的时候有时会想从堆上分配一些内存来使用.
如果你用vc的话,你有几个选择, 一个是调用windows提供给你的AllocHeap函数, 这个函数从进程的默认堆中分配内存.
另一个是调用c/c++运行时库提供的malloc()函数来分配. 如果你使用的是静态版本的库,那么该函数自己另外生成一个堆,并在其中分配内存.
如果你是使用动态版本的库,那么这个函数从进程的默认堆中分配内存.所以如果你在DLL中调用MALLOC来分配内存, 而要在EXE中删除该内存的话,就必须保证他们两个是链接的同一个库!
这个知识点可以解释windows via c/c++ 里面的chapter 19中 DLLs and process' address space 中note 部分的那个问题.
4
在EXE中有一个"import section",这里面列出了该EXE所需要的所有的DLL的名字, windows加载器在加载该EXE时会分析该section,找到需要的DLL并将它们载入. 注意每一个DLL也可能会调用其他DLL中的函数, 所以每个DLL也有它自己的"import section", 加载器也会分析每一个DLL的import section, 导入所有的需要的DLL.
5
正常的话你应该在你的EXE中使用__declspec(dllimport)来修饰你想从DLL中引用的函数,但这不是必须的,没有它也成.
6
loadlibrary()可以通过设置很多东西, 比如不调用dllmain , 载入一个.exe以获取它里面授资源。详见msdn.
7
When the DLL is linked, the linker detects this embedded information about the exported variable, function, or class and automatically produces a .lib file. This .lib file contains the list of symbols exported by the DLL. This .lib file is, of course, required to link any executable module that references this DLL's exported symbols. In addition to creating the .lib file, the linker embeds a table of exported symbols in the resulting DLL file. This export section contains the list (in alphabetical order) of exported variables, functions, and class symbols. The linker also places the relative virtual address (RVA), indicating where each symbol can be found in the DLL module.
也就是说,在dll本身中也有一个表存放导出的变量,函数和类。 所以我们可以在程序中可以通过编码的方法来找到这些值。
这个表中有一项是函数的名字,还有一项是一个序号值,我们在使用GetProAddress()时,可以在参数中输入这个函数的名字,也可以输入它的序号。
8
For the DLL file image to be unmapped from the process' address space, threads in the process must call FreeLibrary twice—the first call simply decrements the DLL's usage count to 1, and the second call decrements the DLL's usage count to 0. When the system sees that a DLL's usage count has reached 0, it unmaps the DLL's file image from this process' address space. Any thread that attempts to call a function in the DLL raises an access violation because the code at the specified address is no longer mapped into the process' address space.
The system maintains a DLL's usage count on a per-process basis; that is, if a thread in Process A makes the following call and then a thread in Process B makes the same call, MyLib.dll is mapped into both processes' address spaces—the DLL's usage count for Process A and for Process B are both 1.
HMODULE hInstDll = LoadLibrary(TEXT("MyLib.dll"));
If a thread in Process B later calls the following function, the DLL's usage count for Process B becomes 0, and the DLL is unmapped from Process B's address space. However, the mapping of the DLL in Process A's address space is unaffected, and the DLL's usage count for Process A remains 1.
FreeLibrary(hInstDll);
9
A thread can determine whether a DLL is already mapped into its process' address space by calling the GetModuleHandle function:
HMODULE GetModuleHandle(PCTSTR pszModuleName);
For example, the following code loads MyLib.dll only if it is not already mapped into the process' address space:
HMODULE hInstDll = GetModuleHandle(TEXT("MyLib")); // DLL extension assumed
if (hInstDll == NULL) {
hInstDll = LoadLibrary(TEXT("MyLib")); // DLL extension assumed
}
If you pass NULL to GetModuleHandle , the handle of the application executable is returned.
10
You can also determine the full pathname of a DLL (or an .exe) if you have only the DLL's HINSTANCE /HMODULE value by using the GetModuleFileName function:
DWORD GetModuleFileName(
HMODULE hInstModule,
PTSTR pszPathName,
DWORD cchPath);
11
when you use the "FARPROC pfn = GetProcAddress(hInstDll, MAKEINTRESOURCE(2)) "function to get a function address :
You should be aware that the first method of calling GetProcAddress is slower than the second because the system must perform string comparisons and searches on the symbol name string that was passed. With the second method, if you pass an ordinal number that hasn't been assigned to any of the exported functions, GetProcAddress might return a non-NULL value. This return value will trick your application into thinking that you have a valid address when you don't. Attempting to call this address will almost certainly cause the thread to raise an access violation. Early in my Windows programming career, I didn't fully understand this behavior and was burned by it several times—so watch out. (This behavior is yet another reason to avoid ordinals in favor of symbol names.)