windows 有没有更好的方法在 C++ 中加载 dll?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2060403/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a better way to load a dll in C++?
提问by Brian T Hannan
Right now I do something like this and it seems messy if I end having a lot of functions I want to reference in my DLL. Is there a better and cleaner way of accessing the functions without having to create a typedef for each function definition so that it will compile and load the function properly. I mean the function definitions are already in the .h file and I shouldn't have to redeclare them after I load the function (or do I?) Is there a better solution than using LoadLibary? I don't necessarily need that function if there is a way I can do the same thing within Visual Studio 2005 project settings.
现在我做这样的事情,如果我结束了很多我想在我的 DLL 中引用的函数,这看起来很混乱。是否有一种更好、更简洁的方法来访问函数,而无需为每个函数定义创建 typedef,以便它能够正确编译和加载函数。我的意思是函数定义已经在 .h 文件中,我不应该在加载函数后重新声明它们(或者我是吗?)有没有比使用 LoadLibary 更好的解决方案?如果有一种方法可以在 Visual Studio 2005 项目设置中做同样的事情,我不一定需要该功能。
BHannan_Test_Class.h
BHannan_Test_Class.h
#include "stdafx.h"
#include <windows.h>
#ifndef BHANNAN_TEST_CLASS_H_
#define BHANNAN_TEST_CLASS_H_
extern "C" {
// Returns n! (the factorial of n). For negative n, n! is defined to be 1.
int __declspec (dllexport) Factorial(int n);
// Returns true iff n is a prime number.
bool __declspec (dllexport) IsPrime(int n);
}
#endif // BHANNAN_TEST_CLASS_H_
BHannan_Test_Class.cpp
BHannan_Test_Class.cpp
#include "stdafx.h"
#include "BHannan_Test_Class.h"
// Returns n! (the factorial of n). For negative n, n! is defined to be 1.
int Factorial(int n) {
int result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;
}
// Returns true iff n is a prime number.
bool IsPrime(int n) {
// Trivial case 1: small numbers
if (n <= 1) return false;
// Trivial case 2: even numbers
if (n % 2 == 0) return n == 2;
// Now, we have that n is odd and n >= 3.
// Try to divide n by every odd number i, starting from 3
for (int i = 3; ; i += 2) {
// We only have to try i up to the squre root of n
if (i > n/i) break;
// Now, we have i <= n/i < n.
// If n is divisible by i, n is not prime.
if (n % i == 0) return false;
}
// n has no integer factor in the range (1, n), and thus is prime.
return true;
}
dll_test.cpp
dll_test.cpp
#include <BHannan_Test_Class.h>
typedef int (*FactorialPtr) (int);
FactorialPtr myFactorial=NULL;
// Tests factorial of negative numbers.
TEST(FactorialTest, Negative) {
HMODULE myDLL = LoadLibrary("BHannan_Sample_DLL.dll");
if(myDLL) {
myFactorial = (FactorialPtr) GetProcAddress(myDLL,"Factorial");
if(myFactorial)
{
EXPECT_EQ(1, myFactorial(-5));
EXPECT_EQ(1, myFactorial(-1));
EXPECT_TRUE(myFactorial(-10) > 0);
}
FreeLibrary(myDLL);
}
}
采纳答案by Dmitry
After building your .dll get the .lib file nearby and link your test application with it. Use functions as they are declared in .h
构建您的 .dll 后,获取附近的 .lib 文件并将您的测试应用程序与其链接。使用在 .h 中声明的函数
There's a minor change you need to do in your header file:
您需要在头文件中做一个小改动:
#ifdef EXPORTS_API
#define MY_API_EXPORT __declspec (dllexport)
#else
#define MY_API_EXPORT __declspec (dllimport)
#endif
extern "C" {
int MY_API_EXPORT Factorial(int n);
// do the same for other functions
}
This way, when building your dll you define EXPORTS_API
in your project settings and functions get exported, in the client application, no need to define anything.
这样,在构建 dll 时,您EXPORTS_API
在项目设置中定义并导出函数,在客户端应用程序中,无需定义任何内容。
回答by John Dibling
In the Windows world, there are (at least) 4 ways to use DLLs:
在 Windows 世界中,有(至少)4 种使用 DLL 的方法:
- Run-Time Dynamic Linking(What you're doing now)
- Load-Time Dynamic Linking(the "typical" way of using DLLs)
- Delay-Load Dynamic Linking
- DLL Forwarding
I don't have to explain Run-Time Dynamic Linkingsince you're already doing it. I choose not to explain Delay-Load Dynamic Linkingnow beyond just describing what it is in general terms. Delay Load is essentially the same as Load-Time Dynamic Linking except it's done Just-In-Time instead of at application load. This is not as useful or as beneficial as you might think, it is difficult to work with and tricky to code for. So let's not go there, at least for now. DLL Forwardingis even more exotic than Delay-Loading -- so exotic, I'd never even heard of it until @mox mentioned it in the comments. I'll let you read the link above to learn about it, but suffice it to say that DLL Forwarding is when you call an exported function in one DLL but that request is actually forwardedto another function in a different DLL.
我不必解释运行时动态链接,因为您已经在这样做了。我现在选择不解释延迟加载动态链接,而不仅仅是描述它的一般术语。延迟加载本质上与加载时动态链接相同,只是它是实时完成的,而不是在应用程序加载时完成。这并不像您想象的那样有用或有益,它很难使用并且编码起来很棘手。所以我们不要去那里,至少现在是这样。 DLL 转发比延迟加载更具异国情调——如此异国情调,直到@mox 在评论中提到它,我才听说过它。我会让你阅读上面的链接来了解它,但足以说明 DLL 转发是当你调用一个 DLL 中的导出函数但该请求实际上转发到另一个 DLL 中的另一个函数时。
Load-Time Dynamic Linking
加载时动态链接
This is what I would consider to be Vanilla DLL Linking.
这就是我认为的Vanilla DLL Linking。
This is what most people are referring to when they refer to using DLLs in their applications. You just #include
the DLL's header file and link to the LIB file. No need to GetProcAddress()
or create function pointer typedefs. Here's how it works in a nutshell:
这是大多数人在提到在他们的应用程序中使用 DLL 时所指的内容。您只需#include
DLL 的头文件并链接到 LIB 文件。无需GetProcAddress()
或创建函数指针 typedef。简而言之,它是如何工作的:
1) You typically get 3 files: a DLL with the runtime code, a LIB file and a header file. The header file is just a header file -- it describes all the facilities in the DLL you can use.
1) 您通常会获得 3 个文件:一个包含运行时代码的 DLL、一个 LIB 文件和一个头文件。头文件只是一个头文件——它描述了您可以使用的 DLL 中的所有功能。
2) You write your application, #include
'ing the header file from the DLL and making calls to those functions just like you would use any function in any header file. The compiler knows the names of functions and objects you use because they are in the DLL's header file. But it doesn't know where they are in memory yet. That is where the LIB file comes in...
2) 您编写应用程序,#include
从 DLL 中读取头文件并调用这些函数,就像您在任何头文件中使用任何函数一样。编译器知道您使用的函数和对象的名称,因为它们位于 DLL 的头文件中。但它还不知道它们在内存中的哪个位置。这就是 LIB 文件的用武之地...
3) You go to the linker settings for your project and add an "additional library dependency," specifying the LIB file. The LIB file tells the linker where the functions and objects you use from the H file reside in memory (in relative terms, not absolute terms, obviously).
3) 转到项目的链接器设置并添加“附加库依赖项”,指定 LIB 文件。LIB 文件告诉链接器您在 H 文件中使用的函数和对象在内存中的位置(显然是相对而言,而不是绝对而言)。
4) Compile your app. If you have set everything up correctly it should compile, link and run. When you get "unresolved external reference" linker errors commonly this is due to things not being set up right. You may either have not specified the correct path to the LIB file or you need to include more LIB files.
4) 编译您的应用程序。如果您正确设置了所有内容,它应该可以编译、链接和运行。当您收到“未解析的外部引用”链接器错误时,通常是由于设置不正确。您可能没有指定 LIB 文件的正确路径,或者您需要包含更多 LIB 文件。
回答by Georg Fritzsche
Import libraries(.lib) simplify DLL usage in user code, see e.g. herefor a basic tutorial.
They spare the users from loading the DLL, using GetProcAddress()
and function pointers themselves - they statically link to the import library instead which does the work for them.
导入库(.lib) 简化了用户代码中的 DLL 使用,例如,请参见此处的基本教程。
他们免除了用户加载 DLL、使用GetProcAddress()
和函数指针本身——他们静态链接到导入库,而不是为他们完成工作。
回答by Igor Zevaka
Why don't you get VS to generate a shim static library around your DLL. That way all you have to do is add a calling convention in the header file and add a couple of pre-procesor directives. The easiest way to figure out how to do it is to create a new DLL project (Visual C++>Win32 Project, Choose DLL Project, check Import symbols)
为什么不让 VS 围绕 DLL 生成一个 shim 静态库。这样你所要做的就是在头文件中添加一个调用约定并添加几个预处理器指令。弄清楚如何去做的最简单的方法是创建一个新的 DLL 项目(Visual C++>Win32 项目,选择 DLL 项目,检查导入符号)
Use the main header file as an example on how to decorate your classes with the import/export calling convention. This head is the important bit as it explains how to use the functions and classes declared there:
以主头文件为例,说明如何使用导入/导出调用约定来装饰您的类。这个头部很重要,因为它解释了如何使用在那里声明的函数和类:
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the DLLTEST2_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// DLLTEST2_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef DLLTEST2_EXPORTS
#define DLLTEST2_API __declspec(dllexport)
#else
#define DLLTEST2_API __declspec(dllimport)
#endif
// This class is exported from the dlltest2.dll
class DLLTEST2_API Cdlltest2 {
public:
Cdlltest2(void);
// TODO: add your methods here.
};
extern DLLTEST2_API int ndlltest2;
DLLTEST2_API int fndlltest2(void);
Then, in the project that uses that DLL simply include the header file and .lib
that the DLL project generated. That way it automatically loads the DLL and you can use all the functions as though statically linked.
然后,在使用该 DLL 的项目中,只需包含头文件和.lib
生成的 DLL 项目。这样它会自动加载 DLL,您可以像静态链接一样使用所有函数。
回答by asveikau
You can link to the DLL's symbols directly instead of using GetProcAddress()
, which gets the address of a function at runtime.
您可以直接链接到 DLL 的符号,而不是使用GetProcAddress()
,后者在运行时获取函数的地址。
Example header file snippet:
示例头文件片段:
#if defined(MY_LIB_STATIC)
#define MY_LIB_EXPORT
#elif defined(MY_LIB_EXPORTS)
#define MY_LIB_EXPORT __declspec(dllexport)
#else
#define MY_LIB_EXPORT __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C"
{
#endif
int MY_LIB_EXPORT Factorial(int n);
#ifdef __cplusplus
}
#endif
Then in BHannan_Test_Class.cpp
, you would #define MY_LIB_EXPORTS
before including the header.
然后在BHannan_Test_Class.cpp
,你会#define MY_LIB_EXPORTS
在包含标题之前。
In dll_test.cpp
you would include the header and just use Factorial()
as you would use a normal function. When linking, you want to link to the import librarythat building your DLL produced. This makes the symbols from the DLL available to the code that links to it.
在dll_test.cpp
您将包含标题并Factorial()
像使用普通函数一样使用。链接时,您希望链接到生成 DLL的导入库。这使得链接到它的代码可以使用来自 DLL 的符号。
回答by villintehaspam
When you build your dll, you should also get a lib file that you can link with. That will do the heavy lifting in the background for you. Include the header file that you've created, but change into dllimport instead of dllexport. You can use a define for that, so that for your dll project it uses dllexport and all others that do not use this define will use dllimport.
当您构建 dll 时,您还应该获得一个可以链接的 lib 文件。这将为您在后台完成繁重的工作。包括您创建的头文件,但更改为 dllimport 而不是 dllexport。您可以为此使用定义,以便您的 dll 项目使用 dllexport,而所有其他不使用此定义的项目将使用 dllimport。
You only need to do a manual LoadLibrary and the typedef if you want to dynamically load the dll yourself. If you do the above approach your exe will fail if the dll is not present.
如果您想自己动态加载 dll,您只需要手动执行 LoadLibrary 和 typedef。如果您执行上述方法,如果 dll 不存在,您的 exe 将失败。
You can also build the dll project into a static library and load that instead, which will also get rid of that problem but will increase the size of your exe. This is of course not an option if you really want it to be a dll.
您还可以将 dll 项目构建到静态库中并加载它,这也将解决该问题,但会增加 exe 的大小。如果您真的希望它是一个 dll,这当然不是一个选项。
回答by Pete Kirkham
Of course you don't needthe typedef
当然你不需要typedef
int (* myFactorial)(int) = 0;
HMODULE myDLL = LoadLibrary("BHannan_Sample_DLL.dll");
if(myDLL) {
myFactorial = reinterpret_cast<int (*) (int)>( GetProcAddress(myDLL,"Factorial"));
...
}
or, exploiting C++ initialise and test idiom
或者,利用 C++ 初始化和测试习语
if ( HMODULE myDLL = LoadLibrary("BHannan_Sample_DLL.dll") ) {
if ( int (* myFactorial)(int) = GetProcAddress ( myFactorial, myDLL, "Factorial" ) ) {
...
}
}
given this to tidy up the repetition of the type
鉴于此整理类型的重复
template <typename T>
T GetProcAddress ( const T&, HMODULE mod, const char* name)
{
return reinterpret_cast<T> (GetProcAddress(mod,name));
}
But not using a typedef is generally worse rather than better, as C's function pointer types are a bit tricky to get right unless you're using them regularly. (if you are using them regularly, then your software may be somewhat unorthodox).
但不使用 typedef 通常更糟而不是更好,因为 C 的函数指针类型很难正确使用,除非您经常使用它们。(如果您经常使用它们,那么您的软件可能有点不正统)。
The Microsoft dllimport
extensions and compiler create a static library which does the loading for you and provides trampolines or thunks, as others have posted. Unless you're creating a plug-in system which doesn't know which dll it will load, then use that instead.
Microsoftdllimport
扩展和编译器创建了一个静态库,它为您加载并提供蹦床或 thunk,正如其他人发布的那样。除非您正在创建一个不知道它将加载哪个 dll 的插件系统,否则请改用它。