您在Luchian Grigore的回答评论中描述了问题的起源:“我希望DLL从exe文件中导入一些符号”。您在问题的文本中还写道,您想要“DLL在EXE文件中查找这些符号。 我们期望DLL能够在EXE文件中找到这些符号。”
大多数情况下,是否从exe导出函数或数据都是一个设计问题。通常只从DLL中创建导出。如果EXE需要向DLL提供一些信息,则通过“参数”提供信息。例如,在EXE中调用某个由DLL实现和导出的函数MyFunc时,您将获得context指针作为MyFunc的附加参数,该指针可以直接或间接地使DLL获取所需的所有有关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
我可以解释如何实现方案,而不需要长时间讨论何时以及是否应该这样做。
首先,LIB文件包含OBJ文件,其格式与
程序可执行文件(PE)不同。在编译期间,不同的公共部分将被放置在OBJ文件中。非常重要的是,程序可执行文件(EXE或DLL)不仅包含代码,还有许多附加信息在PE的头部分。其中最重要的是:
- 导出目录
- 导入目录
- 导入地址表目录
- 基址重定位目录
- 资源目录
你可以使用
DumpBin.exe
实用程序(只需启动“Visual Studio命令提示符(2010)”即可轻松使用它)。要查看有关标头的信息,可以使用
DumpBin.exe /headers my.exe
。要查看导出目录的内容,可以使用
DumpBin.exe /exports my.exe
等等。
如果你编译一个导出一些函数或数据的DLL,那么将会额外创建LIB文件,这被称为“导入库”。如果你在使用DLL中的某些函数或数据的EXE项目中使用LIB,链接器将解析外部引用,并在EXE的导入目录中放置关于应该在加载时解析的函数的信息。因此,导入库仅包含用于填充EXE中的Import Directory和Import Address Table Directory的模板。通常情况下,可以以同样的方式从EXE导出一些数据或函数,创建LIB,在DLL项目中使用LIB,并以此实现从EXE向DLL导入一些信息。我制作了一个演示过程的
演示项目。请仔细阅读我的答案末尾的编译说明,如果你想从项目中删除所有LIB并自己创建所有内容。ExportFromExe.c(EXE)的代码如下:
#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
}
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);
}
为了能够成功创建项目,我们必须解决“先有鸡还是先有蛋”的问题,因为EXE项目依赖于MyDll.lib,而DLL项目依赖于ExportFromExe.lib。为了第一次编译EXE,我们可以暂时从EXE项目的链接器设置中删除$(OutDir)MyDll.lib,并定义CREATE_IMPORT_LIBRARY_ONLY。结果将创建ExportFromExe.exe和ExportFromExe.lib。在更大的项目中,可以使用链接器的Undefined Symbol Only (/FORCE:UNRESOLVED)选项。然后我们可以构建MyDll项目,它将创建MyDll.dll和MyDll.lib。现在您可以从EXE中删除CREATE_IMPORT_LIBRARY_ONLY,并将$(OutDir)MyDll.lib包括在链接器设置中(“Input”部分中的“Additional Dependencies”)。下一次构建EXE项目将产生最终解决方案。
我使用了一些小技巧来移除C-Runtime并将EXE和DLL的大小减小到2.5或3 KB。因此,您可以使用
DumpBin.exe
的
/all
开关来检查包括原始二进制数据在内的完整信息。
由于EXE以ERRORLEVEL形式返回结果,您可以在命令提示符中测试应用程序:
echo %ERRORLEVEL%
0
ExportFromExe.exe
echo %ERRORLEVEL%
18