从DLL和dllexport导出函数。

时间:2022-10-29 23:29:49

I'd like a simple example of exporting a function from a C++ windows DLL.

我想要一个简单的例子,从c++ windows DLL导出一个函数。

I'd like to see the header, the cpp file, and the def file (if absolutely required).

我希望看到header、cpp文件和def文件(如果绝对需要的话)。

I'd like the exported name to be undecorated. I'd like to use the most standard calling convention (__stdcall?). I'd like the use __declspec(dllexport) and not have to use a DEF file.

我想要没有装饰的出口名称。我想使用最标准的调用约定(__stdcall?)我希望使用__declspec(dllexport),而不必使用DEF文件。

For example:

例如:

  //header
  extern "C"
  {
   __declspec(dllexport) int __stdcall foo(long bar);
  }

  //cpp
  int __stdcall foo(long bar)
  {
    return 0;
  }

I'm trying to avoid the linker added underscores and/or numbers (byte counts?) to the name.

我试图避免链接器添加下划线和/或数字(字节数?)的名称。

I'm OK with not supporting dllimport and dllexport using the same header. I don't want any information about exporting C++ class methods, just c-style global functions.

我可以使用相同的标题不支持dllimport和dllexport。我不想要任何关于导出c++类方法的信息,只需要C风格的全局函数。

UPDATE

更新

Not including the calling convention (and using extern "C") gives me the export names as I like, but what does that mean? Is whatever default calling convention I'm getting what pinvoke (.NET), declare (vb6), and GetProcAddress would expect? (I guess for GetProcAddress it would depend on the function pointer the caller created).

不包括调用约定(和使用外部“C”)给了我我喜欢的导出名称,但是这意味着什么呢?无论默认调用约定是什么,我都得到了pinvoke (.NET)、声明(vb6)和GetProcAddress所期望的内容?(我猜测GetProcAddress将取决于调用者创建的函数指针)。

I want this DLL to be used without a header file, so I don't really need the a lot of the fancy #defines to make the header usable by a caller.

我希望这个DLL不用头文件就可以使用,所以我不需要很多花哨的#定义来让调用者可以使用它。

I'm OK with an answer being that I have to use a DEF file.

我没有问题,因为我必须使用DEF文件。

4 个解决方案

#1


104  

If you want plain C exports, use a C project not C++. C++ DLLs rely on name-mangling for all the C++isms (namespaces etc...). You can compile your code as C by going into your project settings under C/C++->Advanced, there is an option "Compile As" which cooresponds to the compiler switches /TP and /TC.

如果您想要纯C导出,请使用C项目而不是c++。c++ dll依赖于对所有c++语言(名称空间等)的名称管理。您可以通过在C/ c++ ->中进入您的项目设置来编译您的代码,其中有一个选项“编译”为编译器开关/TP和/TC的cooresponds。

Exporting/Importing DLL Libs in VC++

What you really want to do is define a conditional macro in a header that will be included in all of the source files in your DLL project:

你真正想做的是在标题中定义一个条件宏,它将包含在你的DLL项目的所有源文件中:

#ifdef LIBRARY_EXPORTS
#    define LIBRARY_API __declspec(dllexport)
#else
#    define LIBRARY_API __declspec(dllimport)
#endif

Then on a function that you want to be exported you use LIBRARY_API:

然后在一个您想要导出的函数中使用LIBRARY_API:

LIBRARY_API int GetCoolInteger();

In your library build project create a define LIBRARY_EXPORTS this will cause your functions to be exported for your DLL build.

在您的库构建项目中创建一个定义LIBRARY_EXPORTS,这会使您的函数导出到DLL构建中。

Since LIBRARY_EXPORTS will not be defined in a project consuming the DLL, when that project includes the header file of your library all of the functions will be imported instead.

由于LIBRARY_EXPORTS不会在使用DLL的项目中定义,所以当该项目包含库的头文件时,所有的函数都将被导入。

If your library is to be cross-platform you can define LIBRARY_API as nothing when not on Windows:

如果您的库是跨平台的,那么您可以将LIBRARY_API定义为没有在Windows上的任何东西:

#ifdef _WIN32
#    ifdef LIBRARY_EXPORTS
#        define LIBRARY_API __declspec(dllexport)
#    else
#        define LIBRARY_API __declspec(dllimport)
#    endif
#elif
#    define LIBRARY_API
#endif

When using dllexport/dllimport you do not need to use DEF files, if you use DEF files you do not need to use dllexport/dllimport. The two methods accomplish the same task different ways, I believe that dllexport/dllimport is the recommended method out of the two.

当使用dllexport/dllimport时,您不需要使用DEF文件,如果您使用的是DEF文件,您不需要使用dllexport/dllimport。这两种方法以不同的方式完成相同的任务,我认为dllexport/dllimport是推荐的方法。

Exporting unmangled functions from a C++ DLL for LoadLibrary/PInvoke

If you need this to use LoadLibrary and GetProcAddress, or maybe doing PInvoke from .NET you can use extern "C" inline with your dllexport. And since we are using GetProcAddress instead of dllimport we don't need to do the ifdef dance from above, just a simple dllexport:

如果您需要使用LoadLibrary和GetProcAddress,或者从。net中执行PInvoke,您可以使用与您的dllexport内联的外部“C”。由于我们使用的是GetProcAddress而不是dllimport,所以我们不需要从上面执行ifdef,只需一个简单的dllexport:

The Code:

代码:

#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)

EXTERN_DLL_EXPORT int getEngineVersion() {
  return 1;
}

EXTERN_DLL_EXPORT void registerPlugin(Kernel &K) {
  K.getGraphicsServer().addGraphicsDriver(
    auto_ptr<GraphicsServer::GraphicsDriver>(new OpenGLGraphicsDriver())
  );
}

And here's what the exports look like with Dumpbin /exports:

这是出口看起来和Dumpbin /出口是一样的:

  Dump of file opengl_plugin.dll

  File Type: DLL

  Section contains the following exports for opengl_plugin.dll

    00000000 characteristics
    49866068 time date stamp Sun Feb 01 19:54:32 2009
        0.00 version
           1 ordinal base
           2 number of functions
           2 number of names

    ordinal hint RVA      name

          1    0 0001110E getEngineVersion = @ILT+265(_getEngineVersion)
          2    1 00011028 registerPlugin = @ILT+35(_registerPlugin)

So this code works fine:

所以这段代码运行良好:

m_hDLL = ::LoadLibrary(T"opengl_plugin.dll");

m_pfnGetEngineVersion = reinterpret_cast<fnGetEngineVersion *>(
  ::GetProcAddress(m_hDLL, "getEngineVersion")
);
m_pfnRegisterPlugin = reinterpret_cast<fnRegisterPlugin *>(
  ::GetProcAddress(m_hDLL, "registerPlugin")
);

#2


13  

For C++ :

c++:

I just faced the same issue and I think it is worth mentioning a problem comes up when one use both __stdcall (or WINAPI) and extern "C":

我只是面临着同样的问题,我认为值得一提的是当一个人同时使用__stdcall(或WINAPI)和外部“C”时出现问题:

As you know extern "C" removes the decoration so that instead of :

正如你所知道的,“C”去掉了装饰,而不是:

__declspec(dllexport) int Test(void)                        --> dumpbin : ?Test@@YaHXZ

you obtain a symbol name undecorated:

您获得一个未修饰的符号名称:

extern "C" __declspec(dllexport) int Test(void)             --> dumpbin : Test

However the _stdcall ( = macro WINAPI, that changes the calling convention) also decorates names so that if we use both we obtain :

但是,_stdcall(=宏WINAPI,更改了调用约定)也会修饰名称,以便我们使用这两个名称时:

   extern "C" __declspec(dllexport) int WINAPI Test(void)   --> dumpbin : _Test@0

and the benefit of extern "C" is lost because the symbol is decorated (with _ @bytes)

而外部的“C”的好处就消失了,因为符号被修饰了(使用_ @bytes)

Note that this only occurs for x86 architecture because the __stdcall convention is ignored on x64 (msdn : on x64 architectures, by convention, arguments are passed in registers when possible, and subsequent arguments are passed on the stack.).

注意,这只发生在x86体系结构中,因为x64 (msdn: x64架构上,根据约定,在可能的情况下通过寄存器传递参数,然后在堆栈上传递后续的参数),因此忽略了__stdcall约定。

This is particularly tricky if you are targeting both x86 and x64 platforms.

如果你的目标是x86和x64平台,这一点尤其棘手。


Two solutions

两种解决方案

  1. Use a definition file. But this forces you to maintain the state of the def file.

    使用一个定义文件。但这迫使您维护def文件的状态。

  2. the simplest way : define the macro (see msdn) :

    最简单的方法:定义宏(参见msdn):

#define EXPORT comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)

#定义导出注释(链接器,“/EXPORT: __FUNCTION__”=“__FUNCDNAME__”)

and then include the following pragma in the function body:

然后在函数体中包含如下的pragma:

#pragma EXPORT

Full Example :

完整的例子:

 int WINAPI Test(void)
{
    #pragma EXPORT
    return 1;
}

This will export the function undecorated for both x86 and x64 targets while preserving the __stdcall convention for x86. The __declspec(dllexport) is not required in this case.

这将导出为x86和x64目标而未修饰的函数,同时保留x86的__stdcall约定。在本例中不需要__declspec(dllexport)。

#3


3  

I had exactly the same problem, my solution was to use module definition file (.def) instead of __declspec(dllexport) to define exports(http://msdn.microsoft.com/en-us/library/d91k01sh.aspx). I have no idea why this works, but it does

我遇到了完全相同的问题,我的解决方案是使用模块定义文件(.def)而不是__declspec(dllexport)来定义导出(http://msdn.microsoft.com/en-us/library/d91k01sh.aspx)。我不知道为什么会这样,但确实如此。

#4


-1  

I think _naked might get what you want, but it also prevents the compiler from generating the stack management code for the function. extern "C" causes C style name decoration. Remove that and that should get rid of your _'s. The linker doesn't add the underscores, the compiler does. stdcall causes the argument stack size to be appended.

我认为_naked可能会得到您想要的,但它也会阻止编译器生成函数的堆栈管理代码。外观“C”引起C风格的名称装饰。把它拿掉,这样就可以去掉你的_。链接器不添加下划线,编译器会添加下划线。stdcall使参数堆栈大小被追加。

For more, see: http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx

更多信息请访问:http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/kb/cpp/calling_customons_demystifiedaspx。

The bigger question is why do you want to do that? What's wrong with the mangled names?

更大的问题是,你为什么要这么做?这些被损坏的名字有什么问题?

#1


104  

If you want plain C exports, use a C project not C++. C++ DLLs rely on name-mangling for all the C++isms (namespaces etc...). You can compile your code as C by going into your project settings under C/C++->Advanced, there is an option "Compile As" which cooresponds to the compiler switches /TP and /TC.

如果您想要纯C导出,请使用C项目而不是c++。c++ dll依赖于对所有c++语言(名称空间等)的名称管理。您可以通过在C/ c++ ->中进入您的项目设置来编译您的代码,其中有一个选项“编译”为编译器开关/TP和/TC的cooresponds。

Exporting/Importing DLL Libs in VC++

What you really want to do is define a conditional macro in a header that will be included in all of the source files in your DLL project:

你真正想做的是在标题中定义一个条件宏,它将包含在你的DLL项目的所有源文件中:

#ifdef LIBRARY_EXPORTS
#    define LIBRARY_API __declspec(dllexport)
#else
#    define LIBRARY_API __declspec(dllimport)
#endif

Then on a function that you want to be exported you use LIBRARY_API:

然后在一个您想要导出的函数中使用LIBRARY_API:

LIBRARY_API int GetCoolInteger();

In your library build project create a define LIBRARY_EXPORTS this will cause your functions to be exported for your DLL build.

在您的库构建项目中创建一个定义LIBRARY_EXPORTS,这会使您的函数导出到DLL构建中。

Since LIBRARY_EXPORTS will not be defined in a project consuming the DLL, when that project includes the header file of your library all of the functions will be imported instead.

由于LIBRARY_EXPORTS不会在使用DLL的项目中定义,所以当该项目包含库的头文件时,所有的函数都将被导入。

If your library is to be cross-platform you can define LIBRARY_API as nothing when not on Windows:

如果您的库是跨平台的,那么您可以将LIBRARY_API定义为没有在Windows上的任何东西:

#ifdef _WIN32
#    ifdef LIBRARY_EXPORTS
#        define LIBRARY_API __declspec(dllexport)
#    else
#        define LIBRARY_API __declspec(dllimport)
#    endif
#elif
#    define LIBRARY_API
#endif

When using dllexport/dllimport you do not need to use DEF files, if you use DEF files you do not need to use dllexport/dllimport. The two methods accomplish the same task different ways, I believe that dllexport/dllimport is the recommended method out of the two.

当使用dllexport/dllimport时,您不需要使用DEF文件,如果您使用的是DEF文件,您不需要使用dllexport/dllimport。这两种方法以不同的方式完成相同的任务,我认为dllexport/dllimport是推荐的方法。

Exporting unmangled functions from a C++ DLL for LoadLibrary/PInvoke

If you need this to use LoadLibrary and GetProcAddress, or maybe doing PInvoke from .NET you can use extern "C" inline with your dllexport. And since we are using GetProcAddress instead of dllimport we don't need to do the ifdef dance from above, just a simple dllexport:

如果您需要使用LoadLibrary和GetProcAddress,或者从。net中执行PInvoke,您可以使用与您的dllexport内联的外部“C”。由于我们使用的是GetProcAddress而不是dllimport,所以我们不需要从上面执行ifdef,只需一个简单的dllexport:

The Code:

代码:

#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)

EXTERN_DLL_EXPORT int getEngineVersion() {
  return 1;
}

EXTERN_DLL_EXPORT void registerPlugin(Kernel &K) {
  K.getGraphicsServer().addGraphicsDriver(
    auto_ptr<GraphicsServer::GraphicsDriver>(new OpenGLGraphicsDriver())
  );
}

And here's what the exports look like with Dumpbin /exports:

这是出口看起来和Dumpbin /出口是一样的:

  Dump of file opengl_plugin.dll

  File Type: DLL

  Section contains the following exports for opengl_plugin.dll

    00000000 characteristics
    49866068 time date stamp Sun Feb 01 19:54:32 2009
        0.00 version
           1 ordinal base
           2 number of functions
           2 number of names

    ordinal hint RVA      name

          1    0 0001110E getEngineVersion = @ILT+265(_getEngineVersion)
          2    1 00011028 registerPlugin = @ILT+35(_registerPlugin)

So this code works fine:

所以这段代码运行良好:

m_hDLL = ::LoadLibrary(T"opengl_plugin.dll");

m_pfnGetEngineVersion = reinterpret_cast<fnGetEngineVersion *>(
  ::GetProcAddress(m_hDLL, "getEngineVersion")
);
m_pfnRegisterPlugin = reinterpret_cast<fnRegisterPlugin *>(
  ::GetProcAddress(m_hDLL, "registerPlugin")
);

#2


13  

For C++ :

c++:

I just faced the same issue and I think it is worth mentioning a problem comes up when one use both __stdcall (or WINAPI) and extern "C":

我只是面临着同样的问题,我认为值得一提的是当一个人同时使用__stdcall(或WINAPI)和外部“C”时出现问题:

As you know extern "C" removes the decoration so that instead of :

正如你所知道的,“C”去掉了装饰,而不是:

__declspec(dllexport) int Test(void)                        --> dumpbin : ?Test@@YaHXZ

you obtain a symbol name undecorated:

您获得一个未修饰的符号名称:

extern "C" __declspec(dllexport) int Test(void)             --> dumpbin : Test

However the _stdcall ( = macro WINAPI, that changes the calling convention) also decorates names so that if we use both we obtain :

但是,_stdcall(=宏WINAPI,更改了调用约定)也会修饰名称,以便我们使用这两个名称时:

   extern "C" __declspec(dllexport) int WINAPI Test(void)   --> dumpbin : _Test@0

and the benefit of extern "C" is lost because the symbol is decorated (with _ @bytes)

而外部的“C”的好处就消失了,因为符号被修饰了(使用_ @bytes)

Note that this only occurs for x86 architecture because the __stdcall convention is ignored on x64 (msdn : on x64 architectures, by convention, arguments are passed in registers when possible, and subsequent arguments are passed on the stack.).

注意,这只发生在x86体系结构中,因为x64 (msdn: x64架构上,根据约定,在可能的情况下通过寄存器传递参数,然后在堆栈上传递后续的参数),因此忽略了__stdcall约定。

This is particularly tricky if you are targeting both x86 and x64 platforms.

如果你的目标是x86和x64平台,这一点尤其棘手。


Two solutions

两种解决方案

  1. Use a definition file. But this forces you to maintain the state of the def file.

    使用一个定义文件。但这迫使您维护def文件的状态。

  2. the simplest way : define the macro (see msdn) :

    最简单的方法:定义宏(参见msdn):

#define EXPORT comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)

#定义导出注释(链接器,“/EXPORT: __FUNCTION__”=“__FUNCDNAME__”)

and then include the following pragma in the function body:

然后在函数体中包含如下的pragma:

#pragma EXPORT

Full Example :

完整的例子:

 int WINAPI Test(void)
{
    #pragma EXPORT
    return 1;
}

This will export the function undecorated for both x86 and x64 targets while preserving the __stdcall convention for x86. The __declspec(dllexport) is not required in this case.

这将导出为x86和x64目标而未修饰的函数,同时保留x86的__stdcall约定。在本例中不需要__declspec(dllexport)。

#3


3  

I had exactly the same problem, my solution was to use module definition file (.def) instead of __declspec(dllexport) to define exports(http://msdn.microsoft.com/en-us/library/d91k01sh.aspx). I have no idea why this works, but it does

我遇到了完全相同的问题,我的解决方案是使用模块定义文件(.def)而不是__declspec(dllexport)来定义导出(http://msdn.microsoft.com/en-us/library/d91k01sh.aspx)。我不知道为什么会这样,但确实如此。

#4


-1  

I think _naked might get what you want, but it also prevents the compiler from generating the stack management code for the function. extern "C" causes C style name decoration. Remove that and that should get rid of your _'s. The linker doesn't add the underscores, the compiler does. stdcall causes the argument stack size to be appended.

我认为_naked可能会得到您想要的,但它也会阻止编译器生成函数的堆栈管理代码。外观“C”引起C风格的名称装饰。把它拿掉,这样就可以去掉你的_。链接器不添加下划线,编译器会添加下划线。stdcall使参数堆栈大小被追加。

For more, see: http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx

更多信息请访问:http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/kb/cpp/calling_customons_demystifiedaspx。

The bigger question is why do you want to do that? What's wrong with the mangled names?

更大的问题是,你为什么要这么做?这些被损坏的名字有什么问题?