在构建链接到该静态库的动态库时,是否可以导出静态库的函数?

时间:2017-12-13 10:56:32

标签: c++ static-libraries dynamic-library

在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打交道时会有什么。

1 个答案:

答案 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.objlibgreeting.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导出的函数hellonicedaygreeting, 叫做。 重要的是,为此,所有这些都是 声明__declspec(dllexport)并将所有这些内容链接到libgreeting.dll 不知何故。碰巧的是,其中两个(helloniceday)从静态库链接而另一个(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