windows-nt/Source/XPSP1/NT/shell/inc/dllload.c
2020-09-26 16:20:57 +08:00

478 lines
20 KiB
C

// You are expected to #include this file from your private dllload.c.
// WARNING! Failure to observe these rules will result in really subtle
// (and horrible) runtime/build problems.
//
// WARNING! If you choose to delay-load a DLL, you **must** disable
// import thunks for that DLL when you include its header file. For example,
// you must #define _OLE32_ before you #include <ole32.h>. Otherwise,
// what happens is that the linker ends up exporting your delayload thunk
// (because it thinks it's an exported function because the header file
// said so) and then everybody who links to your DLL will accidentally
// use the delayload thunk instead of the original function. This is
// particularly gruesome because this problem hoses every DLL in the system
// **except for you**.
//
// WARNING! But if you are delay-loading an optional function and
// you are statically linking to the target DLL for required functions
// (e.g., you are delayloading an NT5-only function), then you should not
// disable import thunks since you really do want import thunks for the
// regular functions. The way do handle this is to delayload the function
// with an alternate name (e.g., prepend an underscore) and use the MAP
// version of the delay-load macros. Then in your global header file,
// #define the function to its mapped name.
//
// End of warnings.
// Delay loading mechanism. This allows you to write code as if you are
// calling implicitly linked APIs, and yet have these APIs really be
// explicitly linked. You can reduce the initial number of DLLs that
// are loaded (load on demand) using this technique.
//
// Use the following macros to indicate which APIs/DLLs are delay-linked
// and -loaded.
//
// DELAY_LOAD
// DELAY_LOAD_HRESULT
// DELAY_LOAD_SAFEARRAY
// DELAY_LOAD_UINT
// DELAY_LOAD_INT
// DELAY_LOAD_VOID
//
// Use these macros for APIs that are exported by ordinal only.
//
// DELAY_LOAD_ORD
// DELAY_LOAD_VOID_ORD
//
// Use these macros for APIs that only exist on the integrated-shell
// installations (i.e., a new shell32 is on the system).
//
// DELAY_LOAD_SHELL
// DELAY_LOAD_SHELL_HRESULT
// DELAY_LOAD_SHELL_VOID
//
//
// Use DELAY_LOAD_IE_* for APIs that come from BrowseUI. This used
// to be important when BrowseUI was in the IEXPLORE directory, but
// now it's in the System directory so the difference is pretty
// meaningless.
//
// Use DELAY_LOAD_OCX_* for APIs that come from OCXs and not DLLs.
//
/**********************************************************************/
#ifdef DEBUG
void _DumpLoading(LPTSTR pszDLL, LPTSTR pszFunc)
{
#ifdef DF_DELAYLOADDLL
if (g_dwDumpFlags & DF_DELAYLOADDLL)
{
TraceMsg(TF_ALWAYS, "DLLLOAD: Loading %s for the first time for %s",
pszDLL, pszFunc);
}
#endif
}
#define ENSURE_LOADED(_hmod, _dll, _ext, pszfn) \
(_hmod ? (_hmod) : (_DumpLoading(TEXT(#_dll) TEXT(".") TEXT(#_ext), pszfn), \
_hmod = LoadLibraryA(#_dll "." #_ext)))
#else
#define ENSURE_LOADED(_hmod, _dll, _ext, pszfn) \
(_hmod ? (_hmod) : (_hmod = LoadLibraryA(#_dll "." #_ext)))
#endif // DEBUG
/**********************************************************************/
void _GetProcFromDLL(HMODULE* phmod, LPCSTR pszDLL, FARPROC* ppfn, LPCSTR pszProc)
{
#ifdef DEBUG
CHAR szProcD[MAX_PATH];
if (!IS_INTRESOURCE(pszProc)) {
lstrcpynA(szProcD, pszProc, ARRAYSIZE(szProcD));
} else {
wsprintfA(szProcD, "(ordinal %d)", LOWORD((DWORD_PTR)pszProc));
}
#endif
// If it's already loaded, return.
if (*ppfn) {
return;
}
if (*phmod == NULL) {
#ifdef DEBUG
#ifdef DF_DELAYLOADDLL
if (g_dwDumpFlags & DF_DELAYLOADDLL)
{
TraceMsg(TF_ALWAYS, "DLLLOAD: Loading %s for the first time for %s",
pszDLL, szProcD);
}
#endif
if (g_dwBreakFlags & 0x00000080)
{
DebugBreak();
}
#endif
*phmod = LoadLibraryA(pszDLL);
#ifdef UNIX
if (*phmod == NULL) {
if (lstrcmpiA(pszDLL, "inetcpl.dll") == 0) {
*phmod = LoadLibraryA("inetcpl.cpl");
}
}
#endif
if (*phmod == NULL) {
return;
}
}
#if defined(DEBUG) && defined(DF_DELAYLOADDLL)
if (g_dwDumpFlags & DF_DELAYLOADDLL) {
TraceMsg(TF_ALWAYS, "DLLLOAD: GetProc'ing %s from %s for the first time",
pszDLL, szProcD);
}
#endif
*ppfn = GetProcAddress(*phmod, pszProc);
}
#if defined(DEBUG) && defined(BROWSEUI_IN_IEXPLORE_DIRECTORY)
void _GetProcFromSystemDLL(HMODULE* phmod, LPCSTR pszDLL, FARPROC* ppfn, LPCSTR pszProc)
{
#ifdef UNIX
if (lstrcmpiA(pszDLL, "inetcpl.dll") == 0) {
_GetProcFromDLL(phmod, "inetcpl.cpl", ppfn, pszProc);
return;
}
#endif
// You must use DELAY_LOAD_IE for BROWSEUI since BROWSEUI lives in the
// IE directory, not the System directory.
if (lstrcmpiA(pszDLL, "BROWSEUI.DLL") == 0) {
ASSERT(!"Somebody used DELAY_LOAD instead of DELAY_LOAD_IE on BROWSEUI");
}
_GetProcFromDLL(phmod, pszDLL, ppfn, pszProc);
}
#else
#define _GetProcFromSystemDLL _GetProcFromDLL
#endif
// NOTE: this takes two parameters that are the function name. the First (_fn) is the name that
// NOTE: the function will be called in this DLL and the other (_fni) is the
// NOTE: name of the function we will GetProcAddress. This helps get around functions that
// NOTE: are defined in the header files with _declspec...
//
// HMODULE _hmod - where we cache the HMODULE (aka HINSTANCE)
// _dll - Basename of the target DLL, not quoted
// _ext - Extension of the target DLL, not quoted (usually DLL)
// _ret - Data type of return value
// _fnpriv - Local name for the function
// _fn - Exported name for the function
// _args - Argument list in the form (TYPE1 arg1, TYPE2 arg2, ...)
// _nargs - Argument list in the form (arg1, arg2, ...)
// _err - Return value if we can't call the actual function
//
#define DELAY_LOAD_NAME_EXT_ERR(_hmod, _dll, _ext, _ret, _fnpriv, _fn, _args, _nargs, _err) \
_ret __stdcall _fnpriv _args \
{ \
static _ret (__stdcall *_pfn##_fn) _args = NULL; \
_GetProcFromSystemDLL(&_hmod, #_dll "." #_ext, (FARPROC*)&_pfn##_fn, #_fn); \
if (_pfn##_fn) \
return _pfn##_fn _nargs; \
return (_ret)_err; \
}
#define DELAY_LOAD_NAME_ERR(_hmod, _dll, _ret, _fnpriv, _fn, _args, _nargs, _err) \
DELAY_LOAD_NAME_EXT_ERR(_hmod, _dll, DLL, _ret, _fnpriv, _fn, _args, _nargs, _err)
#define DELAY_LOAD_ERR(_hmod, _dll, _ret, _fn, _args, _nargs, _err) \
DELAY_LOAD_NAME_ERR(_hmod, _dll, _ret, _fn, _fn, _args, _nargs, _err)
#define DELAY_LOAD(_hmod, _dll, _ret, _fn, _args, _nargs) DELAY_LOAD_ERR(_hmod, _dll, _ret, _fn, _args, _nargs, 0)
#define DELAY_LOAD_HRESULT(_hmod, _dll, _fn, _args, _nargs) DELAY_LOAD_ERR(_hmod, _dll, HRESULT, _fn, _args, _nargs, E_FAIL)
#define DELAY_LOAD_SAFEARRAY(_hmod, _dll, _fn, _args, _nargs) DELAY_LOAD_ERR(_hmod, _dll, SAFEARRAY *, _fn, _args, _nargs, NULL)
#define DELAY_LOAD_UINT(_hmod, _dll, _fn, _args, _nargs) DELAY_LOAD_ERR(_hmod, _dll, UINT, _fn, _args, _nargs, 0)
#define DELAY_LOAD_INT(_hmod, _dll, _fn, _args, _nargs) DELAY_LOAD_ERR(_hmod, _dll, INT, _fn, _args, _nargs, 0)
#define DELAY_LOAD_BOOL(_hmod, _dll, _fn, _args, _nargs) DELAY_LOAD_ERR(_hmod, _dll, BOOL, _fn, _args, _nargs, FALSE)
#define DELAY_LOAD_BOOLEAN(_hmod, _dll, _fn, _args, _nargs) DELAY_LOAD_ERR(_hmod, _dll, BOOLEAN, _fn, _args, _nargs, FALSE)
#define DELAY_LOAD_DWORD(_hmod, _dll, _fn, _args, _nargs) DELAY_LOAD_ERR(_hmod, _dll, DWORD, _fn, _args, _nargs, FALSE)
#define DELAY_LOAD_LRESULT(_hmod, _dll, _fn, _args, _nargs) DELAY_LOAD_ERR(_hmod, _dll, LRESULT, _fn, _args, _nargs, FALSE)
#define DELAY_LOAD_WNET(_hmod, _dll, _fn, _args, _nargs) DELAY_LOAD_ERR(_hmod, _dll, DWORD, _fn, _args, _nargs, WN_NOT_SUPPORTED)
#define DELAY_LOAD_LPVOID(_hmod, _dll, _fn, _args, _nargs) DELAY_LOAD_ERR(_hmod, _dll, LPVOID, _fn, _args, _nargs, 0)
// the NAME variants allow the local function to be called something different from the imported
// function to avoid dll linkage problems.
#define DELAY_LOAD_NAME(_hmod, _dll, _ret, _fn, _fni, _args, _nargs) DELAY_LOAD_NAME_ERR(_hmod, _dll, _ret, _fn, _fni, _args, _nargs, 0)
#define DELAY_LOAD_NAME_HRESULT(_hmod, _dll, _fn, _fni, _args, _nargs) DELAY_LOAD_NAME_ERR(_hmod, _dll, HRESULT, _fn, _fni, _args, _nargs, E_FAIL)
#define DELAY_LOAD_NAME_SAFEARRAY(_hmod, _dll, _fn, _fni, _args, _nargs) DELAY_LOAD_NAME_ERR(_hmod, _dll, SAFEARRAY *, _fn, _fni, _args, _nargs, NULL)
#define DELAY_LOAD_NAME_UINT(_hmod, _dll, _fn, _fni, _args, _nargs) DELAY_LOAD_NAME_ERR(_hmod, _dll, UINT, _fn, _fni, _args, _nargs, 0)
#define DELAY_LOAD_NAME_BOOL(_hmod, _dll, _fn, _fni, _args, _nargs) DELAY_LOAD_NAME_ERR(_hmod, _dll, BOOL, _fn, _fni, _args, _nargs, FALSE)
#define DELAY_LOAD_NAME_DWORD(_hmod, _dll, _fn, _fni, _args, _nargs) DELAY_LOAD_NAME_ERR(_hmod, _dll, DWORD, _fn, _fni, _args, _nargs, 0)
#define DELAY_LOAD_NAME_VOID(_hmod, _dll, _fn, _fni, _args, _nargs) \
void __stdcall _fn _args \
{ \
static void (__stdcall *_pfn##_fni) _args = NULL; \
if (!ENSURE_LOADED(_hmod, _dll, DLL, TEXT(#_fni))) \
{ \
AssertMsg(BOOLFROMPTR(_hmod), TEXT("LoadLibrary failed on ") ## TEXT(#_dll)); \
return; \
} \
if (_pfn##_fni == NULL) \
{ \
*(FARPROC*)&(_pfn##_fni) = GetProcAddress(_hmod, #_fni); \
AssertMsg(BOOLFROMPTR(_pfn##_fni), TEXT("GetProcAddress failed on ") ## TEXT(#_fni)); \
if (_pfn##_fni == NULL) \
return; \
} \
_pfn##_fni _nargs; \
}
#define DELAY_LOAD_VOID(_hmod, _dll, _fn, _args, _nargs) DELAY_LOAD_NAME_VOID(_hmod, _dll, _fn, _fn, _args, _nargs)
// For private entrypoints exported by ordinal.
#define DELAY_LOAD_ORD_ERR(_hmod, _dll, _ret, _fn, _ord, _args, _nargs, _err) \
_ret __stdcall _fn _args \
{ \
static _ret (__stdcall *_pfn##_fn) _args = NULL; \
_GetProcFromSystemDLL(&_hmod, #_dll, (FARPROC*)&_pfn##_fn, (LPCSTR)_ord); \
if (_pfn##_fn) \
return _pfn##_fn _nargs; \
return (_ret)_err; \
}
#define DELAY_LOAD_ORD(_hmod, _dll, _ret, _fn, _ord, _args, _nargs) DELAY_LOAD_ORD_ERR(_hmod, _dll, _ret, _fn, _ord, _args, _nargs, 0)
#define DELAY_LOAD_EXT_ORD(_hmod, _dll, _ext, _ret, _fn, _ord, _args, _nargs) DELAY_LOAD_ORD_ERR(_hmod, #_dll "." #_ext, _ret, _fn, _ord, _args, _nargs, 0)
#define DELAY_LOAD_ORD_VOID(_hmod, _dll, _fn, _ord, _args, _nargs) \
void __stdcall _fn _args \
{ \
static void (__stdcall *_pfn##_fn) _args = NULL; \
_GetProcFromSystemDLL(&_hmod, #_dll, (FARPROC*)&_pfn##_fn, (LPCSTR)_ord); \
if (_pfn##_fn) \
_pfn##_fn _nargs; \
return; \
}
#define DELAY_LOAD_VOID_ORD DELAY_LOAD_ORD_VOID // cuz people mess this up all the time
#define DELAY_LOAD_ORD_BOOL(_hmod, _dll, _fn, _ord, _args, _nargs) \
DELAY_LOAD_ORD_ERR(_hmod, _dll, BOOL, _fn, _ord, _args, _nargs, 0)
#define DELAY_LOAD_EXT(_hmod, _dll, _ext, _ret, _fn, _args, _nargs) \
DELAY_LOAD_NAME_EXT_ERR(_hmod, _dll, _ext, _ret, _fn, _fn, _args, _nargs, 0)
#define DELAY_LOAD_EXT_WRAP(_hmod, _dll, _ext, _ret, _fnWrap, _fnOrig, _args, _nargs) \
DELAY_LOAD_NAME_EXT_ERR(_hmod, _dll, _ext, _ret, _fnWrap, _fnOrig, _args, _nargs, 0)
#if defined(BROWSEUI_IN_IEXPLORE_DIRECTORY) || defined(UNIX)
/*----------------------------------------------------------
Purpose: Loads the DLL via a CLSID it is known to be registered for
*/
void _GetProcFromCLSID(HMODULE* phmod, const CLSID *pclsid, FARPROC* ppfn, LPCSTR pszProc)
{
if (*phmod == NULL) {
//
// SHPinDLLOfCLSID does all the annoying work of opening the
// appropriate registry key, doing REG_EXPAND_SZ, etc.
// It also loads the DLL with exactly the same name that OLE does,
// which is important because NT4 SP3 didn't like it when you loaded
// a DLL sometimes via SFN and sometimes via LFN. (It would
// think they were different DLLs, and two copies of it got loaded
// into memory. Aigh!)
//
*phmod = (HMODULE)SHPinDllOfCLSID(pclsid);
if (!*phmod)
return;
}
// We don't know the name of the DLL, but fortunately _GetProcFromDLL
// doesn't need it if *phmod is already filled in.
ASSERT(*phmod);
_GetProcFromDLL(phmod, "", ppfn, pszProc);
}
//
// Private exports by ordinal for browseui. loads from the browseui in apppath dir
//
#ifndef UNIX
#define DELAY_LOAD_IE_ORD_ERR(_hmod, _dll, _ret, _fn, _ord, _args, _nargs, _err) \
_ret __stdcall _fn _args \
{ \
static _ret (__stdcall *_pfn##_fn) _args = NULL; \
_GetProcFromCLSID(&_hmod, &CLSID_##_dll, (FARPROC*)&_pfn##_fn, (LPCSTR)_ord); \
if (_pfn##_fn) \
return _pfn##_fn _nargs; \
return (_ret)_err; \
}
#else
#define DELAY_LOAD_IE_ORD_ERR(_hmod, _dll, _ret, _fn, _ord, _args, _nargs, _err) \
_ret __stdcall _fn _args \
{ \
static _ret (__stdcall *_pfn##_fn) _args = NULL; \
_GetProcFromCLSID(&_hmod, &CLSID_##_dll, (FARPROC*)&_pfn##_fn, (LPCSTR)#_fn); \
if (_pfn##_fn) \
return _pfn##_fn _nargs; \
return (_ret)_err; \
}
#endif
#ifndef UNIX
#define DELAY_LOAD_IE_ORD_VOID(_hmod, _dll, _fn, _ord, _args, _nargs) \
void __stdcall _fn _args \
{ \
static void (__stdcall *_pfn##_fn) _args = NULL; \
_GetProcFromCLSID(&_hmod, &CLSID_##_dll, (FARPROC*)&_pfn##_fn, (LPCSTR)_ord); \
if (_pfn##_fn) \
_pfn##_fn _nargs; \
return; \
}
#else
#define DELAY_LOAD_IE_ORD_VOID(_hmod, _dll, _fn, _ord, _args, _nargs) \
void __stdcall _fn _args \
{ \
static void (__stdcall *_pfn##_fn) _args = NULL; \
_GetProcFromCLSID(&_hmod, &CLSID_##_dll, (FARPROC*)&_pfn##_fn, (LPCSTR)#_fn); \
if (_pfn##_fn) \
_pfn##_fn _nargs; \
return; \
}
#endif
#else // BrowseUI is in the System directory
#define DELAY_LOAD_IE_ORD_ERR DELAY_LOAD_ORD_ERR
#define DELAY_LOAD_IE_ORD_VOID DELAY_LOAD_ORD_VOID
#endif
#define DELAY_LOAD_IE(_hmod, _dll, _ret, _fn, _ord, _args, _nargs) DELAY_LOAD_IE_ORD_ERR(_hmod, _dll, _ret, _fn, _ord, _args, _nargs, 0)
#define DELAY_LOAD_IE_HRESULT(_hmod, _dll, _fn, _ord, _args, _nargs) DELAY_LOAD_IE_ORD_ERR(_hmod, _dll, HRESULT, _fn, _ord, _args, _nargs, E_FAIL)
#define DELAY_LOAD_IE_BOOL(_hmod, _dll, _fn, _ord, _args, _nargs) DELAY_LOAD_IE_ORD_ERR(_hmod, _dll, BOOL, _fn, _ord, _args, _nargs, FALSE)
#define DELAY_LOAD_IE_ORD(_hmod, _dll, _ret, _fn, _ord, _args, _nargs) DELAY_LOAD_IE_ORD_ERR(_hmod, _dll, _ret, _fn, _ord, _args, _nargs, 0)
#ifndef NO_LOADING_OF_SHDOCVW_ONLY_FOR_WHICHPLATFORM
/*----------------------------------------------------------
Purpose: Performs a loadlibrary on the DLL only if the machine
has the integrated shell installation.
*/
void _SHGetProcFromDLL(HINSTANCE* phinst, LPCSTR pszDLL, FARPROC* ppfn, LPCSTR pszProc)
{
if (PLATFORM_INTEGRATED == WhichPlatform())
_GetProcFromSystemDLL(phinst, pszDLL, ppfn, pszProc);
else
TraceMsg(TF_ERROR, "Could not load integrated shell version of %s for %d", pszDLL, pszProc);
}
#endif // NO_LOADING_OF_SHDOCVW_ONLY_FOR_WHICHPLATFORM
//
// Private exports by ordinal for integrated-shell installs
//
#define DELAY_LOAD_SHELL_ERR(_hinst, _dll, _ret, _fn, _ord, _args, _nargs, _err) \
_ret __stdcall _fn _args \
{ \
static _ret (__stdcall *_pfn##_fn) _args = NULL; \
_SHGetProcFromDLL(&_hinst, #_dll ".DLL", (FARPROC*)&_pfn##_fn, (LPCSTR)_ord); \
if (_pfn##_fn) \
return _pfn##_fn _nargs; \
return (_ret)_err; \
}
#define DELAY_LOAD_SHELL(_hinst, _dll, _ret, _fn, _ord, _args, _nargs) DELAY_LOAD_SHELL_ERR(_hinst, _dll, _ret, _fn, _ord, _args, _nargs, 0)
#define DELAY_LOAD_SHELL_HRESULT(_hinst, _dll, _fn, _ord, _args, _nargs ) DELAY_LOAD_SHELL_ERR(_hinst, _dll, HRESULT, _fn, _ord, _args, _nargs, E_FAIL )
#define DELAY_LOAD_SHELL_VOID(_hinst, _dll, _fn, _ord, _args, _nargs) \
void __stdcall _fn _args \
{ \
static void (__stdcall *_pfn##_fn) _args = NULL; \
_SHGetProcFromDLL(&_hinst, #_dll ".DLL", (FARPROC*)&_pfn##_fn, (LPCSTR)_ord); \
if (_pfn##_fn) \
_pfn##_fn _nargs; \
return; \
}
// Following Macros are functionally same as above only that they are
// using function name on UNIX rather than ordinals. The above macros
// are left untouched because other dlls like shdocvw/shdoc401 still use
// them.
#ifndef UNIX
#define DELAY_LOAD_SHELL_ERR_FN(_hinst, _dll, _ret, _fn, _ord, _args, _nargs, _err, _realfn) \
_ret __stdcall _fn _args \
{ \
static _ret (__stdcall *_pfn##_fn) _args = NULL; \
_SHGetProcFromDLL(&_hinst, #_dll ".DLL", (FARPROC*)&_pfn##_fn, (LPCSTR)_ord); \
if (_pfn##_fn) \
return _pfn##_fn _nargs; \
return (_ret)_err; \
}
#else
#define DELAY_LOAD_SHELL_ERR_FN(_hinst, _dll, _ret, _fn, _ord, _args, _nargs, _err, _realfn) \
_ret __stdcall _fn _args \
{ \
static _ret (__stdcall *_pfn##_fn) _args = NULL; \
_SHGetProcFromDLL(&_hinst, #_dll ".DLL", (FARPROC*)&_pfn##_fn, (LPCSTR)#_realfn); \
if (_pfn##_fn) \
return _pfn##_fn _nargs; \
return (_ret)_err; \
}
#endif
#define DELAY_LOAD_SHELL_FN(_hinst, _dll, _ret, _fn, _ord, _args, _nargs, _realfn) DELAY_LOAD_SHELL_ERR_FN(_hinst, _dll, _ret, _fn, _ord, _args, _nargs, 0, _realfn)
#define DELAY_LOAD_SHELL_HRESULT_FN(_hinst, _dll, _fn, _ord, _args, _nargs, realfn) DELAY_LOAD_SHELL_ERR_FN(_hinst, _dll, HRESULT, _fn, _ord, _args, _nargs, E_FAIL, _realfn)
#ifndef UNIX
#define DELAY_LOAD_SHELL_VOID_FN(_hinst, _dll, _fn, _ord, _args, _nargs, _realfn) \
void __stdcall _fn _args \
{ \
static void (__stdcall *_pfn##_fn) _args = NULL; \
_SHGetProcFromDLL(&_hinst, #_dll ".DLL", (FARPROC*)&_pfn##_fn, (LPCSTR)_ord); \
if (_pfn##_fn) \
_pfn##_fn _nargs; \
return; \
}
#else
#define DELAY_LOAD_SHELL_VOID_FN(_hinst, _dll, _fn, _ord, _args, _nargs, _realfn) \
void __stdcall _fn _args \
{ \
static void (__stdcall *_pfn##_fn) _args = NULL; \
_SHGetProcFromDLL(&_hinst, #_dll ".DLL", (FARPROC*)&_pfn##_fn, (LPCSTR)#_realfn); \
if (_pfn##_fn) \
_pfn##_fn _nargs; \
return; \
}
#endif