如何在另一个 DLL 上使用 '/DELAYLOAD' 的 DLL

How to use `/DELAYLOAD` on a DLL from another DLL

本文关键字:DLL DELAYLOAD 另一个      更新时间:2023-10-16

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

可以在生成 .EXE或.DLL项目。

主 DLL 的项目正确引用了 UI 项目。如果我不使用/DELAYLOAD,每个薄都很好用。这两个 DLL 将安装到同一个目录中,所以我认为从另一个 DLL 中加载一个 DLL 应该很容易。但显然,事实并非如此。

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

事实上,根据这个答案,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 调用。因此,与使用LoadLibrary()GetProcAddress()的"老派"方式相比,我再也看不到/DELAYLOAD的优势了。

问题

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

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

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

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

如何从主 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;