你做错了什么(或者至少不像你在问题中描述的那样)。当然,您在答案中发布的内容也有效,但这只是一种解决方法,因为“常规”方式应该有效。
这是一个小例子。
库00.cpp:
extern "C" __declspec(dllexport) bool foo()
{
return true;
}
dll00.cpp:
extern "C" __declspec(dllexport) bool bar()
{
return false;
}
Output:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q056330888]> sopr.bat
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
[prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2017\VC\Auxiliary\Build\vcvarsall.bat" x64 > nul
[prompt]>
[prompt]> dir /b
dll00.cpp
lib00.cpp
[prompt]>
[prompt]> cl /c /nologo /D_LIB /DSTATIC /Folib00.obj lib00.cpp
lib00.cpp
[prompt]>
[prompt]> lib /nologo /out:lib00.lib lib00.obj
[prompt]>
[prompt]> cl /c /nologo /DDLL /Fodll00.obj dll00.cpp
dll00.cpp
[prompt]>
[prompt]> link /nologo /dll /out:dll00.dll dll00.obj lib00.lib
Creating library dll00.lib and object dll00.exp
[prompt]>
[prompt]> dir /b
dll00.cpp
dll00.dll
dll00.exp
dll00.lib
dll00.obj
lib00.cpp
lib00.lib
lib00.obj
[prompt]>
[prompt]> dumpbin /nologo /exports dll00.dll
Dump of file dll00.dll
File Type: DLL
Section contains the following exports for dll00.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint RVA name
1 0 00001000 bar
Summary
2000 .data
1000 .gehcont
1000 .gxfg
1000 .pdata
9000 .rdata
1000 .reloc
E000 .text
[prompt]>
[prompt]> :: ----- Re-link dll, instructing it to include foo -----
[prompt]>
[prompt]> link /nologo /dll /include:foo /out:dll00.dll dll00.obj lib00.lib
Creating library dll00.lib and object dll00.exp
[prompt]>
[prompt]> dumpbin /nologo /exports dll00.dll
Dump of file dll00.dll
File Type: DLL
Section contains the following exports for dll00.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
2 number of functions
2 number of names
ordinal hint RVA name
1 0 00001000 bar
2 1 00001010 foo
Summary
2000 .data
1000 .gehcont
1000 .gxfg
1000 .pdata
9000 .rdata
1000 .reloc
E000 .text
Notes:
-
如前所述,我使用了命令行,但相同的命令(更多参数)由VStudio IDE
-
Adding /include:foo (2nd Link command) exports foo as well (as seen in the next DumpBin output):
-
指定此选项与添加相同#pragma comment(linker, "/include:foo")
(in dll.cpp- 或直接传递给链接器的任何文件)
-
/导出:foo不是必需的,因为该函数已经由__declspec(dll导出)
-
我没有完成最后(申请),因为foo存在于DumpBin输出就足够了(也可以从依赖步行者)
Update #0
毕竟你可能没有做错事。但请记住它不可扩展(如果您有数百个此类符号)。看着[MS.Learn]:LIB概述,它提供与Link关于出口东西。但他们似乎被忽视了。
当建造一个.lib,也许有人想指定链接时要包含的所有符号(通过选项或通过#pragma 评论), when 建设.lib,而不是在链接时。显然,它们被忽略(我已经测试过),除非在.obj文件(或选项)直接传递给链接器。这是因为[MS.Learn]:构建导入库和导出文件 (emphasis是我的):
请注意,如果您在创建 .dll 之前的预备步骤中创建导入库,构建 .dll 时必须传递与构建导入库时传递的同一组目标文件.
所以通过时有一个区别.obj文件到链接器:
这完全有道理,因为库只是一个集合(存档).obj文件(在Nix归档器是Ar(原名RanLib))。一个例子:
Output:
[prompt]> del *.obj *.exp *.lib *.dll
[prompt]>
[prompt]> dir /b
dll00.cpp
lib00.cpp
[prompt]>
[prompt]> cl /c /nologo /D_LIB /DSTATIC /Folib00.obj lib00.cpp
lib00.cpp
[prompt]>
[prompt]> cl /c /nologo /DDLL /Fodll00.obj dll00.cpp
dll00.cpp
[prompt]>
[prompt]> :: Pass lib00.obj directly to linker
[prompt]>
[prompt]> link /nologo /dll /out:dll00.dll dll00.obj lib00.obj
Creating library dll00.lib and object dll00.exp
[prompt]>
[prompt]> lib /nologo /out:lib00.lib lib00.obj
[prompt]>
[prompt]> dir
Volume in drive E is SSD0-WORK
Volume Serial Number is AE9E-72AC
Directory of e:\Work\Dev\StackOverflow\q056330888
23/01/30 09:15 <DIR> .
23/01/30 09:15 <DIR> ..
23/01/30 09:12 72 dll00.cpp
23/01/30 09:14 106,496 dll00.dll
23/01/30 09:14 733 dll00.exp
23/01/30 09:14 1,790 dll00.lib
23/01/30 09:14 604 dll00.obj
23/01/30 09:07 71 lib00.cpp
23/01/30 09:15 822 lib00.lib
23/01/30 09:13 604 lib00.obj
8 File(s) 111,192 bytes
2 Dir(s) 51,727,843,328 bytes free
[prompt]>
[prompt]> dumpbin /nologo /exports dll00.dll
Dump of file dll00.dll
File Type: DLL
Section contains the following exports for dll00.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
2 number of functions
2 number of names
ordinal hint RVA name
1 0 00001000 bar
2 1 00001010 foo
Summary
2000 .data
1000 .gehcont
1000 .gxfg
1000 .pdata
9000 .rdata
1000 .reloc
E000 .text
[prompt]>
[prompt]> :: Now do the same with the one from inside the .lib
[prompt]>
[prompt]> del lib00.obj
[prompt]>
[prompt]> lib lib00.lib /extract:lib00.obj
Microsoft (R) Library Manager Version 14.16.27048.0
Copyright (C) Microsoft Corporation. All rights reserved.
[prompt]>
[prompt]> dir lib00.obj
Volume in drive E is SSD0-WORK
Volume Serial Number is AE9E-72AC
Directory of e:\Work\Dev\StackOverflow\q056330888
23/01/30 09:16 604 lib00.obj
1 File(s) 604 bytes
0 Dir(s) 51,727,839,232 bytes free
[prompt]>
[prompt]> link /nologo /dll /out:dll00.dll dll00.obj lib00.obj
Creating library dll00.lib and object dll00.exp
[prompt]>
[prompt]> dumpbin /nologo /exports dll00.dll
Dump of file dll00.dll
File Type: DLL
Section contains the following exports for dll00.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
2 number of functions
2 number of names
ordinal hint RVA name
1 0 00001000 bar
2 1 00001010 foo
Summary
2000 .data
1000 .gehcont
1000 .gxfg
1000 .pdata
9000 .rdata
1000 .reloc
E000 .text
Update #1
我短暂地玩过[MS.Learn]:链接器选项 (/INCLUDE and /EXPORT)。混合中增加了一点复杂性。
库01.cpp:
//#pragma comment(linker, "/include:foo1") // Apparently, has no effect in an .obj contained by a .lib
#pragma comment(linker, "/export:foo01")
#if defined(__cplusplus)
extern "C" {
#endif
__declspec(dllexport) bool foo00()
{
return true;
}
bool foo01()
{
return true;
}
bool foo02()
{
return true;
}
#if defined(__cplusplus)
}
#endif
库10.cpp:
#pragma comment(linker, "/export:foo11")
#if defined(__cplusplus)
extern "C" {
#endif
__declspec(dllexport) bool foo10()
{
return true;
}
bool foo11()
{
return true;
}
bool foo12()
{
return true;
}
#if defined(__cplusplus)
}
#endif
Output:
[prompt]> del *.obj *.exp *.lib *.dll
[prompt]>
[prompt]> cl /c /nologo /D_LIB /DSTATIC /Folib01.obj lib01.cpp
lib01.cpp
[prompt]>
[prompt]> cl /c /nologo /D_LIB /DSTATIC /Folib10.obj lib10.cpp
lib10.cpp
[prompt]>
[prompt]> lib /nologo /out:lib0110.lib lib01.obj lib10.obj
[prompt]>
[prompt]> cl /c /nologo /DDLL /Fodll00.obj dll00.cpp
dll00.cpp
[prompt]>
[prompt]> :: ----- "Regular" behavior -----
[prompt]>
[prompt]> link /nologo /dll /out:dll00.dll dll00.obj lib0110.lib
Creating library dll00.lib and object dll00.exp
[prompt]>
[prompt]> dumpbin /nologo /exports dll00.dll
Dump of file dll00.dll
File Type: DLL
Section contains the following exports for dll00.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint RVA name
1 0 00001000 bar
Summary
2000 .data
1000 .gehcont
1000 .gxfg
1000 .pdata
9000 .rdata
1000 .reloc
E000 .text
[prompt]>
[prompt]> :: ----- /export a symbol -----
[prompt]>
[prompt]> link /nologo /dll /out:dll00_export.dll /export:foo02 dll00.obj lib0110.lib
Creating library dll00_export.lib and object dll00_export.exp
[prompt]>
[prompt]> dumpbin /nologo /exports dll00_export.dll
Dump of file dll00_export.dll
File Type: DLL
Section contains the following exports for dll00_export.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
2 number of functions
2 number of names
ordinal hint RVA name
1 0 00001000 bar
2 1 0000E1A0 foo02
Summary
2000 .data
1000 .gehcont
1000 .gxfg
1000 .pdata
9000 .rdata
1000 .reloc
E000 .text
[prompt]>
[prompt]> :: ----- /include a symbol -----
[prompt]>
[prompt]> link /nologo /dll /out:dll00_include.dll /include:foo02 dll00.obj lib0110.lib
Creating library dll00_include.lib and object dll00_include.exp
[prompt]>
[prompt]> dumpbin /nologo /exports dll00_include.dll
Dump of file dll00_include.dll
File Type: DLL
Section contains the following exports for dll00_include.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
3 number of functions
3 number of names
ordinal hint RVA name
1 0 00001000 bar
2 1 00001010 foo00
3 2 00001020 foo01
Summary
2000 .data
1000 .gehcont
1000 .gxfg
1000 .pdata
9000 .rdata
1000 .reloc
E000 .text
正如所见(就像在Docs):
-
/EXPORT:搜索(在.lib)对于符号(foo02)并简单地导出它
-
/INCLUDE:搜索(在.lib)对于符号(foo02),获取包含的目标文件(lib0.obj), and 将其包含在.dll:
- 因此,另外 2 个符号 (foo00, foo01)标记为出口.obj文件已导出
结论
深入观察发现[MS.Learn]:/WHOLEARCHIVE(包括所有库对象文件)其中指出(emphasis是我的):
/WHOLEARCHIVE 选项强制链接器包含每个目标文件来自指定的静态库,或者如果未指定库,来自所有静态库指定给 LINK 命令。
...
/WHOLEARCHIVE 选项是在 Visual Studio 2015 Update 2 中引入的。
Output:
[prompt]> :: ----- YAY ----- /wholearchive ----- YAY -----
[prompt]>
[prompt]> link /nologo /dll /out:dll00_wholearchive.dll /wholearchive:lib0110.lib dll00.obj lib0110.lib
Creating library dll00_wholearchive.lib and object dll00_wholearchive.exp
[prompt]>
[prompt]> dumpbin /nologo /exports dll00_wholearchive.dll
Dump of file dll00_wholearchive.dll
File Type: DLL
Section contains the following exports for dll00_wholearchive.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
5 number of functions
5 number of names
ordinal hint RVA name
1 0 00001000 bar
2 1 00001040 foo00
3 2 00001050 foo01
4 3 00001010 foo10
5 4 00001020 foo11
Summary
2000 .data
1000 .gehcont
1000 .gxfg
1000 .pdata
9000 .rdata
1000 .reloc
E000 .text