我有两个DLL的解决方案。第一个是“主”DLL。它碰巧是一个ODBC驱动程序,但我认为这对于这个问题并不重要。第二个DLL包含第一个DLL的所有UI逻辑。由于并不总是需要UI,我想使用明确说明的/DELAYLOAD
功能:
可以在构建.EXE或.DLL项目期间指定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?
有。我的建议是创建一个延迟加载失败挂钩函数。
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;