【问题标题】:How to use `/DELAYLOAD` on a DLL from another DLL如何在来自另一个 DLL 的 DLL 上使用`/DELAYLOAD`
【发布时间】:2019-08-25 02:11:53
【问题描述】:

我有一个包含两个 DLL 的解决方案。第一个是“主”DLL。它恰好是一个 ODBC 驱动程序,但我认为这对于这个问题并不重要。 第二个 DLL 包含第一个的所有 UI 逻辑。由于并不总是需要 UI,我想使用 /DELAYLOAD 功能,它明确表示:

DLL 的延迟加载可以在构建过程中指定 .EXE 或 .DLL 项目。

主 DLL 的项目正确地引用了 UI 项目。如果我不使用/DELAYLOAD,一切正常。这两个 DLL 将安装到同一个目录中,所以我认为从另一个 DLL 中加载一个 DLL 应该很容易。但显然不是。

一旦调用 UI DLL 中的第一个函数,应用程序(在我的例子中是任何 ODBC 客户端)就会崩溃。 GetLastError() 产生 126,这显然意味着在任何搜索路径中都找不到目标 DLL。

确实,根据this answer LoadLibrary() 确实查看了调用可执行文件的目录,但没有查看当前执行的DLL之一。我假设/DELAYLOAD 也只是在后台使用LoadLibrary(),对吗?

如果我将可执行文件复制到我的驱动程序的安装目录中,它可以正常工作,这证明了我的假设,它只是不在当前 DLL 的目录中查找。

除此之外,我还可以通过调用使其运行

LoadLibrary(L"C:\\absolute\\path\\to\\UI.dll");

就在加载 UI DLL 的第一个函数之前。 我还能够使用

以编程方式确定此路径
wchar_t buffer[512];
GetModuleFileName(hThisDLL, buffer, sizeof(buffer));

但是我必须用这个逻辑覆盖每一个 UI 调用。所以我不会再看到/DELAYLOAD 比使用LoadLibrary()GetProcAddress() 的“老派”方式有太多优势了。

问题

有没有简单的方法让/DELAYLOAD从同一目录下的另一个DLL中找到目标DLL?

【问题讨论】:

  • 写自己的__pfnDliNotifyHook2并处理dliNotePreLoadLibrary
  • 在调用 UI DLL 的任何导出之前,将 /DELAYLOAD 与对 SetDllDirectoryW 的调用组合以将 UI DLL 的目录添加到搜索路径。不需要其他任何东西。
  • 使用AddDllDirectory(而不是SetDllDirectory)和LoadLibraryEx添加额外的搜索路径来加载DLL而不影响以后的DLL加载搜索路径。

标签: c++ visual-studio winapi dll


【解决方案1】:

有。我的建议是创建一个延迟加载失败挂钩函数。

https://docs.microsoft.com/en-us/cpp/build/reference/failure-hooks?view=vs-2019

基本上,您在主 DLL 中编写一个函数,该函数会在延迟加载失败时得到通知。在该函数中,当给定代码指示失败时,您尝试手动调用 LoadLibrary,其路径由主 DLL 所在的文件夹加上加载失败的 DLL 的名称组成

如何从主 DLL 中获取主 DLL 取决于您。有很多方法。

类似这样的:

FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli)
{

    FARPROC fpRet = NULL;

    switch (dliNotify)
    {
      case dliStartProcessing:           
        break;

      case dliNotePreLoadLibrary:
        break;

      case dliNotePreGetProcAddress:
        break;

      case dliFailLoadLib: 
        {
            std::string newPath = GetMyModulePath();
            newPath += "\\";
            newPath  += pdli->szDll;
            fpRet = reinterpret_cast<FARPROC>(::LoadLibrary(csDir));
        }

        break;

      case dliFailGetProc:

        break;

      case dliNoteEndProcessing: 
        break;

      default:  
          break;
    }

    return fpRet;
}

//
// Set access to our delay load hook.
//

PfnDliHook __pfnDliFailureHook2 = delayHook;

【讨论】:

  • 可能和这个,但最好使用__pfnDliNotifyHook2 而不是__pfnDliFailureHook2
  • 我想这取决于您的情况。在使用上述方法的情况下,有问题的 DLL 在某些情况下是正确的路径,而在其他情况下则不是。如果它有效,我们想使用默认处理并将其用作后备。如果你一直都知道它会在哪里,也许不知道
  • 默认处理只是调用LoadLibrary 获取名称。如果失败 - 如果存在,请致电 __pfnDliFailureHook2 。当LoadLibrary 的短名称失败时,我们不能等待,而是根据已知的 dll 名称来处理这种情况。当我们了解在某些情况下需要特殊处理时
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-29
  • 2012-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多