我无法使动态加载工作,但我可以使用Delay loading工作。
我有什么做的是回到提取的.lib文件链接到我的应用程序,然后告诉编译器加载与延迟相关的DLL,这给了我一个机会,创造Notification Hooks到__pfnDliNotifyHook2和__pfnDliFailureHook2和那种方式我可以使用LoadLibrary从正确的位置加载延迟加载的Dll。
但是,只解决了一半的问题,因为这些Dll中的一些依赖于其他DLL,并且当我使用完整路径加载我想要的DLL时,它无法找到辅助DLL(它们位于与一个我正在加载),这会导致LoadLibrary失败。解决方案是跟踪这些依赖关系并预先加载它们。我将包含一些代码,以解决稍后可能遇到类似情况的任何人的问题。
P. S.我使用Embarcadero公司的C++ Builder的,所以有些像Strings
,TStringList
和Exception
对象可能不正是大家所熟悉的,但这个概念应该在VC++以及工作。
#include <map>
struct TDllDependency
{
TStringList* Dependency;
HMODULE hDll;
__fastcall TDllDependency(void)
{
hDll = NULL;
Dependency = new TStringList();
}
virtual __fastcall ~TDllDependency(void)
{
delete Dependency;
}
};
class TDllModList : public std::map<System::String, TDllDependency>
{
public:
void __fastcall CheckDependency(const System::String& aName);
};
//---------------------------------------------------------------------------
System::String __fastcall GetLtDllPath(void)
{
wchar_t* pfPath = NULL;
System::String dllPath;
SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, KF_FLAG_DEFAULT, NULL, &pfPath);
if (NULL != pfPath)
{
dllPath = IncludeTrailingBackslash(pfPath) + L"LeadTools\\17.5\\";
::CoTaskMemFree(pfPath);
}
return dllPath;
}
System::String mDllPath(GetLtDllPath());
TDllModList DllModList;
void __fastcall InitDllDepends()
{
DllModList.clear();
#if defined(_WIN64)
DllModList[L"ltimgefxx.dll"].Dependency->CommaText = L"ltdisx.dll,ltimgutlx.dll";
DllModList[L"ltefxx.dll"].Dependency->CommaText = L"ltdisx.dll,ltimgutlx.dll";
DllModList[L"ltimgcorx.dll"].Dependency->CommaText = L"ltdisx.dll,ltimgutlx.dll";
DllModList[L"ltdlgimgefxx.dll"].Dependency->CommaText = L"ltdisx.dll,ltdlgkrnx.dll,ltdlgcomx.dll,ltdlgctrlx.dll,ltdlgutlx.dll,ltimgefxx.dll,ltimgsfxx.dll,ltimgcorx.dll,ltimgclrx.dll";
DllModList[L"ltdlgutlx.dll"].Dependency->CommaText = L"ltdisx.dll,ltfilx.dll,ltdlgkrnx.dll,ltimgclrx.dll,ltimgcorx.dll,ltimgefxx.dll,ltimgsfxx.dll";
DllModList[L"ltdlgctrlx.dll"].Dependency->CommaText = L"ltdlgutlx.dll,ltdlgkrnx.dll,ltdisx.dll,ltfilx.dll,ltimgefxx.dll";
DllModList[L"ltdlgcomx.dll"].Dependency->CommaText = L"ltdlgkrnx.dll,ltdlgctrlx.dll,ltdlgutlx.dll";
#elif defined(__WIN32__)
DllModList[L"ltimgefxu.dll"].Dependency->CommaText = L"ltdisu.dll,ltimgutlu.dll";
DllModList[L"ltefxu.dll"].Dependency->CommaText = L"ltdisu.dll,ltimgutlu.dll";
DllModList[L"ltimgcoru.dll"].Dependency->CommaText = L"ltdisu.dll,ltimgutlu.dll";
DllModList[L"ltdlgimgefxu.dll"].Dependency->CommaText = L"ltdisu.dll,ltdlgkrnu.dll,ltdlgcomu.dll,ltdlgctrlu.dll,ltdlgutlu.dll,ltimgefxu.dll,ltimgsfxu.dll,ltimgcoru.dll,ltimgclru.dll";
DllModList[L"ltdlgutlu.dll"].Dependency->CommaText = L"ltdisu.dll,ltfilu.dll,ltdlgkrnu.dll,ltimgclru.dll,ltimgcoru.dll,ltimgefxu.dll,ltimgsfxu.dll";
DllModList[L"ltdlgctrlu.dll"].Dependency->CommaText = L"ltdlgutlu.dll,ltdlgkrnu.dll,ltdisu.dll,ltfilu.dll,ltimgefxu.dll";
DllModList[L"ltdlgcomu.dll"].Dependency->CommaText = L"ltdlgkrnu.dll,ltdlgctrlu.dll,ltdlgutlu.dll";
#endif
};
HMODULE SafeLoadLeadDll(const System::String tName)
{
System::String tPath;
HMODULE retVal = NULL;
DllModList.CheckDependency(tName);
tPath = mDllPath + tName;
if(FileExists(tPath))
retVal = ::LoadLibrary(tPath.c_str());
return retVal;
}
FARPROC WINAPI MyDliNotifyHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
FARPROC retVal = NULL;
System::String tStr(pdli->szDll);
tStr = tStr.LowerCase();
if(dliNotePreLoadLibrary == dliNotify)
{
TDllModList::iterator i = DllModList.find(tStr);
if(DllModList.end() == i)
{
retVal = (FARPROC)SafeLoadLeadDll(tStr);
DllModList[tStr].hDll = (HMODULE)retVal;
}
else if(NULL == i->second.hDll)
{
i->second.hDll = SafeLoadLeadDll(tStr);
retVal = (FARPROC)i->second.hDll;
}
else
retVal = (FARPROC)i->second.hDll;
}
else if(dliFailLoadLib == dliNotify)
{
tStr = L"Compleatly falied to load " + tStr;
::OutputDebugString(tStr.c_str());
}
return retVal;
}
FARPROC WINAPI MyDliFailureHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
FARPROC retVal = NULL;
if(dliNotePreLoadLibrary == dliNotify)
{
System::String tMsg = pdli->szDll;
tMsg = L"Failed to load \"" + tMsg + L"\".\n" + SysErrorMessage(::GetLastError());
throw Exception(tMsg);
}
return retVal;
}
extern "C" PfnDliHook __pfnDliNotifyHook2 = MyDliNotifyHook;
extern "C" PfnDliHook __pfnDliFailureHook2 = MyDliFailureHook;
void __fastcall TDllModList::CheckDependency(const System::String& aName)
{
TDllModList::iterator i = find(aName);
if(end() != i)
{
int len = i->second.Dependency->Count;
int j;
System::String tPath;
for(j = 0; j < len; j++)
{
if(end() == find(i->second.Dependency->Strings[j]))
{
CheckDependency(i->second.Dependency->Strings[j]);
tPath = mDllPath + i->second.Dependency->Strings[j];
(*this)[i->second.Dependency->Strings[j]].hDll = ::LoadLibrary(tPath.c_str());
}
}
}
}
//---------------------------------------------------------------------------
当然InitDllDepends();
,并应在WinMain
开始被要求正确设置好了。
*如果我静态链接Leadtools Dll * - 您无法静态链接任何DLL的DLL。你可能的意思是你在你的项目中使用了导入库。 – PaulMcKenzie
您应该首先将DLL保存在您未动态加载它们的同一地点,并尝试动态加载它们。如果这不起作用,那么问题出在您的代码上,而不是在系统周围移动并使用“AddDllDirectory”。 – PaulMcKenzie
@PaulMcKenzie我知道。我已经删除了静态链接,并且只使用动态加载。我试图说的是,静态链接时,具有相同参数的功能相同,但当切换到动态加载时会失败。另外我还将DLL添加到开发机器的System32文件夹中。我无法在生产中做到这一点。 – Sam