Ошибка при использовании dllimport в клиенте DLL

Ребята, в настоящее время я создаю DLL и клиент, который работает с ней, используя стандартную процедуру, упомянутую во многих местах в Интернете. По сути, создайте проект DLL, который на самом деле определяет PROJECT_EXPORTS в файле Project.h. Что-то вроде этого:

// Assume the name of the project is SanProj and the header file is SanProj.h
#ifdef SANPROJ_EXPORTS
#define SANPROJ_API __declspec(dllexport)
#else
#define SANPROJ_API __declspec(dllimport)
#endif

Теперь обычным способом использования этого заголовка является включение его во все заголовки ваших классов API и использование SANPROJ_EXPORTS для «экспорта» объявлений в DLL и «импорта» объявлений в качестве клиента. Например, допустим, у нас есть заголовочный файл с классом валюты:

// currency.hpp
#include "SanProj.h"#include <ostream>
#include <string>

namespace SanProj {

class SANPROJ_API Currency {

public:
Currency();
const std::string& name();
const std::string& code();
bool empty() const;

protected:
std::string name_;
std::string code_;
};

SANPROJ_API bool operator==(const Currency&,
const Currency&);

SANPROJ_API bool operator!=(const Currency&,
const Currency&);

SANPROJ_API std::ostream& operator<<(std::ostream& out, Currency& c);
}

И еще один заголовочный файл с конкретными валютами:

// allccy.hpp
namespace SanProj {

class SANPROJ_API USDCurrency : public Currency {
public:
USDCurrency() {
name_ = "American Dollar";
code_ = "USD";
}
};class SANPROJ_API CADCurrency : public Currency {
public:
CADCurrency() {
name_ = "Canadian Dollar";
code_ = "CAD";
}
};

}

Вышеуказанные классы формируют контракт проекта DLL. Теперь давайте посмотрим на файлы проекта клиента, который представляет собой один класс с main функция:

#include "currency.hpp"#include "allccy.hpp"
#include <iostream>

using namespace SanProj;

int main(int argc, char* argv[])
{
USDCurrency uccy;
std::cout << uccy;
}

Предполагая, что все ссылки / настройки уже сделаны в проекте Visual Studio, я получаю следующую ошибку при попытке скомпилировать клиент:

1>testdll.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall SanProj::USDCurrency::~USDCurrency(void)" (__imp_??1USDCurrency@SanProj@@QAE@XZ)
1>testdll.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall SanProj::USDCurrency::USDCurrency(void)" (__imp_??0USDCurrency@SanProj@@QAE@XZ)

Не удивительно, что эта ошибка исчезает, когда я удаляю dllimport часть из SanProj.h файл и исполняемый файл создан.

У меня вопрос, в чем смысл сгенерированной IDE dllimport если мы не можем скомпилировать клиентов по заголовку? Есть ли способ, которым я могу продолжать использовать заголовок с обоими dllimport а также dllexports и удалить ошибки компоновщика? Кроме того, почему он пытается разрешить символ, который имеет dllimport из файла LIB?

ТИА,
/ Sasuke

РЕДАКТИРОВАТЬ: Команда компоновщика, используемая VisualStudio; как вы видите, он имеет файл LIB.

/OUT:»E:\vsprojects\SomeSln\Release\testdll.exe «/ ОБЯЗАТЕЛЬНО: НЕТ
/ NOLOGO «E: \ vsprojects \ SomeSln \ Release \ SanProj.lib» «kernel32.lib» «user32.lib» «gdi32.lib» «winspool.lib» «comdlg32.lib» «advapi32.lib» «shell32.lib «» ole32.lib «» oleaut32.lib «» uuid.lib «» odbc32.lib «» odbccp32.lib «/ MANIFEST
/ManifestFile:»Release\testdll.exe.intermediate.manifest»/ALLOWISOLATION / MANIFESTUAC: «level = ‘asInvoker’ uiAccess = ‘false'» / DEBUG /PDB:»E:\vsprojects\SomeSln\Release\testdll.pdb » / SUBSYSTEM: КОНСОЛЬ / OPT: REF / OPT: ICF
/PGD:»E:\vsprojects\SomeSln\Release\testdll.pgd «/ LTCG / TLBID: 1
/ DYNAMICBASE / NXCOMPAT / MACHINE: X86 / ERRORREPORT: QUEUE

3

Решение

РЕДАКТИРОВАТЬ: Конечно, я ошибаюсь как ответ jcopenha это ответ. Компоновщик жалуется на отсутствующий конструктор и деструктор, который вы не экспортируете в DLL. Однако остальное все еще в силе.

[…]

У вас должно быть две цели сборки (или проекты, в зависимости от среды, которую вы используете).

Первая цель создаст DLL. Файлы, которые нужно будет создать для этой цели, основаны на том, что вы сообщаете:

currency.hpp
allccy.hpp

и, вероятно, реализация базового класса «валюта». Вы должны определить SANPROJ_EXPORTS в определениях препроцессора, чтобы использовать файл currency.hpp в качестве определения функций, экспортируемых вашей DLL.
Эта цель создаст файл .DLL и, возможно (в зависимости от конфигурации) файл .lib. Он также может генерировать другие файлы, такие как текстовое представление экспорта библиотеки (файл .DEF).

Затем вам нужно построить ваше приложение (вторая цель / проект):
требуемый заголовочный файл — это просто библиотека библиотеки для части #include. Просто убедитесь, что НЕ определено SANPROJ_EXPORTS, иначе компилятор попытается снова экспортировать символы, а не импортировать их.
Затем вам нужно добавить следующие настройки для компилятора и компоновщика:

  • Добавьте к пути включения каталог, содержащий заголовок .hpp.

  • Добавьте в библиотеки путь компоновщика (lib) каталога
    содержащий файл .lib

  • Скажите компоновщику, чтобы он также связывался с только что созданным .lib (добавьте полное имя вашего lib-файла, предполагая, что DLL называется «currency», возможно, это будет «currency.lib»).

Где и как добавить эти настройки, зависит от того, какой инструментарий / среда и компилятор вы используете.

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

Тот факт, что удаление части _dllimport приведет к созданию проекта, возможно, связан с тем, что компилятор нашел заголовок И реализовал функции, которые вы намеревались экспортировать, и статически встроил их все в исполняемый файл.

Предполагая, что вы не находитесь в «управляемом мире» .NET, и что мы говорим о Windows, есть еще несколько моментов, на которые стоит обратить внимание, если вы хотите распространять свою библиотеку, но это другой ответ.

4

Другие решения

Это особенно жалуется на конструктор и деструктор для USDCurrency класс, но ваш код не показывает эти методы как украшенные SANPROJ_API,

И так как вы определяете USDCurrency конструктор в заголовке, когда вы удаляете dllimport из класса USDCurrency вы получаете реализацию, определенную в вашем текущем проекте, а не ссылку на ту, что определена в DLL.

2

Все остальные победили это, я тоже.

Когда компилятор добавляет библиотеки, компилятор делает именно это: компилирует. Линкер ссылки (до). Похоже, у вас проблема с настройкой компоновщика, и существует несколько способов ее решения.

Если у вас есть файл проекта с несколькими проектами (.sln), который содержит как ваш проект DLL, так и ваш проект EXE, вы можете установить явную зависимость, задав для проекта DLL «ссылку» на проект EXE в проекте EXE. В этом случае убедитесь, что «Зависимости библиотеки ссылок» помечены как «true».

Конфигурация References на самом деле является дополнением .NET, начиная с VS2005, хотя она все еще работает и для стандартных проектов C / C ++. Вы можете пропустить это и настроить библиотеку импорта для неявной ссылки на параметры компоновщика / ввода проекта EXE. Параметр «Зависимости библиотеки ссылок» также может быть помечен как true. Это также требует настройки зависимостей проекта решения (Зависимости сборки / проекта …). В вашем случае вы выбираете ваш EXE-проект как «зависит от ..» и проверяете проект DLL. Это гарантирует, что ваш EXE будет перекомпонован всякий раз, когда ваш проект DLL перестраивается и создается новая библиотека импорта.

По запросу доступны скриншоты всего этого. Становится привычкой устанавливать его после нескольких уходов на второй круг. На данный момент я уверен, что смогу сделать это с завязанными глазами.

2

Кажется, что нет решения этой проблемы. Я закончил тем, что отказался от использования dllimport в коде клиента и принять удар производительности. 🙁

1
По вопросам рекламы [email protected]