第二十章 DLL高级技巧
		
		
				1.概览
		
		
				 1.1动态加载DLL文件 LoadLibraryEx
		
		                HMODULE LoadLibraryEx(
		PCTSTR pszDLLPathName,
		HANDLE hFile,
		DWORD dwFlags);
		              返回DLL加载到进程空间原首地址。
		              dwFlags 可以有以下几个值
		
				              (1) DONT_RESOLVE_DLL_REFERENCES
		
				                              
				建议永远不要使有这个值,它的存在仅仅是为了向后兼容、
		
		                              更多内容请访问:http://blogs.msdn.com/oldnewthing/archive/2005/02/14/372266.aspx
		              (2) LOAD_LIBRARY_AS_DATAFILE
		                              把要加载的DLL文件以数据文件的形式加载到进程中。
		                              GetModuleHandle和GetProcAddress返回NULL
		              (3) LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
		                              与前者相同,不同的时独占打开,禁止其它进程访问和修改该DLL中的内容。
		              (4) LOAD_LIBRARY_AS_IMAGE_RESOURCE
		                              不修改DLL中的RVA,以image的形式加载到进程中。常与LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE一起使用。
		              (5) LOAD_WITH_ALTERED_SEARCH_PATH
		                              修改DLL的加载路径
		
				 
				1.2 DLL
				
						的加载与卸载
				
		
		              (1)加载
		                              不要在同一进程中,同时使用LoadLIbrary和LoadLibraryEx加载同一DLL文件。
		                              DLL的引用计数是以进程为单位的。LoadLibrary会把DLL文件加载到内存,然后映射到进程空间中。
		                              多次加载同一DLL只会增加引用计数而不会多次映射。当所有进程对DLL的引用计数都为0时,系统会在内存中释放该DLL。
		              (2)卸载
		                              FreeLibrary,FreeLibraryAndExitThread对当前进程的DLL的引用计数减1
		              (3) GetProcAddress
		                              取得函数地址。它只接受ANSI字符串。
		
				2.DLL的入口函数
		
		
				                2.1 DllMain
		
		              BOOL WINAPI DllMain(
		              HINSTANCE hInstDll, ""加载后在进程中的虚拟地址
		              DWORD fdwReason, ""系统因何而调用该函数
		              PVOID fImpLoad ""查看是隐工还是动态加载该DLL
		 
		              DLLs用DllMain方法来初始化他们自已。DllMain中的代码应尽量简单,只做一些简单的初始化工作。
		              不要在DllMain中调用LoadLibrary,FreeLibrary及Shell, ODBC, COM, RPC, 和 socket 函数,从而避免不可预期的错误。
		 
		
				                2.2 fdwReason的值
		
		               (1)DLL_PROCESS_ATTACH 
		               系统在为每个进程第一次加载该DLL时会,执行DLL_PROCESS_ATTACH后面的语句来初始化DLL,DllMain的返回值仅由它决定。
		 系统会忽略DLL_THREAD_ATTACH等执行后DllMain的返回值。
		               如果DllMain返回FALSE,系统会自动调用DLL_PROCESS_DETACH的代码并解除DLL文件中进程中的内存映射。
		               
		               (2)DLL_PROCESS_DETACH 
		                              如果DLL是因进程终止而卸载其在进程中的映射,那么负责调用ExitProcess的线程会调用DllMain中DLL_PROCESS_DETACH所对应的代码。
		                              如果DLL是因FreeLibrary或FreeLibraryAndExitThread,而卸载其在进程中的映射,
		
				那么FreeLibrary或FreeLibraryAndExitThread会负责调用DllMain中DLL_PROCESS_DETACH所对应的代码。
		                              如果DLL是因TerminateProcess而卸载其在进程中的映射,系统不会调用DllMain中DLL_PROCESS_DETACH所对应的代码。
		              (3) DLL_THREAD_ATTACH
		                              若进程是先加载的DLL,后创建的线程
		                                              那么在进程中创建新线程时(主线程除外),系统会执行该进程已载的所有DLL的DllMain中DLL_THREAD_ATTACH对应的代码。
		                              若进程是先创建的线程,后加载的DLL
		                                              那么系统不会调用DLL的DllMain中的代码。
		              (4) DLL_THREAD_DETACH
		                              进程中的线程退出时,会先执行所有已加载DLL的DllMain中DLL_THREAD_DETACH所对应的代码。若该代码中有死循环,线程不会退出。
		              
		
				 2.3 同步化DllMain的调用
		
		              同一时间只能有一个线程调用DllMain中的代码,所以下面的代码会导致死循环
		BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, PVOID fImpLoad) {
		 
		   HANDLE hThread;
		   DWORD dwThreadId;
		 
		   switch (fdwReason) {
		   case DLL_PROCESS_ATTACH:
		      // The DLL is being mapped into the process' address space.
		 
		      // Create a thread to do some stuff.
		      hThread = CreateThread(NULL, 0, SomeFunction, NULL,
		         0, &dwThreadId);// CreateThread会DLL_THREAD_ATTACH中的代码,但是由于当前线程并未执行完毕,
		//所以DLL_THREAD_ATTACH中的代码不会被执行,且CreateThread永无不会返回。
		 
		      // Suspend our thread until the new thread terminates.
		      WaitForSingleObject(hThread, INFINITE);
		 
		      // We no longer need access to the new thread.
		      CloseHandle(hThread);
		      break;
		 
		   case DLL_THREAD_ATTACH:
		      // A thread is being created.
		      break;
		 
		   case DLL_THREAD_DETACH:
		      // A thread is exiting cleanly.
		      break;
		 
		   case DLL_PROCESS_DETACH:
		      // The DLL is being unmapped from the process' address space.
		      break;
		   }
		   return(TRUE);
		}
		 
		
				3.延时加载DLL
		
		(1)延时加载DLL的限制
		              延时加载是指当程序在运行时用到DLL中的函数时自动会自动加载DLL函数,它与动态加载不同。
		              http://msdn2.microsoft.com/en-us/library/yx1x886y(VS.80).aspx
		 
		
				4.已知的DLL (Known DLLs)
		
		              位置:HKEY_LOCAL_MACHINE"SYSTEM"CurrentControlSet"Control"Session Manager"KnownDLLs
		              LoadLibrary在查找DLL会先去该位置查找有无相应的键值与DLL要对应,若有则根据链值去%SystemRoot%"System32加载键值对应的DLL
		              若无则根据默认规去寻找DLL
		 
		
				5.Bind and Rebase Module
		
		              它可以程序启动的速度。ReBaseImage