Thread overview
instance of sub class created in a dll has wired behaviour when cast
Mar 23, 2009
liyu
Mar 23, 2009
liyu
Mar 23, 2009
davidl
March 23, 2009
I am not sure it's a bug of tango or dmd, so i post it here. The example code as below

//file common
module common;

abstract class DummyA {

}

abstract class DummyB : DummyA {

}

abstract class Factory {
	DummyB createDummy();
}

//file dll
module mydll;

import common;

import tango.sys.win32.Types;

import tango.io.Stdout;
import tango.stdc.stdio;

// The core DLL init code.
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:
		tango.stdc.stdio._fcloseallp = null;
		rt_term();
	    break;

	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	    // Multiple threads not supported yet
	    return false;
   }
   g_hInst=hInstance;
   return true;
}

class DummyC : DummyB {

}

class DllFactory : Factory {
	override DummyC createDummy() {
		return new DummyC;	} 

}

export extern(C) DllFactory create() {
	return new DllFactory;}

//file test
module test;

import common;

import tango.sys.SharedLib;
import tango.util.log.Trace;

// declaring our function pointer
typedef extern (C) Factory function() createFunc;
createFunc dllcreate;

void main() {
   if (auto lib = SharedLib.load(`mydll.dll`)) {
       Trace.formatln("Library successfully loaded");

       void* ptr = lib.getSymbol("create");
       if (ptr) {
           Trace.formatln("Symbol dllprint found. Address = 0x{:x}", ptr);

           // binding function address from DLL to our function pointer
           void **point = cast(void **)&dllcreate;
           *point = ptr;

           // using our function
           auto factory = dllcreate();
           auto dummy = factory.createDummy();

           Trace.formatln(dummy.classinfo.name);
           auto d2 = cast(DummyB)dummy;
           assert(d2 !is null); //ok

           DummyA a = dummy;
           Trace.formatln(a.classinfo.name);
           auto d3 = cast(DummyB)a;
           assert(d3 !is null);    //Assertion failure

       } else {
           Trace.formatln("Symbol dllprint not found");
       }

       lib.unload();
   } else {
       Trace.formatln("Could not load the library");
   }

   assert (0 == SharedLib.numLoadedLibs);
}

The result is:
Library successfully loaded
Symbol dllprint found. Address = 0x1000308c
mydll.DummyC
mydll.DummyC
tango.core.Exception.AssertException@test(35): Assertion failure

As you see, var a is a instance of DummyC which is a subclass of DummyB, but the cast is failure.


March 23, 2009
2009/3/22 liyu <yunwind@msn.com>:
> I am not sure it's a bug of tango or dmd, so i post it here. The example code as below
>
> ...
>
> The result is:
> Library successfully loaded
> Symbol dllprint found. Address = 0x1000308c
> mydll.DummyC
> mydll.DummyC
> tango.core.Exception.AssertException@test(35): Assertion failure
>
> As you see, var a is a instance of DummyC which is a subclass of DummyB, but the cast is failure.

This is, unfortunately, a limitation imposed by Windows DLLs.  DLLs cannot, by their nature, load symbols out of the host application that loads them.  As a result, the typeinfo of classes is duplicated, one copy in the host and one in each DLL.  Thus you end up with classes which _look_ the same, but casting them will inevitably fail.  There is no real robust solution while still using DLLs, and the problem only exists on Windows.

DDL attempts (and succeeds, quite nicely) in performing manual dynamic linking.  http://www.dsource.org/projects/ddl
March 23, 2009
"Jarrett Billingsley" <jarrett.billingsley@gmail.com> wrote in message news:mailman.986.1237779483.22690.digitalmars-d@puremagic.com...
> 2009/3/22 liyu <yunwind@msn.com>:
>> I am not sure it's a bug of tango or dmd, so i post it here. The example
>> code as below
>>
>> ...
>>
>> The result is:
>> Library successfully loaded
>> Symbol dllprint found. Address = 0x1000308c
>> mydll.DummyC
>> mydll.DummyC
>> tango.core.Exception.AssertException@test(35): Assertion failure
>>
>> As you see, var a is a instance of DummyC which is a subclass of DummyB, but
>> the cast is failure.
>
> This is, unfortunately, a limitation imposed by Windows DLLs.  DLLs
> cannot, by their nature, load symbols out of the host application that
> loads them.  As a result, the typeinfo of classes is duplicated, one
> copy in the host and one in each DLL.  Thus you end up with classes
> which _look_ the same, but casting them will inevitably fail.  There
> is no real robust solution while still using DLLs, and the problem
> only exists on Windows.
>
> DDL attempts (and succeeds, quite nicely) in performing manual dynamic
> linking.  http://www.dsource.org/projects/ddl

wow, I never known that before, thanks! 

March 23, 2009
在 Mon, 23 Mar 2009 11:37:56 +0800,Jarrett Billingsley <jarrett.billingsley@gmail.com> 写道:

> 2009/3/22 liyu <yunwind@msn.com>:
>> I am not sure it's a bug of tango or dmd, so i post it here. The example
>> code as below
>>
>> ...
>>
>> The result is:
>> Library successfully loaded
>> Symbol dllprint found. Address = 0x1000308c
>> mydll.DummyC
>> mydll.DummyC
>> tango.core.Exception.AssertException@test(35): Assertion failure
>>
>> As you see, var a is a instance of DummyC which is a subclass of DummyB, but
>> the cast is failure.
>
> This is, unfortunately, a limitation imposed by Windows DLLs.  DLLs
> cannot, by their nature, load symbols out of the host application that
> loads them.  As a result, the typeinfo of classes is duplicated, one
> copy in the host and one in each DLL.  Thus you end up with classes
> which _look_ the same, but casting them will inevitably fail.  There
> is no real robust solution while still using DLLs, and the problem
> only exists on Windows.
>
> DDL attempts (and succeeds, quite nicely) in performing manual dynamic
> linking.  http://www.dsource.org/projects/ddl

I think if the runtime typeinfo is modularized to a seperated dll, the problem should go away. Just on one think that's a must at the moment.