带你玩转Visual Studio——调用约定与(动态)库

  • 时间:
  • 浏览:0

error LNK2019: unresolved external symbol __imp__add@8 referenced in function _main

error LNK2019: unresolved external symbol __imp__multi@16 referenced in function _main

上一章肯能讲了,C/C++默认的调用约定是__cdecl,那并能并能 修改这个默认的调用约定呢?

答案是肯定的。假设你有俩个工程叫华VisualStudio,你想让这个工程下的所有函数默认都使用__stdcall,右键工程->Properties->Configuration Properties->C/C++->Advanced->Calling Convention,将其设置为__stdcall即可。

上一篇回顾:

带你玩转Visual Studio——调用约定__cdecl、__stdcall和__fastcall

要使编译出的动态库更有通过性,大家一般会用C的最好的法子来进行进行编译,即使用

【要点二】:要使编译出的动态库在不同的调用约定下都能使用,还要对所有要导出的函数使用显示的调用约定

同理,大家将VisualStudio工程的默认调用约定改成__stdcall,再进行编译,会报以下俩个错误:

大家把#define EAPI extern "C" _API_中的extern "C"注释掉

error LNK2019: unresolved external symbol __imp_@add@8 referenced in function _main

error LNK2019: unresolved external symbol __imp_@sub@8 referenced in function _main

上一篇文章带你玩转Visual Studio——调用约定__cdecl、__stdcall和__fastcall中肯能讲述了__cdecl、__stdcall和__fastcall几种调用约定的主要区别。这个章将进一步深入了解不同调用约定对编译后函数修饰名的影响,及调用约定对库函数的影响。

相信你一定明白为什么我么我么回事了,让人不再解释了。由此可见

进行导出接口的声明。

error LNK2019: unresolved external symbol __imp__sub referenced in function _main

error LNK2019: unresolved external symbol __imp__multi referenced in function _main

大家并能并能 就看括号中的函数修饰名(_add)、(_sub@8)、(@multi@16)遵循上述所说的规则。

让人就看这个个名称的函数都没被更改了,这时编译出来的dll才具有跨平台、跨编译器的通用性。

C编译最好的法子对函数名的修饰规则在上一章肯能讲了,再来回顾一下:

说明:

PA表示指针,上面的代号表明指针类型,肯能相相似型的指针连续经常经常出现,以“0”代替,俩个“0”代表一次重复;如EAPI void func3(long pL, long pL2, double* pD);修饰名为:?func3@@YGXPAJ0PAN@Z

这是肯能VisualStudio工程默认是使用__cdecl约定,使用到sub和multi的前一天,会去找_sub(或sub)、_multi(或multi)的函数名。但sub是__stdcall约定,编译出的StdcallProject.dll中的名称是_sub@8;而multi是__fastcall约定,编译出的FastcallProject.dll中名称是@multi@16。这个这个自然就找并能 。

修饰名为:?func4@@YGHPAVNode@@PAH@Z

上一章提到的几种不同调用约定对编译后函数名的修饰规则,这个这个 指C的编译最好的法子。VS涵盖这个编译最好的法子,这个是C的编译最好的法子,这个是C++的编译最好的法子。.c默认使用C的编译最好的法子,而.cpp文件默认使用C++的编译最好的法子。C和C++对函数名的修饰规则是不同的。

Stdcall.h

这段代码简单解释一下,肯能定义了DYNAMIC_EXPORT宏,则_API_表示导出接口,但会 表示导入接口;而 #define EAPI extern "C" API 表示以C的最好的法子导出(导入)接口。大家在这个个工程中都加入DYNAMIC_EXPORT预编译宏,表示导出接口;而在使用这个个工程(库)的工程(如VisualStudio)中不加DYNAMIC_EXPORT宏,表示导入接口。

【要点一】:肯能俩个出理 方案涵盖多个工程,或俩个工程中使用了多个开源库,所有工程最好使用同这个默认调用约定。

这时,VisualStudio工程的默认调用约定不管是用__cdecl、__stdcall还是__fastcall,都能正常编译和运行。

俩个工程分别定义俩个函数如下:

对这个个工程进行编译,再用”dumpbin /exports ProjectName.dll”命令查看导出接口如下:

说明:这个值默认是空,肯能默认这个这个 __cdecl,当设置为__stdcal,表示这个工程下的所有函数默认使用__stdcall约定,除非在函数中进行了显示声明。设置为其它值含义与此相似。

FastCall.h

让人发现这和大家第一次查看的结果一样,但你仔细看一下二根绳子 绳子 结果和上面俩个怪怪的不样。二根绳子 绳子 结果左边(add)和右边名称不一样(_add),上面两条结果左边和右边名称都相同。这是肯能:

大家使用C的编译最好的法子来编译这个个工程,得到CdeclProject.dll、StdcallProject.dll、FastcallProject.dll俩个动态库,现在大家并能并能 在VisualStudio工程(这是生成目标为.exe的工程)中使用大家:

在C++的编译最好的法子中,为了能实现C++中的函数重载、继承,编译器增加了更多的修饰符号。

其中EAPI的定义如下:

【code1】:

编译VisualStudio,会发现报以下俩个错误:

【要点三】:要使编译出的动态库具有跨平台、跨编译器的通用性,还要使用模块文件定义导出接口(__cdecl调用约定除外)。

要命__stdcall和__fastcall调用约定的函数不被改变,大家还要使用模块文件。为CdeclProject、FastcallProject和StdcallProject俩个工程上加模块文件,右键工程 -> Add -> New Item -> Visual C++ -> Code -> Module-Definition File(.def)

肯能PA表示的是类对象的指针,则PA后接“V+类名+@@”,如

大家用”dumpbin /exports ProjectName.dll”命令查看这个个dll的导出接口:

通过关键字extern "C" __declspec(dllexport)声明的接口函数并能并能 保证__cdecl调用约定的函数名称不被改变(不被编译器上加特殊的符号进行修饰),却并能 保证__stdcall和__fastcall调用约定的函数不被改变。

....

把VisualStudio工程的默认调用约定改成__fastcall,再进行编译,会报以下俩个错误:

Cdecl.h

俩个工程中上加入俩个函数的声明和定义

下一篇要讲述的内容:

带你玩转Visual Studio2——Git与Github的使用

用C++的最好的法子来编译,再用命令查看函数修饰名如下:

肯能帮我使编译出的dll在不同的调用约定下都能正常使用,并能并能 原本实现:

上面显示使用调用约定的最好的法子觉得并能并能 使编译出的dll在任何调用约定下都能被使用,但仅限于在相同的编译器下。

为了说明这个点,大家做俩个验证。建俩个工程分别对应__cdecl、__stdcall和__fastcall,即为这个个工程分别设置默认__cdecl、__stdcall和__fastcall调用约定。

将这个个工程编译成动态库(dll),并用VS的”dumpbin /exports ProjectName.dll”命令查看这个个dll的接口如下: