Static Variable Null In Method Call, But Initialized In Program
Asked Answered
A

4

10

I have a bit of a head-scratcher here that I wonder if someone may know the answer to.

The setup is basically this:

//in Visual Studio plug-in application
SpinUpProgramWithDebuggerAttached();

//in spun up program
void Start()
{
    StaticClass.StaticVariable = "I want to use this.";
    XmlSerializer.Deserialize(typeof(MyThingie), "xml");
}

class MyThingie : IXmlSerializable
{
     ReadXml()
     {
         //why the heck is this null?!?
         var thingIWantToUse = StaticClass.StaticVariable;
     }
}

The problem that has me pulling my hair out is that StaticClass.StaticVariable is null in the IXmlSerializable.ReadXml() method, even though it's called RIGHT AFTER the variable is set.

Of note is that breakpoints aren't hit and Debugger.Launch() is ignored in the precise spot the problem occurs.

Mysteriously, I determined through raising exceptions that the AppDomain.CurrentDomain.FriendlyName property is the same for the place the static variable is populated vs. null!

Why the heck is the static variable out of scope?!? What's going on?!? How can I share my variable?

EDIT:

I added a static constructor, per a suggestion in the responses, and had it do a Debug.WriteLine. I noticed it was called twice, even though all the code appears to be running in the same AppDomain. Here is what I see in the output window, which I'm hoping will be a useful clue:

Static constructor called at: 2015-01-26T13:18:03.2852782-07:00

...Loaded 'C:...\GAC_MSIL\System.Numerics\v4.0_4.0.0.0__b77a5c561934e089\System.Numerics.dll'...

...Loaded 'Microsoft.GeneratedCode'...

...Loaded 'C:...\GAC_MSIL\System.Xml.Linq\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.Linq.dll'....

...Loaded 'C:\USERS...\APPDATA\LOCAL\MICROSOFT\VISUALSTUDIO\12.0EXP\EXTENSIONS...SharePointAdapter.dll'. Symbols loaded.

...Loaded 'Microsoft.GeneratedCode'.

Static constructor called at: 2015-01-26T13:18:03.5196524-07:00

ADDITIONAL DETAIL:

Here is the actual code, since a couple of commenters thought it might help:

//this starts a process called "Emulator.exe"
var testDebugInfo = new VsDebugTargetInfo4
{
    fSendToOutputWindow = 1,
    dlo = (uint)DEBUG_LAUNCH_OPERATION.DLO_CreateProcess,
    bstrArg = "\"" + paramPath + "\"",
    bstrExe = EmulatorPath, 
    LaunchFlags = grfLaunch | (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_StopDebuggingOnEnd | (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_WaitForAttachComplete,
    dwDebugEngineCount = 0,
    guidLaunchDebugEngine = VSConstants.CLSID_ComPlusOnlyDebugEngine,
};

var debugger = Project.GetService(typeof(SVsShellDebugger)) as IVsDebugger4;
var targets = new[] { testDebugInfo };
var processInfos = new[] { new VsDebugTargetProcessInfo() };

debugger.LaunchDebugTargets4(1, targets, processInfos);

//this is in the emulator program that spins up
public partial class App : Application
{
    //***NOTE***: static constructors added to static classes.
    //Problem still occurs and output is as follows (with some load messages in between):
    //
    //MefInitializer static constructor called at: 2015-01-26T15:34:19.8696427-07:00
    //ContainerSingleton static constructor called at: 2015-01-26T15:34:21.0609845-07:00. Type: SystemTypes.ContainerSingleton, SystemTypes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=...
    //ContainerSingleton static constructor called at: 2015-01-26T15:34:21.3399330-07:00. Type: SystemTypes.ContainerSingleton, SystemTypes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=...

    protected override void OnStartup(StartupEventArgs e)
    {
         //...

         //initializes a MEF container singleton (stored as static variable)
         MefInitilizer.Run(); 

         //here's where it blows up. the important details are that 
         //FullSelection implements IXmlSerializable, and its implemention
         //ends up referencing the MEF container singleton, which ends up
         //null, even though it was initialized in the previous line.
         //NOTE: the approach works perfectly under a different context
         //so the problem is not the code itself, per se, but a problem
         //with the code in the environment it's running in.
         var systems = XmlSerialization.FromXml<List<FullSelection>>(systemsXml);
    }
 }

public static class MefInitilizer
{
    static MefInitilizer() { Debug.WriteLine("MefInitializer static constructor called at: " + DateTime.Now.ToString("o")); }

    public static void Run()
    {
        var catalog = new AggregateCatalog();
        //this directory should have all the defaults
        var dirCatalog = new DirectoryCatalog(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
        //add system type plug-ins, too
        catalog.Catalogs.Add(dirCatalog);

        var container = new CompositionContainer(catalog);
        ContainerSingleton.Initialize(container);
    }
}

public class ContainerSingleton
{
    static ContainerSingleton() 
    {
        Debug.WriteLine("ContainerSingleton static constructor called at: " + DateTime.Now.ToString("o") + ". Type: " + typeof(ContainerSingleton).AssemblyQualifiedName);
    }
    private static CompositionContainer compositionContainer;

    public static CompositionContainer ContainerInstance
    {
        get 
        {
            if (compositionContainer == null)
            {
                var appDomainName = AppDomain.CurrentDomain.FriendlyName;
                throw new Exception("Composition container is null and must be initialized through the ContainerSingleton.Initialize()" + appDomainName);
            }
            return compositionContainer; 
        }
    }

    public static void Initialize(CompositionContainer container)
    {
        compositionContainer = container;
    }
}
Arnaud answered 24/1, 2015 at 1:59 Comment(20)
We can't answer this with the given information. This does not display your situation so at best we can give you a "yup, looks weird" response. Show the chain of method calls and how these snippets relate to eachother.Falsetto
Either Visual Studio or WinDbg should give you at least call stack when exception is thrown. With amount of information you've provided this is not answerable... Note that likely there are ways to avoid static fields (but it should be separate question).Orphanage
If it is the same AppDomain and static constructor called twice it means you actually have 2 types - dump detailed information about type itself (typof(StaticClass).FullName may be enough) to see if it is the case.Orphanage
Is the static class in the same namespace and project? In what context is it working?Refugio
You could try using dependency injection and pass around exactly the instance you need at any time. You have very tight couping in your code according to your comments, and personally i would avoid static classes holding any state, since it's error prone and hard to test.Refugio
@Alexei, the Debug.Write() statement that shows up in the output is in a static constructor of my static class. That's how I know it's the same type.Arnaud
@Refugio - the static class basically encapsulates a DI container. I need the static class so that I CAN do dependency injection/service location. The static class is in a separate project, because it's shared between projects. The context it works is when it's used inside of the Visual Studio extension vs. the app that the Visual Studio extension launches.Arnaud
Ok, it's always a bit abstract when you don't have the whole picture. Have you tried using a lock?Refugio
"That's how I know it's the same type." - the only thing you know for sure in this case is the fact type(s) that generated the message come from the same source... Unfortunately just text message is not enough to confirm that there is no multiple types created from the same source for whatever reason...Orphanage
What happens if you call ContainerSingleton.ContainerInstance method after Initializing it, right after MefInitializer.Run()??Refugio
@Refugio - I haven't tried to lock, but the initialization code and consumption of the result occur synchronously and sequentially on the same thread, so I doubt it would help. If I end up calling ContainerSingleton.ContainerInstance right after initialization it all looks good and initialized.Arnaud
@Alexei - I see what you're saying and that seemed like a promising lead. I was hoping that something was dynamically generating a copy of the type or something, but when I printed out typeof(ContainerSingleton).AssemblyQualifiedName, the two types were identical on down to their assembly.Arnaud
@Arnaud Well then, a lock wouldn't be of much help. See my test run on a simplified version of your code, where i've tried to mock the class calling your ContainerSingleton to replicate your error in some way.Refugio
ok - at least you can scratch this one out. Looks OakNinja have much better suggestion.Orphanage
One thing just struck me - is the FullSelection class in a separate project? Do you have proper references set up to all of your projects? Could it be that FullSelection is referencing an instance of ContainerSingleton in your other project, which naturally is null?Refugio
FullSelection IS in a separate project and does reference ContainerSingleton; it's the class that's blowing up when it tries to use ContainerSingleton. However, the idea is that the singleton is initialized before FullSelection tries to use it. The good news is that I may have a workaround, even though I still don't understand what's going wrong.Arnaud
@VMAtm - Thanks for the feedback, but that might be a red herring since the post is about Java and its framework.Arnaud
No worries, @VMAtm, at this point I appreciate about any idea someone can throw out. :-DArnaud
@Arnaud Ok! What goes where is obvious to you but to me your solution structure is a bit abstract ;) It wasn't stated if FullSelection belonged to the same project or not. Could you describe your solution structure in basic? Just which of these classes belong to what project and the structure for the working implementation as well.Refugio
If your failing code is called twice and you only initiated it once, then you need to find what's calling it the second time and why. Check the full stack trace in the second case (including not only your code but also Microsoft/VS method frames).Tristich
A
0

Thanks to all who offered suggestions! I never did figure out exactly what was going on (and will continue to investigate/post updates if I ever do), but I did end up coming up with a workaround.

A few suggested that initialization of the static class be internalized, but due to the nature of the actual problem, initialization logic had to be externalized (I was basically loading a DI/service location container whose composition varied from environment to environment).

Also, I suspect it wouldn't have helped, since I could observe that the static constructor was called twice (thus, whatever initialization logic there was would just be called twice, which didn't directly address the problem).

However, the suggestion got me on the right track.

In my case, none of the services I loaded needed to be stateful, so it didn't really matter that the initialization happened twice aside from the performance hit.

Therefore, I simply had the static class check if the MEF container was loaded, and if it wasn't I'd read a configuration file which specified a class that handled initialization.

By doing so, I could still vary the composition of the MEF container from environment to environment, which is currently working pretty well, even if it's not an ideal solution.

I'd like to split the bounty between all who helped, but since that doesn't appear to be possible, I will probably reward OakNinja since he was a hero in spitting out as many good ideas as could realistically be expected given the information I provided. Thanks again!

Arnaud answered 2/2, 2015 at 23:2 Comment(0)
R
2

Bear in mind I've just copied your code to try to replicate your problem. When running this code, I get a NullReferenceException on Debug.Write, AnotherClass hasn't properly initialized before the call is resolved.

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            MefInitilizer.Run();
            Debug.Write(AnotherClass.Test);
        }
    }

    public class AnotherClass
    {
        public static String Test = ContainerSingleton.ContainerInstance;
    }

    public static class MefInitilizer
    {
        public static void Run()
        {
            ContainerSingleton.Initialize("A string");
        }
    }

    public class ContainerSingleton
    {
        private static String compositionContainer;

        public static String ContainerInstance
        {
            get
            {
                if (compositionContainer != null) return compositionContainer;

                var appDomainName = AppDomain.CurrentDomain.FriendlyName;
                throw new Exception("Composition container is null and must be initialized through the ContainerSingleton.Initialize()" + appDomainName);
            }
        }

        public static void Initialize(String container)
        {
            compositionContainer = container;
        }
    }


}

However, when I add static constructors to all classes with static fields it works as expected:

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            MefInitilizer.Run();

            Debug.Write(AnotherClass.Test);
        }
    }

    public class AnotherClass
    {
        static AnotherClass()
        {

        }

        public static String Test = ContainerSingleton.ContainerInstance;
    }

    public static class MefInitilizer
    {
        static MefInitilizer()
        {

        }
        public static void Run()
        {

            ContainerSingleton.Initialize("A string");
        }
    }

    public class ContainerSingleton
    {
        static ContainerSingleton()
        {

        }
        private static String compositionContainer;

        public static String ContainerInstance
        {
            get
            {
                if (compositionContainer != null) return compositionContainer;

                var appDomainName = AppDomain.CurrentDomain.FriendlyName;
                throw new Exception("Composition container is null and must be initialized through the ContainerSingleton.Initialize()" + appDomainName);
            }
        }

        public static void Initialize(String container)
        {
            compositionContainer = container;
        }
    }


}

I'd say this could definitely be a BeforeFieldInit problem.

Refugio answered 26/1, 2015 at 22:12 Comment(9)
Excellent lead and example, but unfortunately it's STILL not working! I added the static constructors I'm using at the Debug output to my OP.Arnaud
But how about the class calling the ContainerSingleton.ContainerInstance? It might not be the ContainerSingletonclass that is the problem even though it seems like it.Refugio
The class calling it isn't actually static. It's a XAML code-behind (listed as "App" in the sample code).Arnaud
Would you like to share your source in full somewhere? It seems like we're missing something.Refugio
Maybe it has to do with the entry point of the application. You could try to explicitly define the application entry point. See the answers to this question. #6157050Refugio
that sounds like another promising lead! Right now I have a functional workaround that I'm in the process of implementing, but I'll circle back within days to check that solution out!Arnaud
Thanks for all your help, OakNinja! You stuck it out with me the longest and had many of the best guesses for what was going on so enjoy the bounty!Arnaud
Thanks Colin! But most importantly, is everything working as expected now?Refugio
Working? Yes. As excited expected? Not exactly. I ended up specifying the "initializer" in config, then spinning it up at runtime. This, the initiaization code can vary by environment/situation, but initialization still happens twice in the weird case.Arnaud
D
2

As I understood, your code is an plug-in for a Visual Studio, and the main problem of your application is that your class is being instantiated twice, once for a normal AppDomain, and once for some other reason you can't really find out.

First of all, I see here a potential sandboxing from a Visual studio - it wants to test your code in various sets of rights to ensure your code won't harm any other parts of the Visual Studio or end user work. In this case your code could be loaded into another AppDomain, without some rights (You can find a good article at the MSDN), so you can understand why is your code called twice per application.

Second, I want to point out that you are misunderstanding the idea of static constructor and static method:

public static void Initialize(CompositionContainer container)
{
    compositionContainer = container;
}

is not the same as

public static ContainerSingleton()
{
    compositionContainer = container;
}

So, I suggest you to move the all initialization logic into a static container, something like this:

public class ContainerSingleton
{
    private static CompositionContainer compositionContainer;

    public static CompositionContainer ContainerInstance
    {
        get 
        {
            if (compositionContainer == null)
            {
                var appDomainName = AppDomain.CurrentDomain.FriendlyName;
                throw new Exception("Composition container is null and must be initialized through the ContainerSingleton.Initialize()" + appDomainName);
            }
            return compositionContainer; 
        }
    }

    public static ContainerSingleton()
    {
        var catalog = new AggregateCatalog();
        //this directory should have all the defaults
        var dirCatalog = new DirectoryCatalog(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
        //add system type plug-ins, too
        catalog.Catalogs.Add(dirCatalog);

        compositionContainer = new CompositionContainer(catalog);
    }
}

Second approach: I want to point out that the pattern you use for getting the singleton is outdated, try to use the Lazy<T> class, something like this:

public class ContainerSingleton
{
    private static Lazy<CompositionContainer> compositionContainer;

    public static CompositionContainer ContainerInstance
    {
        get 
        {
            return compositionContainer.Value;
        }
    }

    public static ContainerSingleton()
    {
        compositionContainer = new Lazy<CompositionContainer>(() => Initialize());
    }
    public static void Initialize()
    {
         // Full initialization logic here
    }
}

Also, you should remember that simply adding the empty static constructors isn't enough - you should move all assignments to it, so you should replace such code:

public class AnotherClass
{
    static AnotherClass()
    {

    }

    public static String Test = ContainerSingleton.ContainerInstance;
}

with this one:

public class AnotherClass
{
    static AnotherClass()
    {
        Test = ContainerSingleton.ContainerInstance;
    }

    public static String Test;
}

Update:

@Colin You can even use [LazyTask type][https://msdn.microsoft.com/en-us/magazine/dn683795.aspx] - simply pass a Func to your constructor, and it will be a thread-safe approach, see more in the article. The same Id of the AppDomain means nothing - the sandbox could run your code via AppDomain.ExecuteAssembly method (it's obsolete in 4.5, but still could be a possible variant) to see how it behaves in various set of permissions.

May be there is another technique for this in .NET 4.5, but can't find an article related right now.

Update 2:

As I can see in your code, you are reading some information from disk. Try to add a Code Access Security rule for this to see, if your code is being ran under restricted permissions, like this:

FileIOPermission f2 = new FileIOPermission(FileIOPermissionAccess.Read, Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
//f2.AddPathList(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, "C:\\example\\out.txt");
try
{
    f2.Demand();
}
catch (SecurityException s)
{
    Console.WriteLine(s.Message);
}

More about FileIOPermission Class on MSDN.

Directions answered 26/1, 2015 at 22:51 Comment(2)
Thanks for the reply! A couple notes: the static constructors have been in place for awhile (see comments on other replies and updated sample code). Initialization can't take place in the constructor in my case, because the initialization should be externalized as it needs to vary from environment to environment (the code is used in multiple contexts). On a related note, that means I'm not lazy-loading the field, though that's a neat trick.Arnaud
Clarification to my last comment: ContainerSingleton is reused in multiple contexts but MefInitializer and the App client are specific to the context in question. Also, I previously checked AppDomain.CurrentDomain.FriendlyName and AppDomain.CurrentDomain.Id and they were identical in the calling client and erroring code, FWIW.Arnaud
P
1

Try adding a static constructor to ContainerSingleton. I believe this is BeforeFieldInit raising its ugly head again.

Pounds answered 26/1, 2015 at 19:26 Comment(3)
Good lead, but it didn't work, unfortunately. However, it did result in a clue. I had the static constructor do a Debug.WriteLine() and I saw that it was called twice, the second time being after some generated code was loaded. I'll add detail to the OP.Arnaud
The fact that it executes twice means you have 2 app domains (maybe they have the same name?). In the article I linked, it quotes the c# spec as saying: The static constructor for a class executes at most once in a given application domainPounds
Yes, that's what's so puzzling; it should only execute once per app domain, but when I include AppDomain.CurrentDomain.FriendlyName or AppDomain.CurrentDomain.ID in the Exception details, everything is identical. It could very well be that there are indeed two AppDomains spun up with identical details, but I need to identify why and need to find a way around the problem once I do. That's where I'm stuck.Arnaud
A
0

Thanks to all who offered suggestions! I never did figure out exactly what was going on (and will continue to investigate/post updates if I ever do), but I did end up coming up with a workaround.

A few suggested that initialization of the static class be internalized, but due to the nature of the actual problem, initialization logic had to be externalized (I was basically loading a DI/service location container whose composition varied from environment to environment).

Also, I suspect it wouldn't have helped, since I could observe that the static constructor was called twice (thus, whatever initialization logic there was would just be called twice, which didn't directly address the problem).

However, the suggestion got me on the right track.

In my case, none of the services I loaded needed to be stateful, so it didn't really matter that the initialization happened twice aside from the performance hit.

Therefore, I simply had the static class check if the MEF container was loaded, and if it wasn't I'd read a configuration file which specified a class that handled initialization.

By doing so, I could still vary the composition of the MEF container from environment to environment, which is currently working pretty well, even if it's not an ideal solution.

I'd like to split the bounty between all who helped, but since that doesn't appear to be possible, I will probably reward OakNinja since he was a hero in spitting out as many good ideas as could realistically be expected given the information I provided. Thanks again!

Arnaud answered 2/2, 2015 at 23:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.