可以从 DLL 导出函数 foo,前提是:-
foo.obj 如何链接到 DLL 并不重要。
也许您在 DLL 的链接中明确指定 foo.obj。
也许你已经把foo.obj 放在一个静态库中,比如foobar.lib,并且链接
DLL 包含对函数foo 的一些引用。然后链接器将
从foobar.lib 中提取foo.obj 并将其链接到DLL 中,以解决
那个参考。
如果 foo.obj 通过从静态库中提取来链接,则链接
与foo.obj 按名称链接完全相同。静态库很简单
一袋目标文件,链接器可以从中选择它需要携带的文件
在联动上。链接完成后,生成的程序或DLL没有依赖关系
在静态库上。如果它需要包中的任何目标文件,它现在已经得到它们。它不需要袋子。
这是一个使用适用于 Windows 的 GCC mingw-w64 工具链的插图,说明如何
DLL 导出的函数是从静态库中链接的:
C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev0>echo off
Microsoft Windows [Version 10.0.15063]
(c) 2017 Microsoft Corporation. All rights reserved.
C:\>cd develop\so\scrap
C:\develop\so\scrap>dir
Volume in drive C has no label.
Volume Serial Number is 16C7-F955
Directory of C:\develop\so\scrap
16/12/2017 17:50 <DIR> .
16/12/2017 17:50 <DIR> ..
16/12/2017 12:41 116 greeting.cpp
16/12/2017 12:42 99 greeting.h
16/12/2017 12:10 109 hello.cpp
16/12/2017 12:12 90 hello.h
16/12/2017 16:21 197 main.cpp
16/12/2017 12:25 117 niceday.cpp
16/12/2017 12:26 96 niceday.h
7 File(s) 824 bytes
2 Dir(s) 101,878,943,744 bytes free
hello.cpp
#include "hello.h"
#include <iostream>
void hello()
{
std::cout << "Hello world!" << std::endl;
}
hello.h
#ifndef HELLO_H
#define HELLO_H
extern __declspec(dllexport) void hello();
#endif
我们将编译hello.cpp:
C:\develop\so\scrap>g++ -Wall -c -o hello.obj hello.cpp
同样:
niceday.cpp
#include "niceday.h"
#include <iostream>
void niceday()
{
std::cout << "What a nice day!" << std::endl;
}
niceday.h
#ifndef NICEDAY_H
#define NICEDAY_H
extern __declspec(dllexport) void niceday();
#endif
我们将编译niceday.cpp
C:\develop\so\scrap>g++ -Wall -c -o niceday.obj niceday.cpp
现在我们将这两个目标文件放在一个静态库中。 libgreet.lib
ar rcs libgreet.lib hello.obj niceday.obj
另一个源文件和头文件:
greeting.cpp
#include "greeting.h"
#include "hello.h"
#include "niceday.h"
void greeting()
{
hello();
niceday();
}
greeting.h
#ifndef GREETING_H
#define GREETING_H
extern __declspec(dllexport) void greeting();
#endif
我们还将编译:
g++ -Wall -c -o greeting.obj greeting.cpp
现在我们将创建一个 DLL,libgreeting.dll,使用 greeting.obj 和静态
图书馆libgreet.lib:
C:\develop\so\scrap>g++ -shared -o libgreeting.dll greeting.obj libgreet.lib
这里有一个程序源文件:
main.cpp
#include "hello.h"
#include "niceday.h"
#include "greeting.h"
#include <iostream>
int main()
{
hello();
niceday();
std::cout << "I said..." << std::endl;
greeting();
return 0;
}
我们也会编译:
C:\develop\so\scrap>g++ -Wall -c -o main.obj main.cpp
最后,我们将通过链接main.obj 和libgreeting.dll 来制作一个程序。
仅与libgreeting.dll。
C:\develop\so\scrap>g++ -o prog main.obj libgreeting.dll
运行程序:
C:\develop\so\scrap>prog
Hello world!
What a nice day!
I said...
Hello world!
What a nice day!
所有三个 DLL 导出函数,hello、niceday 和 greeting,
被称为。 所有重要的,要发生这种情况,就是他们所有人都是
声明 __declspec(dllexport) 并且所有这些都链接到 libgreeting.dll
不知何故。碰巧的是,其中两个(hello、niceday)是从静态库链接的,另一个(greeting)是直接从目标文件链接的:这没关系。
如果您有兴趣...
以下是使用 Visual Studio 2017 工具链执行相同操作的方法:
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.4.3
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'
C:\Users\mikek\source>cd C:\develop\so\scrap
C:\develop\so\scrap>dir
Volume in drive C has no label.
Volume Serial Number is 16C7-F955
Directory of C:\develop\so\scrap
16/12/2017 18:31 <DIR> .
16/12/2017 18:31 <DIR> ..
16/12/2017 12:41 116 greeting.cpp
16/12/2017 12:42 99 greeting.h
16/12/2017 12:10 109 hello.cpp
16/12/2017 12:12 90 hello.h
16/12/2017 16:21 197 main.cpp
16/12/2017 12:25 117 niceday.cpp
16/12/2017 12:26 96 niceday.h
7 File(s) 824 bytes
2 Dir(s) 101,877,473,280 bytes free
编译hello.cpp:
C:\develop\so\scrap>cl /W4 /EHsc /c /Fohello.obj hello.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
hello.cpp
编译niceday.cpp:
C:\develop\so\scrap>cl /W4 /EHsc /c /Foniceday.obj niceday.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
niceday.cpp
制作静态库:
C:\develop\so\scrap>lib /out:libgreet.lib hello.obj niceday.obj
Microsoft (R) Library Manager Version 14.11.25547.0
Copyright (C) Microsoft Corporation. All rights reserved.
编译greeting.cpp:
C:\develop\so\scrap>cl /W4 /EHsc /c /Fogreeting.obj greeting.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
greeting.cpp
链接libgreeting.dll:
C:\develop\so\scrap>link /dll /out:libgreeting.dll greeting.obj libgreet.lib
Microsoft (R) Incremental Linker Version 14.11.25547.0
Copyright (C) Microsoft Corporation. All rights reserved.
Creating library libgreeting.lib and object libgreeting.exp
这里,如您所知,Microsoft 链接器创建了一个导入库 libgreeting.lib
(不要与静态库 libgreet.lib 混淆)用于
将 libgreeting.dll 链接到程序或另一个 DLL。
编译main.cpp:
C:\develop\so\scrap>cl /W4 /EHsc /c /Fomain.obj main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
链接我们的程序(使用导入库 libgreeting.lib 代替
libgreeting.dll):
C:\develop\so\scrap>link /out:prog.exe main.obj libgreeting.lib
Microsoft (R) Incremental Linker Version 14.11.25547.0
Copyright (C) Microsoft Corporation. All rights reserved.
然后运行:
C:\develop\so\scrap>prog
Hello world!
What a nice day!
I said...
Hello world!
What a nice day!
稍后
如果 foo 首先是在没有 __declspec(dllexport) 的情况下编译的,并且在我构建 B.lib 时有这样的声明 void foo()。
但是当我构建 C.exe 时,我将 foo 的声明更改为 __declspec(dllexport) void foo()。问题是我还能做到以上吗?
原则上,如果你编译一个在其头文件中带有foo的特定声明的目标文件
然后将该声明 更改 为 #include 中的头文件
编译其他一些目标文件,那么你很可能是在对编译器撒谎
第二次编译,当您将这些目标文件链接到某个程序或 DLL 时,您
可以预料到坏事会发生。
但是,在这种情况下,您可以侥幸逃脱。在上面的示例中,您
可以先编译,比如说,hello.cpp,声明在hello.h:
extern void hello(void);
稍后,当你编译greeting.cpp,其中#includes hello.h,你
可以将声明更改为:
extern __declspec(dllexport) void hello(void);
当您链接libgreeting.dll 时,hello 将被 DLL_exported。
带有__declspec(dllexport) 的声明改进而不是矛盾
没有的。在链接libgreeting.dll 时,链接器看到一个非 DLL 导出的引用
到hello.obj 中的hello 和greeting.obj 中的DLL 导出引用。它
DLL 导出符号,因为它至少看到了一个 DLL 导出的引用。
毫无疑问,这是一个hack。