Windows 延迟加载DLL是否有助于避免链接特定功能?

Windows 延迟加载DLL是否有助于避免链接特定功能?,windows,winapi,dll,dllimport,delay-load,Windows,Winapi,Dll,Dllimport,Delay Load,考虑以下代码,它需要调用User32.dll中的两个函数之一 if( IsWindowsVistaOrGreater() ) AddClipboardFormatListener(hWnd) ; else SetClipboardViewer(hWnd) ; 如果我没有弄错的话,这个程序将无法在WinXP上启动,因为XP下的User32.dll中不存在AddClipboardFormatListener 解决此问题的一种方法是不直接调用AddClipboardFormatLis

考虑以下代码,它需要调用
User32.dll
中的两个函数之一

if( IsWindowsVistaOrGreater() )
    AddClipboardFormatListener(hWnd) ;
else
    SetClipboardViewer(hWnd) ;
如果我没有弄错的话,这个程序将无法在WinXP上启动,因为XP下的
User32.dll
中不存在
AddClipboardFormatListener

解决此问题的一种方法是不直接调用
AddClipboardFormatListener
,而是自己获取指向它的指针:
GetProcAddress(GetModuleHandle(“User32.dll”),“AddClipboardFormatListener”)

但是,如果我指示链接器延迟加载
User32.dll

  • 这是否可以避免在XP下加载特定函数,这样我就不需要调用
    GetModuleHandle
    GetProcAddress
  • 当只需要延迟加载少数函数时,是否建议延迟加载DLL
  • 在第二点上,
    User32.dll
    的情况特别引人注目,因为程序中使用的大多数函数都存在于所有Windows版本的dll中。
    我猜加载时的链接比运行时的链接更有效,因为后者在每次函数调用之前都需要额外的检查。
    但我只是在猜测,所以问题就来了

    这是否可以避免在XP下加载特定函数,这样我就不需要调用
    GetModuleHandle
    GetProcAddress

    对。这正是延迟加载发明的目的。代码可以像静态链接一样调用DLL函数,但在第一次实际调用该函数之前,可执行文件不会在运行时加载DLL函数指针。在内部,延迟加载机制为您使用
    LoadLibrary()
    GetProcAddress()

    当只需要延迟加载少数函数时,是否建议延迟加载DLL

    如果一个DLL是延迟加载的,那么它的所有函数都是延迟加载的,您不能选择想要的函数。因此,如果您的应用程序需要使用同一DLL中的许多函数,如
    user32.DLL
    ,那么静态链接通常更有效,然后您可以手动使用
    GetProcAddress()
    来处理确实需要不同处理的少数函数

    在这种情况下,我建议完全取消OS检查,只依赖DLL函数是否实际存在,例如:

    typedef BOOL (WINAPI *LPFN_ACFL)(HWND);
    
    LPFN_ACFL lpAddClipboardFormatListener = (LPFN_ACFL) GetProcAddress(GetModuleHandle(TEXT("user32")), "AddClipboardFormatListener");
    
    if( lpAddClipboardFormatListener != NULL )
        lpAddClipboardFormatListener(hWnd);
    else
        SetClipboardViewer(hWnd);
    
    延迟加载的真正亮点在于它的挂钩。例如,在早期的系统上,您可以使用延迟加载失败挂钩来实现自己版本的
    AddClipboardFormatListener()
    ,然后您的主代码就可以在所有系统上无条件地调用
    AddClipboardFormatListener()
    ,它不会知道其中的区别。例如(只是一个演示,没有实际测试):


    1.是的,因为getprocaddress实际上在第一次调用时调用(仅在第一次调用时)2。是的,这是标准用例是的,您可以为
    user32.dll使用delayload
    -在这种情况下,您不需要直接调用
    GetProcAddress
    -所有任务都将在
    \uu delayLoadHelper2
    中完成。在本例中,您可以将代码snipet用作参考文档的isLink:。
    LRESULT CALLBACK ClipboardSubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
    {
        switch (uMsg)
        {
            case WM_NCDESTROY:
                RemoveWindowSubclass(hWnd, &ClipboardSubClassProc, uIdSubclass);
                break;
    
            case WM_CHANGECBCHAIN:
            {
                if (wParam == dwRefData) 
                    SetWindowSubclass(hWnd, &ClipboardSubClassProc, uIdSubclass, lParam);
    
                else if (dwRefData != 0) 
                    SendMessage((HWND)dwRefData, uMsg, wParam, lParam); 
    
                break;
            }
    
            case WM_DRAWCLIPBOARD:
            {
                SendMessage(hWnd, WM_CLIPBOARDUPDATE, 0, 0);
    
                if (dwRefData != 0)
                    SendMessage((HWND)dwRefData, uMsg, wParam, lParam);
    
                break;
            }
        }
    
        return DefSubclassProc(hWnd, uMsg, wParam, lParam);
    }
    
    BOOL WINAPI My_AddClipboardFormatListener(HWND hWnd)
    {
        HWND hWndNext = SetClipboardViewer(hWnd);
        if ((!hWndNext) && (GetLastError() != 0))
            return FALSE;
    
        if (!SetWindowSubclass(hWnd, &ClipboardSubClassProc, 1, (DWORD_PTR)hWndNext))
        {
            DWORD dwErr = GetLastError();
            ChangeClipboardChain(hWnd, hwndNext);
            SetLastError(dwErr);
            return FALSE;
        }
    
        return TRUE;
    }
    
    BOOL WINAPI My_RemoveClipboardFormatListener(HWND hWnd)
    {
        DWORD_PTR dwRefData;
        if (!GetWindowSubclass(hWnd, &ClipboardSubClassProc, 1, &dwRefData))
        {
            SetLastError(ERROR_NOT_FOUND);
            return FALSE;
        }
    
        RemoveWindowSubclass(hWnd, &ClipboardSubClassProc, 1);
    
        return ChangeClipboardChain(hWnd, (HWND)dwRefData);
    }
    
    FARPROC WINAPI MyDliFailureHook(unsigned dliNotify, PDelayLoadInfo pdli)
    {
        if ((dliNotify == dliFailGetProc) && (pdli->dlp.fImportByName))
        {
            if (strcmp(pdli->dlp.szProcName, "AddClipboardFormatListener") == 0)
                return (FARPROC) &My_AddClipboardFormatListener;
    
            if (strcmp(pdli->dlp.szProcName, "RemoveClipboardFormatListener") == 0)
                return (FARPROC) &My_RemoveClipboardFormatListener;
        }
    
        return NULL;
    }  
    
    __pfnDliFailureHook2 = &MyDliFailureHook;
    
    ...
    
    LRESULT CALLBACK MyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            case WM_CREATE:
                AddClipboardFormatListener(hWnd);
                break;
    
            case WM_DESTROY: 
                RemoveClipboardFormatListener(hWnd);
                break;
    
            case WM_CLIPBOARDUPDATE:
                // do all of your clipboard processing here...
                break;
    
            ...
        }
    
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }