0
votes

I try to create a DLL in Embarcadero C++ Builder XE3, and use it in a test-project in the same environment.

I take example on a tutorial which code does not give a good result for me (!) : http://docwiki.embarcadero.com/RADStudio/XE3/en/Tutorial:_Using_Dynamic_Linked_Libraries_in_C%2B%2BBuilder_Applications

Here is the content of my DLL :

BaseAuth.h file :

#ifndef   BaseAuthH
#define   BaseAuthH

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

Auth.h file :

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

Auth.cpp file :

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

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

Now in the test application I have :

  • the same BaseAuth.h file

  • a form with a Button :

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

The result is :

the pointer mpAuth has an adress.

But its methods have no adress, and when I call a simple method such as "void TestMessage()", it raises an access violation.

=> It first seemed to be a question of string compatibility (but between "C++ Builder XE3" and "C++ Builder XE3" I would expect the same string format to be used ?!) : Error calling DLL with Unicode Delphi

=> I found a similar issue but with C++ DLL into Delphi, not C++ DLL into C++ ... : Call C++ DLL in Delphi app

=> I tried using "HMODULE" instead of "HINSTANCE load;" : same result.

=> I tried without success using

mpAuth = static_cast<AuthParent*>(myFunc);

instead of :

mpAuth = (AuthParent*)myFunc;

=> I also tried replacing "__stdcall" by "__cdecl" or "" (removing) : the libray loads but GetProcAdress returns NULL.

=> What am I doing wrong in attempting to call the DLL's method "TestMessage()" ?

1
Dependency Walker says "Error: At least one module has an unresolved import due to a missing export function in an implicitly dependent module. Error: Modules with different CPU types were found." because "IESHIMS.dll can't be found, and because all DLL are in x64 except my DLL for which I have chosen "Win32" as "Plates-formes cible" ("Target platforms") in Embarcadero.Arnaud
I compile the DLL in Embarcadero for "Win32" platform and in "Release", and as Dependency Walker says DLL included in my DLL are in Win64 I turned the DLL in Win64-Release and Dependency ; so this "solved" the "different CPU types" issue but still Dependency Walker says some dll are missing ; how can I incorporate them in my DLL ?Arnaud

1 Answers

0
votes

To correctly bind function from dll you should give it full definition, including calling convention, arguments, return type and __dllexport/__dllimport modifier. The easiest way to do it - using typedef so instead of typing (in Test_DLLAuthOrga.cpp)

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

use

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

If you are using __cdecl convention you should also add underscore to the target function name

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

you can also explicitly define AuthParent* as a return type for your factory function to get rid of uneccessary casts to void* and back to AuthParent* (in File1.cpp and Test_DLLAuthOrga.cpp) so, the final code snippet would look like this:

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