Search code examples
c++windowsdllleadtools-sdk

Dynamic loading Leadtools DLLs


I am using Leadtools 17.5. If I statically link the Leadtools Dlls into my 64 bit C++ Application and then call L_SetLicenseBuffer everything works fine and the return value is zero. But for security reasons, the final product is not allowed to add those DLLs into the System32 folder and is also not allowed to change the system path, and since multiple applications are using the tools I want to install them in a common folder (C:\Program Files\Common Files\LeadTools\17.5 for example) and use AddDllDirectory to add the path to the DLL search path. So I decided to load the DLLs dynamically at the run-time. So I created a definition for the function like this:

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);

then created a function pointer like this:

TL_SetLicenseBuffer pfSetLicenseBuffer = NULL;
TL_IsSupportLocked pfIsSupportLocked = NULL;

then add the paths to where the DLLs are to the DLL search path:

AddDllDirectory(LEAD_DLL_PATH);
AddDllDirectory(LEAD_FILTER_PATH);

and set the default directory search path for DLLs to be the user defined:

SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_USER_DIRS);

then load the DLL and get the address of the functions I need:

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

now if I use the function pointer with the same parameters as before, the function fails and returns -13 and any subsequent call to for example to pfIsSupportLocked shows the nag dialog:

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

Does anyone know how I can fix this?

Thank you
Sam


Solution

  • I was not able to make the dynamic loading to work at all, but I was able to use 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 to load the delayed loaded Dlls from correct location.
    But that only fixed half the problem because some of these Dlls are dependent on other DLLs and when I used the full path to load the DLL that I wanted, it could not find the secondary DLLs (which were located in the same directory as the one I was loading) and that would cause LoadLibrary to fail. The solution was to keep track of those dependencies and pre-load them. I am including some of the code to fix the issue for anyone whom might run into similar situation later on.
    P. S. I am using Embarcadero's C++ Builder, so Some of the objects like the Strings, TStringList and Exception may not be exactly what everyone is familiar with, but the concept should work in VC++ as well.

    #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());
                }
            }
        }
    }
    //---------------------------------------------------------------------------
    

    And of course InitDllDepends(); should be called at the beginning of WinMain to set things up correctly.