2017-01-04 4 views
2

Я попытался загрузить динамическую копию C++ динамически, сначала я загрузил dll с помощью функции «LoadLibrary», и он правильно обрабатывает ее. После этого я попытался получить функцию-указатель функции DLL-файла с помощью «GetProcAddress», он возвращает NULL. Пожалуйста, найдите мой код DLL и код приложения для тестирования и сообщите мне, где в коде происходит неправильное.Функция GetProcAddress, возвращающая NULL

dummy2.h

namespace newer 
{ 
    class dllclass 
    { 
    public: 
     static __declspec(dllexport) int run(int a,int b); 
    }; 
} 

dummy2.cpp

#include <iostream> 
using namespace std; 

#include "dummy2.h" 

namespace newer 
{ 
    int dllclass::run(int a,int b) 
    { 
    return a+b; 
    } 
} 

dummy1.cpp

#include "stdafx.h" 
#include <windows.h> 

#include <iostream> 
using namespace std; 
typedef int (*Addition)(int,int); 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    Addition add; 
    HINSTANCE hDLL; 
    hDLL = LoadLibrary(TEXT("Dummy2.dll")); 

    add = (Addition)GetProcAddress(hDLL, "run"); 

    getchar(); 
    return 0; 
} 

см выше код и наставит меня.

+2

Вы, вероятно, нужно 'GetProcAddress (hDLL "dllclass :: Run"); 'или' GetProcAddress (hDLL, "newer :: dllclass :: run"); '? –

+1

GetProcAddress должен знать ** точное ** имя, которое экспортируется, включая обложку и любые специальные символы, дефисы, вопросительные знаки, @ символы и т. Д. Таким образом, «Run» - это не то же самое, что «run», который не является таким же, как "запустить @ 4". Вам нужно загрузить DLL в нечто вроде Dependency Walker или другого инструмента, чтобы узнать, что такое * точное имя, и это имя, которое вы должны использовать в GetProcAddress. – PaulMcKenzie

+1

На самом деле это нечто более похожее на «__imp_? Run @ dllclass @ newer @@ SAHHH @ Z» – axalis

ответ

4

Это потому, что имя искажено (т. Е. Имя функции не «работает», а что-то другое).

Ваш код будет работать (для MSVC 2013, где я тестировал):

add = (Addition)GetProcAddress(hDLL, "[email protected]@[email protected]@[email protected]"); 
cout << add(1, 2) << endl; 

В общем, если вы хотите загрузить класс с помощью плагина, ваш лучший выстрел использовать виртуальный интерфейс. Пример:

//dummy2.h 
namespace newer 
{ 
    class dllclass_interface 
    { 
    public: 
     virtual int run(int a,int b) = 0; 
}; 

} 

extern "C" __declspec(dllexport) newer::dllclass_interface* getDllClass(); 

//dummy2.cpp 
#include <iostream> 
using namespace std; 

#include "dummy2.h" 

namespace newer 
{ 
    class dllclass: public dllclass_interface 
    { 
    public: 
     virtual int run(int a,int b); 
}; 

    int dllclass::run(int a,int b) 
    { 
    return a+b; 
    } 
} 

extern "C" newer::dllclass_interface* getDllClass() 
{ 
    static newer::dllclass instance; 
    return &instance; 
} 

typedef newer::dllclass_interface* (*GetClassFunc)(); 

GetClassFunc getClassFunc = (GetClassFunc)GetProcAddress(hDLL, "getDllClass"); 

newer::dllclass_interface* dllClass = getClassFunc(); 
cout << dllClass->run(a, b) << endl; 
+0

Для меня также отлично работает, но что это такое? run @ dllclass @ newer @@ SAHHH @ Z" – sivanesan1

+0

Это фактическое имя 'never :: dllclass :: run() 'как экспортируется из DLL. Учтите, что у вас будет другой класс 'dllclass2', который также предоставит' run() 'в той же DLL или другой' run() 'override внутри того же класса - как бы экспортированное имя различалось между ними? Поэтому название mangling (украшение) находится на месте. Вы можете видеть, что имя содержит также пространство имен и имя класса, а остальные символы находятся в соответствии с параметрами для различения различных перегрузок. – axalis

+0

Обратите внимание, что имя будет выглядеть совершенно иначе, если экспортируется другим компилятором (GCC, Borland и т. Д.). Поэтому виртуальный интерфейс работает лучше (обратите внимание, что существует только один «extern» C «экспортированный аксессуар для класса intance и имя не искажено - с« extern »C» «он такой же простой для всех компиляторов, поэтому вы можете использовать DLL даже между разными компиляторами). – axalis

1

Фактически, библиотеки DLL были введены обратно во времена C. С тех пор C++ вводит переименование функций (в зависимости от типов параметров) и нечто, называемое «искаженные имена», чтобы разрешить вызов функции ссылки с соответствующим именем. В стандарте C++ не указывается, как должно выглядеть это имя. Различные компиляторы реализовали встраивание типов параметров в имена по-разному.

C++ понимает эту проблему, и иногда необходимо иметь предсказуемое имя. Существует специальная конструкция в C++ для этого:

extern "C" 
{ 
    int run(int, int); 
} 

При указании имени функции в GetProcAddress оно должно быть именно так, как она была экспортирована из DLL. Вы можете просмотреть эти имена, используя специальные утилиты, такие как DependencyWalker.

+0

Название функции все еще может быть украшено, но не искажено. Способ получить чистое имя - это как развязать имя, так и указать фактическое имя в файле файла определения модуля (DEF). – PaulMcKenzie

+0

Обратите внимание, что DependencyWalker устаревает и выдает множество предупреждений и ошибок о вещах, скомпилированных с более новыми версиями VS. – rubenvb

+0

Если вы используете Dependency Walker для получения только экспортированных имен функций, он работает правильно. Другое использование может быть «устаревшим», но для получения списка экспортируемых имен не так много. – PaulMcKenzie