Импорт библиотеки DLL Embarcadero C++ Builder XE3 в Embarcadero C++ Builder XE3

Я пытаюсь создать DLL в Embarcadero C++ Builder XE3 и использовать ее в тестовом проекте в той же среде.

Я беру пример с учебника, код которого не дает мне хорошего результата (!) : ="nofollow noreferrer">http://docwiki.embarcadero.com/RADStudio/XE3/en/Tutorial:_Using_Dynamic_Linked_Libraries_in_C%2B%2BBuilder_Applications

Вот содержимое моей DLL:

Файл BaseAuth.h:

#ifndef   BaseAuthH
#define   BaseAuthH

#include <System.hpp>
class TBaseAuth
{
public:
    virtual void TestMessage() = 0;
};
#endif // BaseAuthH

Файл Auth.h:

//---------------------------------------------------------------------------
#ifndef AuthH
#define AuthH
//---------------------------------------------------------------------------
#include "BaseAuth.h"
class TAuth : public TBaseAuth
{
public:
    TAuth();
    ~TAuth();   
    void TestMessage();
};
#endif

Файл Auth.cpp:

//---------------------------------------------------------------------------
#pragma hdrstop
#include "Auth.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
TAuth::TAuth()
{
}
TAuth::~TAuth()
{
}
void TAuth::TestMessage()
{
    MessageBox(0, "Test message", "Test", MB_OK);
}

и File1.cpp:

#pragma hdrstop
#pragma argsused

#include "Auth.h"
extern "C" __declspec(dllexport) void* __stdcall GetClassInstance()
{
    return static_cast<void*>(new TAuth());
}
extern "C" int _libmain(unsigned long reason)
{
    return 1;
}    

Теперь в тестовом приложении у меня есть:

  • тот же файл BaseAuth.h

  • форма с кнопкой:

Test_DLLAuthOrga.h:

#ifndef Test_DLLAuthOrgaH
#define Test_DLLAuthOrgaH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
#include "BaseAuth.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // Composants gérés par l'EDI
    TButton *Button2;
    void __fastcall Button2Click(TObject *Sender);
private:    // Déclarations utilisateur
    TBaseAuth *mpAuth;
public:     // Déclarations utilisateur
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Test_DLLAuthOrga.cpp:

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Test_DLLAuthOrga.h"
#include "BaseAuth.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
const wchar_t* library = L"DLLAuthOrga.dll";
extern "C" __declspec(dllimport) void* __stdcall GetClassInstance();
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
    HINSTANCE load;
    try
    { load = LoadLibrary(library); }
    catch(Exception &e)
    { ShowMessage(e.Message); }
    if (load)
    {
        ShowMessage("Library Loaded!");
        void *myFunc;
        myFunc = (void *)GetProcAddress(load, "GetClassInstance");
        mpAuth = (AuthParent*)myFunc;
    }
    else { ShowMessage("Library not loaded!"); }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    if (mpAuth == NULL) return;
    try { pRes = mpAuth->TestMessage(); }
    catch(Exception &e) { ShowMessage(e.Message); return; }
}

Результат:

указатель mpAuth имеет адрес.

Но его методы не имеют адреса, и когда я вызываю простой метод, такой как "void TestMessage()", он вызывает нарушение прав доступа.

=> Сначала казалось, что это вопрос совместимости строк (но между «C++ Builder XE3» и «C++ Builder XE3» я ожидал бы, что будет использоваться один и тот же формат строки?!): Ошибка вызова DLL с Unicode Delphi

=> Я обнаружил аналогичную проблему, но с C++ DLL в Delphi, а не с C++ DLL в C++...: Вызов C++ DLL в приложении Delphi

=> Я попытался использовать «HMODULE» вместо «HINSTANCE load»; : тот же результат.

=> Я безуспешно пытался использовать

mpAuth = static_cast<AuthParent*>(myFunc);

вместо :

mpAuth = (AuthParent*)myFunc;

=> Я также пытался заменить «__stdcall» на «__cdecl» или «» (удаление): библиотека загружается, но GetProcAdress возвращает NULL.

=> Что я делаю неправильно, пытаясь вызвать метод DLL "TestMessage()"?


person Arnaud    schedule 20.03.2013    source источник
comment
Зависимость Walker говорит об ошибке: по крайней мере один модуль имеет неразрешенный импорт из-за отсутствия функции экспорта в неявно зависимом модуле. Ошибка: Найдены модули с разными типами ЦП. потому что IESHIMS.dll не может быть найден, и потому что все DLL находятся в x64, кроме моей DLL, для которой я выбрал Win32 в качестве форм-пластин (целевых платформ) в Embarcadero.   -  person Arnaud    schedule 20.03.2013
comment
Я компилирую DLL в Embarcadero для платформы Win32 и в Release, и, как говорит Dependency Walker, DLL, включенные в мою DLL, находятся в Win64, я превратил DLL в Win64-Release and Dependency; так что это решило проблему с различными типами процессоров, но все же Dependency Walker говорит, что некоторые dll отсутствуют; как я могу включить их в свою DLL?   -  person Arnaud    schedule 20.03.2013


Ответы (1)


Чтобы правильно связать функцию из dll, вы должны дать ей полное определение, включая соглашение о вызовах, аргументы, тип возвращаемого значения и модификатор __dllexport/__dllimport. Самый простой способ сделать это - использовать typedef вместо того, чтобы печатать (в Test_DLLAuthOrga.cpp)

void *myFunc;
myFunc = (void *)GetProcAddress(load, "GetClassInstance");

использовать

typedef __declspec(dllimport) void (__stdcall *MyFuncPointerType)(void);
MyFuncPointerType myFunc;
myFunc = (MyFuncPointerType)GetProcAddress(load, "GetClassInstance");

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

typedef __declspec(dllimport) void (__cdecl *MyFuncPointerType)(void);
MyFuncPointerType myFunc;
myFunc = (MyFuncPointerType)GetProcAddress(load, "_GetClassInstance");

вы также можете явно определить AuthParent* как возвращаемый тип для вашей фабричной функции, чтобы избавиться от ненужных приведений к void* и обратно к AuthParent* (в File1.cpp и Test_DLLAuthOrga.cpp), поэтому окончательный фрагмент кода будет выглядеть так:

typedef __declspec(dllimport) AuthParent* (__cdecl *MyFuncPointerType)(void);
MyFuncPointerType myFunc;
myFunc = (MyFuncPointerType)GetProcAddress(load, "_GetClassInstance");
mpAuth = myFunc();
person Sergey Karpukhin    schedule 05.08.2013