Shared static constructors from C# EXE

Thalamus via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Thu Feb 25 09:46:18 PST 2016


On Thursday, 25 February 2016 at 16:05:37 UTC, Benjamin Thaut 
wrote:
> On Thursday, 25 February 2016 at 14:42:14 UTC, Thalamus wrote:
>>> your entry point.
>>
>> Hi Guillaume,
>>
>> Thanks for responding so quickly! I had found that wiki page 
>> before and I'd been following the "DLLs with a C Interface" 
>> section closely. I had forgotten to add -shared when building 
>> the DLL, but the behavior didn't change when I added it. So, I 
>> added a call to Runtime.initialize() as the first line of the 
>> endpoint I'm exposing. (I also made sure that this was the 
>> only endpoint invoked and that it was only invoked once just 
>> to be cautious.) I can see Runtime.initialize() being called, 
>> but the Class A shared static constructor still is not called 
>> when run from the C# EXE.
>>
>> Do you have any other ideas?
>>
>> In the meantime, I'm working on putting together a minimal 
>> repro source, but the scenario is a bit complicated so there's 
>> a lot of details to whittle away.
>>
>> thanks!
>> Gene
>
> You shouldn't be calling Runtime.initialize() manually. Just do 
> the following in one of your source files:
>
> import core.sys.windows.dll;
> mixin SimpleDllMain;
>
> This will generate a DllMain that will correctly initialize and 
> deinitialize druntime.
>
> Kind Regards
> Benjamin Thaut

Thanks Benjamin. When I went to whittle this down to its barest 
essentials, though, the repro is pretty simple. It involves LIBs, 
but not Dlls, and it doesn't require anything but a single D EXE.

NOTE: if attempting to repro any of this, you must do a clean 
build after changing build.cmd or main.d before you'll see a 
change in behavior. I have the shared static ctors output files 
to make it really easy to see.

If you have Class A:

module ClassA;

import std.file;
import std.stdio;

export class ClassA
{
     shared static this()
     {
         File file = File(r"c:\A.txt", "w");
         file.writeln("Called A's shared static constructor."); 
file.flush();
         file.close();
     }
}

and you have Class B:

module ClassB;

import std.file;
import std.stdio;

export class ClassB
{
     shared static this()
     {
         File file = File(r"c:\B.txt", "w");
         file.writeln("Called B's shared static constructor."); 
file.flush();
         file.close();
     }
}

and you have main.d:

void main()
{
}

And you build it in one step into an EXE:

dmd -m64 -debug ClassA.d ClassB.d main.d -ofDriver.exe

Then you run Driver.exe, both A.txt and B.txt are created.

But, if you build it as a LIB and then link the LIB to the EXE:

dmd -c -lib -m64 -debug ClassA.d ClassB.d -ofInit.lib
dmd -m64 -debug Init.lib main.d -ofDriver.exe

When you run Driver.exe, neither are created. If you then add 
"import ClassA" to main.d and clean build, only A.txt will be 
created, or instead if you add "import ClassB", then only B.txt 
is created. Also, if either of these is included, Driver.exp and 
Driver.lib are emitted by the build, whereas otherwise they 
aren't.

It looks from this like a class in a linked LIB that is not 
directly imported will not have its shared static constructor 
called. Am I missing something obvious? :)

Long term I will need all this not only in separate LIBs but in 
separate DLLs. My scenario is roughly like this (-> indicate 
dependencies):

ClassA : IClass -> ClassManagement
ClassB : IClass -> ClassManagement
EntryPoint -> ClassManagement, ClassB, IClass

Then EntryPoint asks ClassManagement to give it an instance of 
ClassB's complement (ClassA). ClassManagement only knows anything 
about any of these classes via TypeInfo_Class object mappings, 
and it uses Object.factory to instantiate them. EntryPoint and 
its dependencies then work with that object via the IClass 
interface. I've gotten this to work in C# easily and C++ with 
some effort. (Class map population was, of course, very different 
for each, though.)

I can probably figure out a way to make this work for now, but if 
there's a way to ensure shared static ctors are run in this 
scenario without importing modules across separation boundaries, 
it would be a very good thing.

thanks!
Gene




More information about the Digitalmars-d-learn mailing list