在win32上,我构建了一个名为 A.dll 的动态库,它链接到一个名为 B.lib 的静态库,并且还构建了一个名为 C的可执行文件.exe 仅依赖于 A.dll 。
但是现在,在 C.exe 中如果我想使用 foo 这个只在 B.lib 中有定义的函数,我必须再次将 C.exe 与 B.lib 相关联。
问题是,在构建 A.dll时,我可以直接从 B.lib 将 foo 导出到 A.dll ,怎么样?
此外,我想知道在与GCC打交道时会有什么。
答案 0 :(得分:9)
可以从DLL导出函数foo
,前提是: -
当您使用foo
属性声明__declspec(dllexport)
时
将函数编译成目标文件,比如foo.obj
您将foo.obj
链接到DLL。
foo.obj
如何链接到DLL无关紧要。
也许您在DLL的链接中明确指定foo.obj
。
也许你已将foo.obj
置于静态库中,比如foobar.lib
,以及链接
DLL包含对函数foo
的一些引用。然后链接器会
从foo.obj
中提取foobar.lib
并将其链接到DLL中以进行解析
那个参考。
如果通过从静态库中提取来链接foo.obj
,则链接
与foo.obj
按名称链接的完全相同。一个静态库很简单
一包目标文件,链接器可以从中拾取它需要携带的目标文件
关于联系。链接完成后,生成的程序或DLL没有依赖关系
在静态库上。如果它需要包中的任何目标文件,它现在已经得到它们。它不需要包。
这是一个使用GCC mingw-w64工具链的Windows的插图 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
现在我们将使用libgreeting.dll
和静态创建一个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
#include
hello.h
extern __declspec(dllexport) void hello(void);
时
可能会将声明更改为:
hello
,当您链接libgreeting.dll
时,__declspec(dllexport)
将成为DLL_exported。
libgreeting.dll
的声明提炼而不是与之相矛盾
一个没有。在链接hello
时,链接器会看到非DLL导出的引用
hello.obj
中的greeting.obj
和{{1}}中的DLL导出的引用。它
DLL导出符号,因为它至少看到过一个DLL导出的引用。
毫无疑问,这是 hack 。