2017-07-27 43 views
2

考虑下面的代码需要调用User32.dll中的两个函数之一。延迟加载的DLL有用于避免链接特定的功能吗?

if(IsWindowsVistaOrGreater()) 
    AddClipboardFormatListener(hWnd) ; 
else 
    SetClipboardViewer(hWnd) ; 

如果我没有记错,这个程序将无法启动在WinXP因为AddClipboardFormatListener不存在User32.dll下XP。要解决这不是直接调用AddClipboardFormatListener,而是获得一个指向它自己

方式一:
GetProcAddress(GetModuleHandle("User32.dll"), "AddClipboardFormatListener")

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

  1. 这会避免装载的是XP下的特定功能,这样我就不需要调用GetModuleHandleGetProcAddress
  2. 当只有少数函数需要延迟加载时,推荐延迟加载DLL吗?

User32.dll的情况在第二点上特别引人注目,因为程序中使用的大多数函数都知道存在于所有Windows版本的该DLL中。
我猜想加载时链接比运行时更有效,因为后者在每次函数调用之前都需要额外的检查。
但我只是猜测,因此这个问题。

+0

1.是,作为GetProcAddress的实际要求第一次调用(仅在第一次)2.是的,这是标准的用例 –

+0

是的,你可以使用DELAYLOAD对于'user32.dll' - 在这种情况下,您不需要直接调用'GetProcAddress' - 所有任务都将在'__delayLoadHelper2'内完成。并且你可以在这种情况下使用snipet代码 – RbMm

回答

3

这是否避免在XP下加载该特定功能,以便我不需要拨打GetModuleHandleGetProcAddress

是的。这正是发明延迟加载的情况。您的代码可以调用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(),并且它不会知道区别。例如(只是一个演示,没有实际测试过):

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); 
} 
+0

链接到参考文档:[链接器支持延迟加载的DLL](https://docs.microsoft.com/en-us/cpp/build/reference/linker-support - 用于延迟加载的dll)。 – IInspectable