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

Reply via email to