动态加载 Leadtools DLL

Dynamic loading Leadtools DLLs

我正在使用 Leadtools 17.5。如果我将 Leadtools Dll 静态 link 到我的 64 位 C++ 应用程序中,然后调用 L_SetLicenseBuffer 一切正常并且 return 值为零。但是出于安全原因,最终产品不允许将这些DLL添加到System32文件夹中,也不允许更改系统路径,并且由于多个应用程序都在使用我想将它们安装在一个通用的工具中文件夹(例如 C:\Program Files\Common Files\LeadTools.5)并使用 AddDllDirectory 添加DLL 搜索路径的路径。所以我决定在 运行 时间动态加载 DLL。所以我为这个函数创建了一个定义:

typedef L_INT (EXT_FUNCTION* TL_SetLicenseBuffer)(L_UCHAR* pLicenseBuffer, L_SSIZE_T nSize, L_TCHAR* pszDeveloperKey);
typedef L_BOOL (EXT_FUNCTION* TL_IsSupportLocked)(L_UINT uType);

然后像这样创建了一个函数指针:

TL_SetLicenseBuffer pfSetLicenseBuffer = NULL;
TL_IsSupportLocked pfIsSupportLocked = NULL;

然后将 DLL 所在的路径添加到 DLL 搜索路径:

AddDllDirectory(LEAD_DLL_PATH);
AddDllDirectory(LEAD_FILTER_PATH);

并将 DLL 的默认目录搜索路径设置为用户定义的:

SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_USER_DIRS);

然后加载DLL,得到我需要的函数地址:

HINSTANCE hKrn = LoadLibrary(L"ltkrnx.dll");
pfSetLicenseBuffer = (TL_SetLicenseBuffer)GetProcAddress(hKrn, "L_SetLicenseBuffer");
pfIsSupportLocked  = (TL_IsSupportLocked)GetProcAddress(hKrn, "L_IsSupportLocked");

现在,如果我使用与以前相同的参数的函数指针,该函数将失败并且 returns -13 以及对 pfIsSupportLocked 的任何后续调用都会显示 nag 对话框:

retCode = pfSetLicenseBuffer(pLicenseData, LicSize, pKeyStr); // retCode is -13
pfIsSupportLocked(L_SUPPORT_DOCUMENT); // Shows nag dialog

有谁知道我该如何解决这个问题?

谢谢
山姆

您需要做的第一件事是检查调试器输出,并通过验证路径确保您期望加载的 DLL 是加载的 DLL。您的搜索路径中可能有多个版本的 LTKRNX.DLL。我在这里测试了你的代码,它返回了成功:

typedef L_INT (EXT_FUNCTION* TL_SetLicenseBuffer)(L_UCHAR* pLicenseBuffer, L_SSIZE_T nSize, L_TCHAR* pszDeveloperKey);
typedef L_BOOL (EXT_FUNCTION* TL_IsSupportLocked)(L_UINT uType);

HINSTANCE hKrn = LoadLibrary(L"ltkrnx.dll");
TL_SetLicenseBuffer pfSetLicenseBuffer = NULL;
TL_IsSupportLocked pfIsSupportLocked = NULL;

pfSetLicenseBuffer = (TL_SetLicenseBuffer)GetProcAddress(hKrn, "L_SetLicenseBuffer");
pfIsSupportLocked  = (TL_IsSupportLocked)GetProcAddress(hKrn, "L_IsSupportLocked");

L_INT retCode = pfSetLicenseBuffer(szLICAnsi, _countof(szLICAnsi), pKeyStr);
if(retCode == SUCCESS)
  bRet = pfIsSupportLocked(L_SUPPORT_DOCUMENT);
else
  printf("Problem!");

此外,PaulMcKenzie 建议的是另一种验证您对 LoadLibrary 的调用是否正常工作的方法。如果您仍然无法解决问题,您可以通过 support@leadtools.com

联系我们的技术支持以帮助您解决此问题

我根本无法使动态加载工作,但我能够使用 Delay loading to work.
What I had to do was to go back to linking the extracted .Lib files to my application and then tell compiler to load the associated DLLs with delay, which gave me a chance to create Notification Hooks to __pfnDliNotifyHook2 and __pfnDliFailureHook2 and that way I could use LoadLibrary 从正确的位置加载延迟加载的 Dll。
但只修复了一半问题是因为其中一些 DLL 依赖于其他 DLL,当我使用完整路径加载我想要的 DLL 时,它找不到辅助 DLL(它们与我正在加载的 DLL 位于同一目录中)并且这会导致 LoadLibrary 失败。解决方案是跟踪这些依赖项并预加载它们。我包含了一些代码来解决以后可能 运行 遇到类似情况的任何人的问题。
P。 S. 我正在使用 Embarcadero 的 C++ Builder,所以有些对象像 StringsTStringListException 可能不是每个人都熟悉的,但这个概念应该也适用于 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 的开头调用以正确设置。