Visual C ++:使用相对路径从另一个DLL链接DLL

时间:2015-09-15 16:13:40

标签: c++ windows

我有以下文件结构

C:\Application\application.exe
C:\Application\plugins\myplugin\myplugin.dll
C:\Application\plugins\myplugin\libs\utils.dll

此处application.exe通过myplugin.dll动态加载LoadLibrary。请注意,我无法控制application.exe,因为我只开发插件。

我想要的是通过 相对 路径(理想情况下使用静态链接)myplugin.dll加载libs\utils.dll。也就是说,我不想依赖application.exe的位置。我在安装C:\Application\plugins\myplugin\libs时最初将PATH添加到myplugin环境变量,但环境变量不是理想的解决方案,我希望避免这样做。

我希望我可以使用程序集和配置文件来指定libs\utils.dll中的相对路径myplugin.dll。我试过这个,但无济于事。然后我看到有人在StackOverflow上提到配置文件仅适用于应用程序(即可执行文件)。但正如我上面所说,我无法控制application.exe。有没有一个解决这个看似简单的问题的解决方案,我认为Unix系统可以通过rpath解决?

1 个答案:

答案 0 :(得分:3)

您根本无法静态链接到DLL路径,相对或绝对。 PE导入表仅包含文件名。这就是DLL search path存在以找到DLL的原因。

如果要控制加载utils.dll的位置,则必须动态加载。 myplugin.dll可以使用传递到其GetModuleFileName()入口点的模块句柄,使用DllMain()检索自己的路径。然后它可以从路径中删除文件名,附加路径的相对路径,然后在需要时加载DLL(不在DllMain()本身内部,否则可能发生死锁/崩溃)。

有两种方法可以解决这个问题:

  1. 自己动态加载所有内容:

    #include <windows.h> 
    #include <shlwapi.h> 
    
    #pragma comment(lib, "shlwapi.lib")
    
    HINSTANCE hThisDLL = NULL;
    HMODULE hUtils = NULL;
    
    typedef ReturnType __CallingConv (*DllFuncType)(Params);
    DllFuncType DllFunc = NULL;
    
    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
    {
        if (fdwReason == DLL_PROCESS_ATTACH)
        {
            hThisDLL = hinstDLL;
            ...
        }
    
        return TRUE;
    }
    
    ...
    
    ReturnType CallDllFunc(Params)
    {
        if (!hUtils)
        {
            TCHAR szUtilsFileName[MAX_PATH] = {0};
            GetModuleFileName(hThisDLL, szUtilsFileName, MAX_PATH);
    
            if (!PathRemoveFileSpec(szUtilsFileName))
            {
                // do something...
                return ...;
            }
    
            if (!PathAppend(szUtilsFileName, TEXT("libs\\utils.dll")))
            {
                // do something...
                return ...;
            }
    
            hUtils = LoadLibrary(szUtilsFileName);
            if (!hUtils)
            {
                // do something...
                return ...;
            }
        }
    
        if (!DllFunc)
        {
            DllFunc = (DllFuncType) GetProcAddress(hUtils, "DllFuncName");
            if (!DllFunc)
            {
                // do something...
                return ...;
            }
        }
    
        return DllFunc(Params);
    }
    
  2. 静态链接到你通常会做的所有事情,但是然后利用编译器的delay load功能(如果支持),这样你就可以在运行时动态指定DLL的文件名,但仍然静态链接到DLL函数本身(延迟加载机制会为你调用GetProcAddress()

    #include <windows.h> 
    #include <shlwapi.h> 
    #include <delayimp.h>
    
    #pragma comment(lib, "Delayimp.lib")
    #pragma comment(lib, "shlwapi.lib")
    
    HINSTANCE hThisDLL = NULL;
    
    FARPROC WINAPI DelayLoadHook(unsigned dliNotify, PDelayLoadInfo pdli)
    {
        if ((dliNotify == dliNotePreLoadLibrary) &&
            (strcmpi(pdli->szDll, "utils.dll") == 0))
        {
            TCHAR szUtilsFileName[MAX_PATH] = {0};
            GetModuleFileName(hThisDLL, szUtilsFileName, MAX_PATH);
    
            if (!PathRemoveFileSpec(szUtilsFileName))
            {
                // do something...
                return NULL;
            }
    
            if (!PathAppend(szUtilsFileName, TEXT("libs\\utils.dll")))
            {
                // do something...
                return NULL;
            }
    
            HMODULE hUtils = LoadLibrary(szUtilsFileName);
            return reinterpret_cast<FARPROC>(hUtils);
        }
    
        return NULL;
    }
    
    PfnDliHook __pfnDliNotifyHook2 = DelayLoadHook;
    
    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
    {
        if (fdwReason == DLL_PROCESS_ATTACH)
        {
            hThisDLL = hinstDLL;
            ...
        }
    
        return TRUE;
    }
    
    ...
    
    ReturnType CallDllFunc(Params)
    {
        return DllFuncName(Params);
    }