DLL 延时加载
1. exe加載多個dll時,初始化可能會比較慢
2. exe加載老版本dll時,如果沒有相應(yīng)的導(dǎo)出函數(shù),exe可以做相應(yīng)處理后繼續(xù)執(zhí)行。
?
局限性
1)導(dǎo)出全局變量不能延遲。
2)kernel32.dll 不能延遲, LoadLibrary and?GetProcAddress
3) 不能在dllmain中調(diào)用延遲加載的函數(shù)。
?
onedll.dll project
//onedll.h
#ifdef ONEDLL_EXPORTS
#define ONEDLL_API __declspec(dllexport)
#else
#define ONEDLL_API __declspec(dllimport)
#endif
extern "C" ONEDLL_API int fnLib(void);
extern "C" ONEDLL_API int fnLib2(void);
?
//onedll.cpp
#include "onedll.h"
ONEDLL_API int fnLib(void)
{
?return 42111;
}
ONEDLL_API int fnLib2(void)
{
?return 42222;
}
oneexe.exe project
//oneexe.cpp
#include <Windowsx.h>
#include <tchar.h>
#include <StrSafe.h>
#include <Windows.h>
#include <iostream>
#include <Delayimp.h>?? // For error handling & advanced features
#include "..\onedll.h"
#pragma comment(lib, "onedll.lib")
#pragma comment(lib, "Delayimp.lib")
// Note: it is not possible to use #pragma comment(linker, "")
//?????? for /DELAYLOAD and /DELAY
// The name of the Delay-Load module (only used by this sample app)
wchar_t g_szDelayLoadModuleName[] = L"onedll";
// Forward function prototype
LONG WINAPI DelayLoadDllExceptionFilter(PEXCEPTION_POINTERS pep);
///
void IsModuleLoaded(PCTSTR pszModuleName) {
?? HMODULE hmod = GetModuleHandle(pszModuleName);
?? wchar_t sz[100];
?? StringCchPrintfW(sz, _countof(sz), L"Module \"%s\" is %sloaded.",
????? pszModuleName, (hmod == NULL) ? L"not " : L"");
?? std::wcout << sz << std::endl;
}
//int WINAPI _tmain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int)
int _tmain(int argc, _TCHAR* argv[])
{
?? // Wrap all calls to delay-load DLL functions inside SEH
?? __try {
????? int x = 0;
????? // If you're in the debugger, try the new Debug.Modules menu item to
????? // see that the DLL is not loaded prior to executing the line below
????? IsModuleLoaded(g_szDelayLoadModuleName);
????? x = fnLib();? // Attempt to call delay-load function
????? // Use Debug.Modules to see that the DLL is now loaded
????? IsModuleLoaded(g_szDelayLoadModuleName);
????? x = fnLib2(); // Attempt to call delay-load function
????? // Unload the delay-loaded DLL
????? // NOTE: Name must exactly match /DelayLoad:(DllName)
??PCSTR pszDll = "onedll.dll";
????? BOOL bRet = __FUnloadDelayLoadedDLL2(pszDll);????? // link -> advanced -> Unload delay loaded DLL:yes
????? // Use Debug.Modules to see that the DLL is now unloaded
????? IsModuleLoaded(g_szDelayLoadModuleName);
????? x = fnLib();? // Attempt to call delay-load function
????? // Use Debug.Modules to see that the DLL is loaded again
????? IsModuleLoaded(g_szDelayLoadModuleName);
?? }
?? __except (DelayLoadDllExceptionFilter(GetExceptionInformation())) {
????? // Nothing to do in here, thread continues to run normally
?? }
?? // More code can go here...
?? return(0);
}
///
LONG WINAPI DelayLoadDllExceptionFilter(PEXCEPTION_POINTERS pep) {
?? // Assume we recognize this exception
?? LONG lDisposition = EXCEPTION_EXECUTE_HANDLER;?
?? // If this is a Delay-load problem, ExceptionInformation[0] points
?? // to a DelayLoadInfo structure that has detailed error info
?? PDelayLoadInfo pdli =
????? PDelayLoadInfo(pep->ExceptionRecord->ExceptionInformation[0]);
?? // Create a buffer where we construct error messages
?? char sz[500] = { 0 };
?? switch (pep->ExceptionRecord->ExceptionCode) {
?? case VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND):
????? // The DLL module was not found at runtime
????? StringCchPrintfA(sz, _countof(sz), "Dll not found: %s", pdli->szDll);
????? break;
?? case VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND):
????? // The DLL module was found, but it doesn't contain the function
????? if (pdli->dlp.fImportByName) {
???????? StringCchPrintfA(sz, _countof(sz), "Function %s was not found in %s",
??????????? pdli->dlp.szProcName, pdli->szDll);
????? } else {
???????? StringCchPrintfA(sz, _countof(sz), "Function ordinal %d was not found in %s",
??????????? pdli->dlp.dwOrdinal, pdli->szDll);
????? }
????? break;
?? default:
????? // We don't recognize this exception
????? lDisposition = EXCEPTION_CONTINUE_SEARCH;?
????? break;
?? }
?? if (lDisposition == EXCEPTION_EXECUTE_HANDLER) {
????? // We recognized this error and constructed a message, show it
????? //chMB(sz);
??? std::wcout << sz << std::endl;
?? }
?? return(lDisposition);
}
// Skeleton DliHook function that does nothing interesting
FARPROC WINAPI DliHook(unsigned dliNotify, PDelayLoadInfo pdli) {
?? FARPROC fp = NULL;?? // Default return value
?? // NOTE: The members of the DelayLoadInfo structure pointed
?? // to by pdli shows the results of progress made so far.
?? switch (dliNotify) {
?? case dliStartProcessing:
????? // Called when __delayLoadHelper2 attempts to find a DLL/function
????? // Return 0 to have normal behavior or nonzero to override
????? // everything (you will still get dliNoteEndProcessing)
????? break;
?? case dliNotePreLoadLibrary:
????? // Called just before LoadLibrary
????? // Return NULL to have __delayLoadHelper2 call LoadLibary
????? // or you can call LoadLibrary yourself and return the HMODULE
????? fp = (FARPROC) (HMODULE) NULL;
????? break;
?? case dliFailLoadLib:
????? // Called if LoadLibrary fails
????? // Again, you can call LoadLibary yourself here and return an HMODULE
????? // If you return NULL, __delayLoadHelper2 raises the
????? // ERROR_MOD_NOT_FOUND exception
????? fp = (FARPROC) (HMODULE) NULL;
????? break;
?? case dliNotePreGetProcAddress:
????? // Called just before GetProcAddress
????? // Return NULL to have __delayLoadHelper2 call GetProcAddress,
????? // or you can call GetProcAddress yourself and return the address
????? fp = (FARPROC) NULL;
????? break;
?? case dliFailGetProc:
????? // Called if GetProcAddress fails
????? // You can call GetProcAddress yourself here and return an address
????? // If you return NULL, __delayLoadHelper2 raises the
????? // ERROR_PROC_NOT_FOUND exception
????? fp = (FARPROC) NULL;
????? break;
?? case dliNoteEndProcessing:
????? // A simple notification that __delayLoadHelper2 is done
????? // You can examine the members of the DelayLoadInfo structure
????? // pointed to by pdli and raise an exception if you desire
????? break;
?? }
?? return(fp);
}
// Tell __delayLoadHelper2 to call my hook function
PfnDliHook __pfnDliNotifyHook2? = DliHook;
PfnDliHook __pfnDliFailureHook2 = DliHook;
//note:
生成exe后,可以rename onedll.dll 名字,或者 delete 某個導(dǎo)出函數(shù) 測試, dependency walker 查看延遲加載的dll圖標
1.一個dll中申請、釋放內(nèi)存
2.HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager 可修改加載程序的搜索順序
3. 隱式加載,可執(zhí)行程序包含導(dǎo)出dll工程的頭文件,其__declspec(dllimport) 比我們不include頭文件而直接用extern 效果高
HMODULE WINAPI GetModuleHandle(__in_opt? LPCTSTR lpModuleName);
傳NULL,返回可執(zhí)行文件的句柄,傳DLL,返回dll實例句柄
DWORD WINAPI GetModuleFileName(__in_opt? HMODULE hModule, __out???? LPTSTR lpFilename, __in????? DWORD nSize);
傳NULL,返回可執(zhí)行文件的名字,傳DLL,返回dll名字
DllMain的序列貨調(diào)用,系統(tǒng)保證一個線程執(zhí)行完DllMain才會讓另一個線程執(zhí)行
_DllMainCRTStartup -> __DllMainCRTStartup --> _CRT_INIT, DllMain
_DllMainCRTStartup -> __DllMainCRTStartup --> DllMain, _CRT_INIT
如果用戶不添加DllMain函數(shù),將調(diào)用C/C++運行庫提供的下面函數(shù)
BOOL WINAPI DllMain(
??????? HANDLE? hDllHandle,
??????? DWORD?? dwReason,
??????? LPVOID? lpreserved
??????? )
{
#if defined (CRTDLL)
??????? if ( dwReason == DLL_PROCESS_ATTACH && ! _pRawDllMain )
??????????????? DisableThreadLibraryCalls(hDllHandle);
#endif? /* defined (CRTDLL) */
??????? return TRUE ;
}
?
?
?
?
總結(jié)