有没有更好的方法在C++中加载DLL?

Bri*_*nan 10 c++ windows dll loadlibrary

现在我做这样的事情,如果我在我的DLL中有很多我想要引用的函数,它看起来很麻烦.是否有更好,更清晰的方式来访问函数,而无需为每个函数定义创建一个typedef,以便它可以正确地编译和加载函数.我的意思是函数定义已经在.h文件中,我不应该在加载函数后重新声明它们(或者我?)是否有比使用LoadLibary更好的解决方案?如果有一种方法可以在Visual Studio 2005项目设置中执行相同的操作,我不一定需要该功能.


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_
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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);
    }

}
Run Code Online (Sandbox Code Playgroud)

Joh*_*ing 22

在Windows世界中,有(至少)4种方式使用DLL:

  1. 运行时动态链接(你现在正在做什么)
  2. 加载时动态链接(使用DLL的"典型"方式)
  3. 延迟加载动态链接
  4. DLL转发

我不必解释运行时动态链接,因为你已经在做了.我现在选择不解释延迟加载动态链接,而不只是描述它的概括性.延迟加载与加载时动态链接基本相同,除非它是实时的而不是在应用程序加载时完成的.这并不像你想象的那样有用或有益,它很难用来编写代码.所以,至少现在,我们不要去那里. DLL转发甚至比延迟加载更具异国情调 - 如此异国情调,我甚至从未听过它,直到@mox在评论中提及它.我会让你阅读上面的链接来了解它,但足以说DLL转发就是你在一个DLL中调用导出的函数但该请求实际上被转发到另一个DLL中的另一个函数.

加载时动态链接

这就是我认为的Vanilla DLL Linking.

这是大多数人在他们的应用程序中引用DLL时所指的内容.您只需#includeDLL的头文件并链接到LIB文件.无需GetProcAddress()或创建函数指针typedef.以下是它的工作原理:

1)您通常会得到3个文件:带有运行时代码的DLL,LIB文件和头文件.头文件只是一个头文件 - 它描述了你可以使用的DLL中的所有功能.

2)您编写应用程序,#include从DLL中获取头文件并调用这些函数,就像在任何头文件中使用任何函数一样.编译器知道您使用的函数和对象的名称,因为它们位于DLL的头文件中.但它还不知道它们在记忆中的位置.这就是LIB文件的用武之地......

3)转到项目的链接器设置并添加"附加库依赖项",指定LIB文件.LIB文件告诉链接器您在H文件中使用的函数和对象驻留在内存中(相对而言,显然不是绝对术语).

4)编译您的应用程序.如果你已经正确设置了一切,它应该编译,链接和运行.当您得到"未解析的外部引用"链接器错误时,这通常是由于未正确设置的.您可能没有指定LIB文件的正确路径,或者您需要包含更多LIB文件.

  • @John,很好的答案,但根据你所包含的链接,你的术语似乎是倒退的.MSDN将"运行时动态链接"定义为OP正在执行的操作,并将"加载时动态链接"定义为您所描述的内容. (2认同)

Dmi*_*try 10

构建.dll后,获取附近的.lib文件,并将测试应用程序与其链接.使用在.h中声明的函数

您需要在头文件中进行一些小改动:

#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
}
Run Code Online (Sandbox Code Playgroud)

这样,在构建您的dll时,您EXPORTS_API在项目设置和函数中定义导出,在客户端应用程序中,无需定义任何内容.

  • Brian,是的,该库仍然是动态加载的.不同之处在于它在应用程序启动时立即加载,而不是等待对LoadLibrary的调用. (3认同)
  • 是的,它使用你的dll,lib只包含链接器所需的信息,它只是一个_exports库_,而不是真正的'静态库'. (2认同)