Quiz of the day: Why does this not work?

Jarrett Billingsley jarrett.billingsley at gmail.com
Thu Nov 6 12:57:57 PST 2008


On Thu, Nov 6, 2008 at 3:14 PM, TomD <t_demmer at nospam.web.de> wrote:
> Hi,
> this is about dmd and DLLs.
>
> Given a simple object hierachy in myclasses.d:
> module myclasses;
> class base{ char[] toString(){ return "I am base";} }
> class c1: base{ char[] toString(){ return "I am c1";} }
> class c2: base{ char[] toString(){ return "I am c2";} }
>
> and a main file that first does some sanity checks, and then loads and
> calls a function in a DLL:
>
> mymain.d:
> import tango.sys.SharedLib;
>
> import myclasses;
>
> void main(){
>  base[] instances;
>  // populate instances
>  instances.length=3;
>  instances[0] = new base;
>  instances[1] = new c1;
>  instances[2] = new c2;
>  // no problem
>  assert( cast(c1) instances[1] !is null);
>  assert( cast(c2) instances[2] !is null);
>
>  SharedLib lib = SharedLib.load(`mydll.dll`);
>  assert( lib !is null);
>
>  extern(C) void function(base[]) my_c_check;
>
>  void*  ptr;
>  void** point;
>  ptr = lib.getSymbol("my_c_check");
>  point = cast(void** ) &my_c_check;
>  *point = ptr;
>
>  my_c_check( instances );
> }
>
> Finally, a DLL that is supposed to work on instances, mydll.d:
> import myclasses;
> import tango.sys.win32.Types;
> import tango.util.log.Trace;
>
> // The core DLL init code, taken from tango wiki.
> extern (C) bool  rt_init( void delegate( Exception ) dg = null );
> extern (C) bool  rt_term( void delegate( Exception ) dg = null );
>
> HINSTANCE g_hInst;
>
> extern (Windows) BOOL
> DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved){
>  switch (ulReason){
>  case DLL_PROCESS_ATTACH:
>    rt_init();
>    break;
>
>  case DLL_PROCESS_DETACH:
>    rt_term();
>    break;
>
>  case DLL_THREAD_ATTACH:
>  case DLL_THREAD_DETACH:
>    // Multiple threads not supported yet
>    return false;
>  }
>  g_hInst=hInstance;
>  return true;
> }
> // End of core DLL Init
>
> export extern(C) int my_c_check( base[] instances){
>  Trace.formatln("into my_c_check");
>  assert( instances.length ==3 );
>  Trace.formatln("length check OK");
>  assert( instances[0] !is null);
>  assert( instances[1] !is null);
>  assert( instances[2] !is null);
>  Trace.formatln("instances check OK");
>  Trace.formatln("instances[0] says: {}", instances[0].toString() );
>  Trace.formatln("instances[1] says: {}", instances[1].toString() );
>  Trace.formatln("instances[2] says: {}", instances[2].toString() );
>  Trace.formatln("instances[0] is: {}", instances[0].classinfo.name );
>  Trace.formatln("instances[1] is: {}", instances[1].classinfo.name );
>  Trace.formatln("instances[2] is: {}", instances[2].classinfo.name );
>  // Boom!
>  assert( cast(c1) instances[1] !is null);
>  assert( cast(c2) instances[2] !is null);
>
>  return 0;
> }
>
> This is a real show stopper for using D with/for dynamic libraries.
> Is there anything simple to fix this?
>
> Ciao
> Tom
>

DLLs are unusable.  It's not actually D's fault, it's the fault of
Microsoft using such a poorly-capable dynamic library loading system.
Basically, the problem with DLLs is that they cannot load symbols from
the "host" application at load-time.  This means that information
contained in the host is not available to the DLL unless the host
passes that data to the DLL after it has been loaded.  In the case of
D, there is type info embedded into your applications and libraries
that is required for things like class casting (as you've seen here)
to work correctly.  What happens currently is that when you build your
application and then you build the DLL, they each end up _with their
own copies of the same type info_, meaning that even though a "host"
c1 is the same thing as a DLL c1, they are in effect two different
classes as they have two different type infos.

The way around this?  Don't use DLLs.  Use DDLs.
http://www.dsource.org/projects/ddl



More information about the Digitalmars-d mailing list