Can't use a C++ class from a DLL

Denis Shelomovskij verylonglogin.reg at gmail.com
Mon Oct 29 05:11:20 PDT 2012


28.10.2012 23:52, Artie пишет:
> I have a DLL with a C++ class and a factory function that creates it.
> The aim is to load the DLL, get an instance of the class and use it.
>
> The interface of the DLL is as follows:
> -----------------
> class IBank
> {
>      public:
>          virtual const char* APIENTRY getLastError() = 0;
>          virtual const char* APIENTRY getDetail(char* detail) = 0;
>          virtual const bool APIENTRY deposit(unsigned long number,
> double amount) = 0;
>          virtual const bool APIENTRY withdraw(unsigned long number,
> double amount) = 0;
>          virtual const double APIENTRY getBalance(unsigned long number)
> = 0;
>          virtual const bool APIENTRY transfer(unsigned long numberFrom,
> IBank* bankTo, unsigned long numberTo, double amount) = 0;
>          virtual const bool APIENTRY transferAccept(IBank* bankFrom,
> unsigned long numberTo, double amount) = 0;
> };
> -----------------
>
> I've followed the instructions given at dlang.org to interface to C/C++
> code but got no success. If I use extern(C++) at the place in D code
> where extern declaration is required I get an access violation when
> calling any method. On the other hand, if I use extern(Windows, C or
> Pascal) I can call a method successfully, except that I get wrong return
> value.
>
> The D interface is declared as follows:
> -----------------
> extern (Windows) interface IBank
> {
>      const char* getLastError();
>      const char* getDetail(char* detail);
>      const bool deposit(uint number, double amount);
>      const bool withdraw(uint number, double amount);
>      const double getBalance(uint number);
>      const bool transfer(uint numberFrom, IBank* bankTo, uint numberTo,
> double amount);
>      const bool transferAccept(IBank* bankFrom, uint numberTo, double
> amount);
> }
>
> export extern (C) IBank Get();
> -----------------
>
>
> And the main program in D that uses the DLL:
> -----------------
> module main;
>
> import std.stdio;
> import core.runtime;
> import core.sys.windows.windows;
> import std.string;
> import std.conv;
>
> import ibank;
>
> int main()
> {
>      alias extern(C) IBank function() getBankInstance;
>      FARPROC pDllFunctionVBank, pDllFunctionSberbank;
>
>      // Load DLL file
>      void* handleVBank = Runtime.loadLibrary("vbank.dll");
>      void* handleSberbank = Runtime.loadLibrary("sberbank.dll");
>
>      if ( (handleVBank is null) || (handleSberbank is null) )
>      {
>          writeln("Couldn't find necessary DLL files");
>          return 1;
>      }
>
>      getBankInstance get1 = cast(getBankInstance)
> GetProcAddress(handleVBank, "Get".toStringz);
>      getBankInstance get2 = cast(getBankInstance)
> GetProcAddress(handleSberbank, "Get".toStringz);
>
>      if ( get1 is null || get2 is null )
>      {
>          writeln("Couldn't load factory functions");
>          return 2;
>      }
>
>      getBankInstance get;
>      IBank vbank = (*get1)();
>      IBank sberbank = get2();
>
>
>      uint sbnum = 100500;
>      uint vbnum = 128500;
>
>      writeln("You have an account in Sberbank (100500)");
>      auto balance = sberbank.getBalance(sbnum);
>      writefln("getBalance(%d) = %s", sbnum, balance);
>      bool res = sberbank.withdraw(sbnum, 500.0);
>      writefln("withdraw(%d, %f) = %s", sbnum, 500.0, res);
>      writeln("You got it!");
> ...
> -----------------
>
> The output I get is (in case I use extern (Windows, C or Pascal)):
> -----------------
> You have an account in Sberbank (100500)
> getBalance(100500) = -nan
> got into GenericBank::getBalance() // this is an output from a method
> called inside the DLL
> account number = 100500 // inside the DLL
> balance is 1100 // inside the DLL
> withdraw(100500, 500.000000) = false
> You got it!
> -----------------

First, to interact with C++ `interface` you need:
---
extern(C++) interface Ixxx
{
     ...
}
---

Your `IBank` C++ functions are declared as `APIENTRY` which is almost 
definitely defined as `__stdcall`. So the correct interface declaration is:
---
extern(C++) interface IBank
{
     extern(Windows) const char* getLastError();
     ...
}
---

As all your functions are `APIENTRY`, write `extern(Windows):` before 
them. And use `c_ulong` as analogue of `unsigned long`. So full correct 
`IBank` interface declaration here:
---
import core.stdc.config: c_ulong;

extern(C++) interface IBank
{
extern(Windows):
     const char* getLastError();
     const char* getDetail(char* detail);
     bool deposit(c_ulong number, double amount);
     bool withdraw(c_ulong number, double amount);
     double getBalance(c_ulong number);
     bool transfer(c_ulong numberFrom, IBank* bankTo, c_ulong numberTo, 
double amount);
     bool transferAccept(IBank* bankFrom, c_ulong numberTo, double amount);
};
---

-- 
Денис В. Шеломовский
Denis V. Shelomovskij


More information about the Digitalmars-d mailing list