创建DLL时未解析的外部符号

qdi*_*dii 4 c++ dll linker visual-studio

我的一个朋友在创建DLL时会遇到一堆错误.Visual Studio抱怨未解析的外部符号.我主要是一个Unix用户,所以我可能会在那里弄错.在Unix上,当您创建静态库(存档)时,它不会将不同的目标文件连接到存档文件中.我希望动态对象以相同的方式创建,但显然会发生额外的链接阶段.

第一个问题:为什么dll存在链接阶段?

在这种情况下,DLL indeeds包含未定义的符号,因为我们希望DLL在EXE文件中找到这些符号.这与典型的DLL行为完全相反,其中EXE使用DLL中定义的符号.为了清楚起见,我希望在DLL加载到内存中时能够找到这些符号.

第二个问题:如何使DLL使用EXE文件中定义的符号?

编辑: 我重新提出了问题,因为我认为我没有明确说明问题.

Ole*_*leg 11

您将问题的根源描述为:"我希望DLL从exe文件中导入一些符号"在评论中输入Luchian Grigore的答案.您在问题的文本中另外写了"DLL要在EXE文件中找到这些符号.我们希望DLL在EXE文件中找到这些符号."

大多数是设计问题是否从exe导出函数或数据.通常只能从DLL创建导出.如果EXE需要向DLL提供一些信息,它会通过参数提供信息.例如,您在EXE中调用了一些MyFunc在DLL中实现和导出的函数.作为附加参数MyFunc你得到的context指针可以直接或间接获得所需的EXE所有信息.

在某些情况下,您可以从EXE导出数据或函数.例如,您使用DumpBin.exe实用程序(只需启动"Visual Studio命令提示符(2010)"来使用它)来验证Outlook.exe导出

DumpBin.exe /exports "C:\Program Files\Microsoft Office\Office14\OUTLOOK.EXE"

File Type: EXECUTABLE IMAGE

  Section contains the following exports for outlook.exe

    00000000 characteristics
    4E79B6C8 time date stamp Wed Sep 21 12:04:56 2011
        0.00 version
           1 ordinal base
          66 number of functions
          66 number of names

    ordinal hint RVA      name

          1    0 00B58A88 CleanupAddressComponents
          2    1 00B58A88 CleanupNameComponents
          3    2 00228DC4 DllCanUnloadNow
          4    3 004848F8 DllGetClassObject
          ...
         65   40 0038EF30 UpdateContactTracker
         66   41 00902788 dwIsLoggingEnabled
Run Code Online (Sandbox Code Playgroud)

我可以解释一下如何在没有长时间讨论的情况下实现场景,何时以及是否真的应该这样做.

首先,LIB文件包含OBJ文件,其另一种格式为Program Executable(PE).在编译期间,不同的公共部分将放在OBJ文件中.非常重要的是,程序可执行文件(EXE或DLL)不仅包含代码,还包含PE头部的许多附加信息.最重要的是

  • 出口目录
  • 导入目录
  • 导入地址表目录
  • 基地重定位目录
  • 资源目录

您可以使用DumpBin.exe实用程序(只需启动"Visual Studio命令提示符(2010)"即可轻松使用它).要查看有关可以使用的标头的信息DumpBin.exe /headers my.exe.要查看包含您可以使用的导出目录DumpBin.exe /exports my.exe,等等.

如果编译导出某些函数或数据的DLL,则将另外创建LIB文件.它是如此命名的导入库.如果在EXE项目中使用LIB,它使用DLL中的某些函数或数据,则链接器将解析外部引用,并在EXE的导入目录中放置有关应在加载时解析的函数的信息.

因此,导入库仅包含用于在EXE中填充"导入目录"和"导入地址表目录"的模板.

通常,可以以相同的方式从EXE导出一些函数数据,创建LIB,在DLL项目中使用LIB,以及实现从EXE导入DLL中的一些信息的方式.

我制作了演示项目,演示了这种方式.如果您想从项目中删除所有LIB并自行创建,请仔细阅读我的答案末尾的编译说明.ExportFromExe.c(EXE)的代码:

//#define CREATE_IMPORT_LIBRARY_ONLY
#include <Windows.h>

EXTERN_C __declspec(dllexport) int someData = 0;
EXTERN_C __declspec(dllexport) int __stdcall myFunc (int x);
EXTERN_C __declspec(dllexport) int __stdcall MyFunc();

int __stdcall myFunc (int x)
{
    return x + 10;
}

#ifndef _DEBUG
int mainCRTStartup()
#else
int main()
#endif
{
    someData = 5;
#ifndef CREATE_IMPORT_LIBRARY_ONLY
    return MyFunc();
#endif
}
Run Code Online (Sandbox Code Playgroud)

MyDll.c(DLL)的代码:

#include <Windows.h>

EXTERN_C __declspec(dllexport) int myData = 3;
EXTERN_C __declspec(dllimport) int someData;
EXTERN_C __declspec(dllimport) int __stdcall myFunc (int x);

#ifndef _DEBUG
EXTERN_C BOOL WINAPI _DllMainCRTStartup (HINSTANCE hinstDLL, DWORD fdwReason,
                                         LPVOID lpvReserved)
#else
BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
#endif
{
    if (fdwReason == DLL_PROCESS_ATTACH)
        DisableThreadLibraryCalls(hinstDLL);

    return TRUE;
    UNREFERENCED_PARAMETER (lpvReserved);
}

EXTERN_C __declspec(dllexport) int WINAPI MyFunc()
{
    return someData + myFunc(myData);
}
Run Code Online (Sandbox Code Playgroud)

为了能够在第一时间成功创建项目,我们必须解决问题:"谁是第一个:鸡还是鸡蛋?" 因为EXE项目依赖于MyDll.lib而DLL项目依赖于ExportFromExe.lib.对于EXE的第一次编译,我们可以$(OutDir)MyDll.lib从EXE项目的链接器设置中删除temoprary 并定义CREATE_IMPORT_LIBRARY_ONLY.结果我们将创造ExportFromExe.exeExportFromExe.lib.在更大的项目中,可以使用Undefined Symbol Only (/FORCE:UNRESOLVED)链接器选项.然后我们可以建立MyDll创建MyDll.dll和的项目MyDll.lib.现在您可以CREATE_IMPORT_LIBRARY_ONLY从EXE中删除并包含$(OutDir)MyDll.lib为链接器设置("输入"部分设置中的"附加深度").EXE项目的下一个版本将产生最终解决方案.

我使用了一些小技巧来删除C-Runtime并将EXE和DLL的大小减小到2,5或3 KB.因此,您可以使用/allswitch DumpBin.exe来检查EXE和DLL包含RAW二进制数据的完整信息.

因为EXE返回结果为ERRORLEVEL,所以您可以在推荐提示中测试应用程序:

echo %ERRORLEVEL%
0

ExportFromExe.exe

echo %ERRORLEVEL%
18
Run Code Online (Sandbox Code Playgroud)