Есть ли лучший способ загрузить dll в C ++?

Прямо сейчас я делаю что-то вроде этого, и кажется грязным, если я заканчиваю наличие большого количества функций, я хочу сослаться в своем DLL. Есть ли лучший и более чистый способ получить доступ к функциям, не имея необходимость создавать typedef для каждого определения функции так, чтобы это собрало и загрузило функцию правильно. Я подразумеваю, что определения функции уже находятся в.h файле, и мне не придется повторно объявить их после того, как я загружаю функцию (или делаю меня?) Есть ли лучшее решение, чем использование LoadLibary? Мне не обязательно нужна та функция, если есть способ, которым я могу сделать то же самое в рамках Визуальных параметров настройки проекта Студии 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_

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

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

}
10
задан John Dibling 13 January 2010 в 21:32
поделиться

7 ответов

После создания вашего .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
}

таким образом, при создании вашей DLL определяют Exports_api в ваших настройках проекта и функции Вызвать, в клиентском приложении не нужно ничего определять.

10
ответ дан 3 December 2019 в 14:24
поделиться

Импорт библиотек (.lib) упрощает использование DLL в пользовательском коде, смотрите, например, здесь для базового руководства.
Они избавляют пользователей от загрузки DLL, используя GetProcAddress() и сами указатели функций - они статически связываются с библиотекой импорта, которая делает работу за них.

3
ответ дан 3 December 2019 в 14:24
поделиться

Почему вы не получаете VS для генерации прокладки статической библиотеки вокруг вашей DLL. Таким образом, все, что вам нужно сделать, это добавить конвенцию о вызове в заголовочном файле и добавить пару директив Pre-Precesor. Самый простой способ выяснить, как это сделать, это создать новый проект DLL (Visual C ++> Win32 Project, выберите DLL Project, проверьте символы импорта)

alt text

Используйте основной файл заголовка в качестве примера, как украсить ваши классы с Конвенция об импорте / экспорте. Эта голова является важным битом, поскольку он объясняет, как использовать функции и классы, объявленные там:

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

, затем в проекте, который использует, что DLL просто включает файл заголовка и .lib , который генерируется проект DLL Отказ Таким образом, он автоматически загружает DLL, и вы можете использовать все функции, как будто статически связанные.

1
ответ дан 3 December 2019 в 14:24
поделиться

Когда вы создаете свою dll, вы также должны получить lib-файл, с которым вы можете связаться. Это сделает для вас тяжелую работу в фоновом режиме. Включите заголовочный файл, который вы создали, но вместо dllexport перейдите в dllimport. Вы можете использовать определение для этого, так что для вашего проекта dll будет использоваться dllexport, а все остальные, которые не используют это определение, будут использовать dllimport.

Вам нужно только сделать вручную LoadLibrary и typedef, если вы хотите динамически загружать dll самостоятельно. Если вы сделаете описанный выше подход, ваш exe не справится, если dll не будет присутствовать.

Вы также можете собрать проект dll в статическую библиотеку и загрузить ее, что также избавит вас от этой проблемы, но увеличит размер вашего exe. Конечно, это не вариант, если вы действительно хотите, чтобы это была dll.

0
ответ дан 3 December 2019 в 14:24
поделиться

Вы можете напрямую связываться с символами DLL вместо использования GetProcAddress(), который получает адрес функции во время выполнения.

Пример фрагмента заголовочного файла:

#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

Затем в BHannan_Test_Class.cpp вы бы #определили MY_LIB_EXPORTS перед включением заголовка.

В dll_test.cpp вы бы включили заголовок и просто использовали Factorial() так же, как вы бы использовали обычную функцию. При линковке вы хотите связать с библиотекой import, которая собирает созданную вами DLL. Это делает символы из DLL доступными для кода, который на нее ссылается.

0
ответ дан 3 December 2019 в 14:24
поделиться

В Windows World есть (по крайней мере) 4 способа использовать DLL:

  1. динамическое соединение времени выполнения (что вы делаете сейчас)
  2. Динамическое соединение времени («Типичный» способ использования DLLS)
  3. Динамическое соединение нагрузки задержки
  4. Переадресация DLL

Мне не нужно объяснять Динамическое соединение времени работы ] Так как вы уже делаете это. Я предпочитаю не объяснять динамическое связывание нагрузки задержки , теперь только что описывая то, что он имеет в общих чертах. Задержка нагрузки по существу то же самое, что и динамическое соединение времени нагрузки, за исключением того, что он выполняется просто вовремя, а не при нагрузке приложения. Это не так полезно или полезно, насколько вы можете подумать, сложно работать и сложно для кода. Так что давайте не будем идти туда, по крайней мере, на данный момент. Переадресация DLL еще более экзотична, чем загрузка задержки - так экзотика, я бы даже не слышал об этом, пока @mox не упомянул об этом в комментариях. Я позволю вам прочитать ссылку выше, чтобы узнать об этом, но достаточно сказать, что DLL-переадресация, когда вы вызываете экспортированную функцию в одну DLL, но этот запрос на самом деле переадресован к другой функции в другом DLL.

Время нагрузки динамическое соединение

Это то, что я бы считал связывание Vanilla DLL .

Это то, что большинство людей ссылаются, когда они ссылаются на использование DLL в своих приложениях. Вы просто #inClude Файл заголовка DLL и ссылка на файл lib. Не надо GetProcaddress () или создать функцию указателя Typedefs. Вот как он работает в двух словах:

1) Вы обычно получаете 3 файла: DLL с кодом выполнения, файл lib и файл заголовка. Файл заголовка - это просто заголовочный файл - он описывает все средства в DLL, которую вы можете использовать.

2) Вы пишете свое приложение, #inClude 'in include файл заголовка из DLL и делая вызовы этих функций, которые просто используете любую функцию в любом файле заголовка. Компилятор знает имена функций и объектов, которые вы используете, потому что они находятся в заголовом файле DLL. Но это не знает, где они еще в памяти. Вот где находится файл lib ...

3) Вы идете в настройки линкера для вашего проекта и добавить «дополнительную доступность библиотеки», указав файл lib. Файл lib сообщает линкеру, где функции и объекты, которые вы используете из файла H, находитесь в памяти (в относительном выражении, не абсолютные условия, очевидно).

4) Составьте свое приложение. Если вы установили все правильно, он должен скомпилировать, ссылку и запустить. Когда вы получаете ошибки линкера «нерешенные внешние ссылки», обычно это связано с тем, что их не настроено. Вы можете либо не указать правильный путь к файлу lib, либо вам нужно включить больше файлов lib.

22
ответ дан 3 December 2019 в 14:24
поделиться

Конечно, вам не нужночтобы был набран typedef

int (* myFactorial)(int) = 0;

HMODULE myDLL = LoadLibrary("BHannan_Sample_DLL.dll");

if(myDLL) {
    myFactorial = reinterpret_cast<int (*) (int)>( GetProcAddress(myDLL,"Factorial"));
    ...
}

или же, используя инициализацию C++ и тестовую идиому идиому

if ( HMODULE myDLL = LoadLibrary("BHannan_Sample_DLL.dll") ) {
    if ( int (* myFactorial)(int) = GetProcAddress ( myFactorial, myDLL, "Factorial" ) ) {
        ...
    }
}

дабы убрать повторение типа

template <typename T>
T GetProcAddress ( const T&, HMODULE mod, const char* name) 
{
    return reinterpret_cast<T> (GetProcAddress(mod,name)); 
}

но не использовать typedef обычно хуже, а не лучше, так как типы указателей на функции в C немного хитры, если только вы их не используете регулярно. (Если вы используете их регулярно, то ваша программа может быть несколько неортодоксальной).

Расширения dllimport от Microsoft и компилятор создают статическую библиотеку, которая делает загрузку за вас и предоставляет батуты или фишки, как написали другие. Если вы не создаете подключаемую систему, которая не знает, какую dll она будет загружать, то используйте ее вместо этого.

-2
ответ дан 3 December 2019 в 14:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: