//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997. // // File: D L O A D . C P P // // Contents: Delay Load Failure Hook // // Notes: This DLL implements a form of exception handler (a failure // hook, really) that is called by the delay-load section of // the loader. This DLL implements delay-load handlers for // standard APIs by returning an error code specific to the // API that failed to load. This allows clients of those APIs // to safely delay load the DLLs that implement those APIs // without having to worry about the success or failure of the // delay load operation. Failures in the delay load operation // cause the appropriate stub (implemented in this DLL) to be // invoked which simply returns an error code specific to the // API being called. (Other interface semantics such as // setting output parameters are also performed.) // // To Use: 1. Add the following line to one of your source modules. // // PfnDliHook __pfnDliFailureHook = DelayLoadFailureHook; // // 2. Define a global variable like the following: // // HANDLE g_hBaseDllHandle; // // And set it equal to your Dlls instance handle from // DLL_PROCESS_ATTACH of DllMain. // // Author: shaunco 19 May 1998 // // Revised by: pritobla 23 Novemver 1998 (removed C-Runtime calls/RtlAssert // and modified the "To Use" section) // //---------------------------------------------------------------------------- #include "precomp.h" #pragma hdrstop #if DBG VOID WINAPI AssertDelayLoadFailureMapsAreSorted ( VOID ) { const DLOAD_DLL_ENTRY* pDll; const DLOAD_PROCNAME_MAP* pProcNameMap; const DLOAD_ORDINAL_MAP* pOrdinalMap; CHAR szMsg[1024]; UINT iDll, iProcName, iOrdinal; INT nRet; for (iDll = 0; iDll < g_DllMap.NumberOfEntries; iDll++) { if (iDll >= 1) { nRet = StrCmpIA ( g_DllMap.pDllEntry[iDll].pszDll, g_DllMap.pDllEntry[iDll-1].pszDll); if (nRet <= 0) { wnsprintfA(szMsg, countof(szMsg), "dload: rows %u and %u are out of order in dload!g_DllMap", iDll-1, iDll); AssertFailedA(__FILE__, __LINE__, szMsg, TRUE); } } pDll = g_DllMap.pDllEntry + iDll; pProcNameMap = pDll->pProcNameMap; pOrdinalMap = pDll->pOrdinalMap; if (pProcNameMap) { ASSERT (pProcNameMap->NumberOfEntries); for (iProcName = 0; iProcName < pProcNameMap->NumberOfEntries; iProcName++) { if (iProcName >= 1) { nRet = StrCmpA ( pProcNameMap->pProcNameEntry[iProcName].pszProcName, pProcNameMap->pProcNameEntry[iProcName-1].pszProcName); if (nRet <= 0) { wnsprintfA(szMsg, countof(szMsg), "dload: rows %u and %u of pProcNameMap are out " "of order in dload!g_DllMap for pszDll=%s", iProcName-1, iProcName, pDll->pszDll); AssertFailedA (__FILE__, __LINE__, szMsg, TRUE); } } } } if (pOrdinalMap) { ASSERT (pOrdinalMap->NumberOfEntries); for (iOrdinal = 0; iOrdinal < pOrdinalMap->NumberOfEntries; iOrdinal++) { if (iOrdinal >= 1) { if (pOrdinalMap->pOrdinalEntry[iOrdinal].dwOrdinal <= pOrdinalMap->pOrdinalEntry[iOrdinal-1].dwOrdinal) { wnsprintfA(szMsg, countof(szMsg), "dload: rows %u and %u of pOrdinalMap are out " "of order in dload!g_DllMap for pszDll=%s", iOrdinal-1, iOrdinal, pDll->pszDll); AssertFailedA(__FILE__, __LINE__, szMsg, TRUE); } } } } } } #endif // DBG const DLOAD_DLL_ENTRY* FindDll ( LPCSTR pszDll ) { const DLOAD_DLL_ENTRY* pDll = NULL; INT nResult; // These must be signed integers for the following binary search // to work correctly when iMiddle == 0 and nResult < 0. // INT iLow; INT iMiddle; INT iHigh; ASSERT(pszDll); ASSERT(StrLenA(pszDll) <= MAX_PATH); iLow = 0; iHigh = g_DllMap.NumberOfEntries - 1; while (iHigh >= iLow) { iMiddle = (iLow + iHigh) / 2; nResult = StrCmpIA (pszDll, g_DllMap.pDllEntry[iMiddle].pszDll); if (nResult < 0) { iHigh = iMiddle - 1; } else if (nResult > 0) { iLow = iMiddle + 1; } else { ASSERT (0 == nResult); pDll = &g_DllMap.pDllEntry[iMiddle]; break; } } return pDll; } FARPROC LookupHandlerByName ( LPCSTR pszProcName, const DLOAD_PROCNAME_MAP* pMap ) { FARPROC pfnHandler = NULL; INT nResult; // These must be signed integers for the following binary search // to work correctly when iMiddle == 0 and nResult < 0. // INT iLow; INT iMiddle; INT iHigh; ASSERT (pszProcName); iLow = 0; iHigh = pMap->NumberOfEntries - 1; while (iHigh >= iLow) { iMiddle = (iLow + iHigh) / 2; nResult = StrCmpA ( pszProcName, pMap->pProcNameEntry[iMiddle].pszProcName); if (nResult < 0) { iHigh = iMiddle - 1; } else if (nResult > 0) { iLow = iMiddle + 1; } else { ASSERT (0 == nResult); pfnHandler = pMap->pProcNameEntry[iMiddle].pfnProc; break; } } return pfnHandler; } FARPROC LookupHandlerByOrdinal ( DWORD dwOrdinal, const DLOAD_ORDINAL_MAP* pMap ) { FARPROC pfnHandler = NULL; DWORD dwOrdinalProbe; // These must be signed integers for the following binary search // to work correctly when iMiddle == 0 and dwOrdinal < dwOrdinalProbe. // INT iLow; INT iMiddle; INT iHigh; iLow = 0; iHigh = pMap->NumberOfEntries - 1; while (iHigh >= iLow) { iMiddle = (iLow + iHigh) / 2; dwOrdinalProbe = pMap->pOrdinalEntry[iMiddle].dwOrdinal; if (dwOrdinal < dwOrdinalProbe) { iHigh = iMiddle - 1; } else if (dwOrdinal > dwOrdinalProbe) { iLow = iMiddle + 1; } else { ASSERT (dwOrdinal == dwOrdinalProbe); pfnHandler = pMap->pOrdinalEntry[iMiddle].pfnProc; break; } } return pfnHandler; } FARPROC LookupHandler ( PDelayLoadInfo pDelayInfo ) { FARPROC pfnHandler = NULL; const DLOAD_DLL_ENTRY* pDll; ASSERT (pDelayInfo); #if DBG AssertDelayLoadFailureMapsAreSorted(); #endif // Find the DLL record if we have one. // pDll = FindDll (pDelayInfo->szDll); if (pDll) { // Now find the handler whether it be by name or ordinal. // if (pDelayInfo->dlp.fImportByName && pDll->pProcNameMap) { pfnHandler = LookupHandlerByName ( pDelayInfo->dlp.szProcName, pDll->pProcNameMap); } else if (pDll->pOrdinalMap) { pfnHandler = LookupHandlerByOrdinal ( pDelayInfo->dlp.dwOrdinal, pDll->pOrdinalMap); } } return pfnHandler; } #if DBG #define DBG_ERROR 0 #define DBG_INFO 1 //+--------------------------------------------------------------------------- // Trace a message to the debug console. Prefix with who we are so // people know who to contact. // INT __cdecl DbgTrace ( INT nLevel, PCSTR Format, ... ) { INT cch = 0; if (nLevel >= DBG_INFO) { CHAR szBuf [1024]; va_list argptr; va_start(argptr, Format); cch = wvnsprintfA(szBuf, countof(szBuf), Format, argptr); va_end(argptr); OutputDebugStringA("dload: "); OutputDebugStringA(szBuf); } return cch; } #endif // DBG //+--------------------------------------------------------------------------- // // FARPROC WINAPI DelayLoadFailureHook ( UINT unReason, PDelayLoadInfo pDelayInfo ) { FARPROC ReturnValue = NULL; // According to the docs, this parameter is always supplied. // ASSERT (pDelayInfo); // Trace some potentially useful information about why we were called. // #if DBG if (pDelayInfo->dlp.fImportByName) { DbgTrace (DBG_INFO, "%s: Dll=%s, ProcName=%s\r\n", (dliFailLoadLib == unReason) ? "FailLoadLib" : "FailGetProc", pDelayInfo->szDll, pDelayInfo->dlp.szProcName); } else { DbgTrace (DBG_INFO, "%s: Dll=%s, Ordinal=%u\r\n", (dliFailLoadLib == unReason) ? "FailLoadLib" : "FailGetProc", pDelayInfo->szDll, pDelayInfo->dlp.dwOrdinal); } #endif // For a failed LoadLibrary, we will return the HINSTANCE of this DLL. // This will cause the loader to try a GetProcAddress on our DLL for the // function. This will subsequently fail and then we will be called // for dliFailGetProc below. // if (dliFailLoadLib == unReason) { ReturnValue = (FARPROC)g_hBaseDllHandle; } // The loader is asking us to return a pointer to a procedure. // Lookup the handler for this DLL/procedure and, if found, return it. // If we don't find it, we'll assert with a message about the missing // handler. // else if (dliFailGetProc == unReason) { FARPROC pfnHandler; // Try to find an error handler for the DLL/procedure. // pfnHandler = LookupHandler (pDelayInfo); if (pfnHandler) { #if DBG DbgTrace (DBG_INFO, "Returning handler function at address 0x%08x\r\n", (LONG_PTR)pfnHandler); #endif // Do this on behalf of the handler now that it is about to // be called. // SetLastError (ERROR_MOD_NOT_FOUND); } #if DBG else { CHAR szMsg[MAX_PATH]; if (pDelayInfo->dlp.fImportByName) { wnsprintfA(szMsg, countof(szMsg), "No delayload handler found for Dll=%s, ProcName=%s\r\n", pDelayInfo->szDll, pDelayInfo->dlp.szProcName); } else { wnsprintfA(szMsg, countof(szMsg), "No delayload handler found for Dll=%s, Ordinal=%u\r\n", pDelayInfo->szDll, pDelayInfo->dlp.dwOrdinal); } AssertFailedA(__FILE__, __LINE__, szMsg, TRUE); } #endif ReturnValue = pfnHandler; } #if DBG else { ASSERT (NULL == ReturnValue); DbgTrace (DBG_INFO, "Unknown unReason (%u) passed to DelayLoadFailureHook. Ignoring.\r\n", unReason); } #endif return ReturnValue; }