0

This is a bit of a weird issue as I cannot reproduce it. I have two customers capable of reproducing this issue who have sent me WER logs.

I have a C# application which queues work items and executes them in the background. In a very small percentage of machines, immediately upon trying to execute the first line of the worker thread, it immediately throws an NPE.

Ausnahmeinformationen: System.NullReferenceException
   bei magicsim.SimcPreloaderData+<>c__DisplayClass16_0.<LoadArmoryData>b__0(System.Object)
   bei System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(System.Object)
   bei System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   bei System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   bei System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   bei System.Threading.ThreadPoolWorkQueue.Dispatch()
   bei System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

The WER information is very interesting as well.

Problemsignatur:
P1: magicsim.exe
P2: 2.2.0.2
P3: 5b7df04c
P4: magicsim
P5: 2.2.0.2
P6: 5b7df04c
P7: 29b
P8: fb
P9: System.NullReferenceException
P10:

So this is in method 29b, trying to hit IL line fb.

Using ILDASM I can validate the location of this problem signature matches the stack trace (SimcPreloaderData)

Method #2 (0600029b) 
    -------------------------------------------------------
        MethodName: <LoadArmoryData>b__0 (0600029B)
        Flags     : [Assem] [HideBySig] [ReuseSlot]  (00000083)
        RVA       : 0x0000d338
        ImplFlags : [IL] [Managed]  (00000000)
        CallCnvntn: [DEFAULT]
        hasThis 
        ReturnType: Void
        1 Arguments
            Argument #1:  Object
        1 Parameters
            (1) ParamToken : (0800020f) Name : _ flags: [none] (00000000)

In ILSpy, I can see the following structure of the above method:

.method public hidebysig 
    instance void LoadArmoryData (
        string simcString
    ) cil managed 
{
    // Method begins at RVA 0x7b4d
    // Code size 48 (0x30)
    .maxstack 8

    IL_0000: newobj instance void magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::.ctor()
    IL_0005: dup
    IL_0006: ldarg.0
    IL_0007: stfld class magicsim.SimcPreloaderData magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::'<>4__this'
    IL_000c: dup
    IL_000d: ldarg.1
    IL_000e: stfld string magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::simcString
    IL_0013: ldarg.0
    IL_0014: ldstr "Acquiring SimC Executable"
    IL_0019: call instance void magicsim.SimcPreloaderData::set_Label(string)
    IL_001e: ldftn instance void magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::'<LoadArmoryData>b__0'(object)
    IL_0024: newobj instance void [mscorlib]System.Threading.WaitCallback::.ctor(object, native int)
    IL_0029: call bool [mscorlib]System.Threading.ThreadPool::QueueUserWorkItem(class [mscorlib]System.Threading.WaitCallback)
    IL_002e: pop
    IL_002f: ret
} // end of method SimcPreloaderData::LoadArmoryData

There is clearly no IL_00fb command to execute, hence the NPE and lack of a reference of where specifically inside LoadArmoryData this was happening (besides the fact that it simply was). The only thing I can surmise from the above is that for some reason, multithreading is going errant and trying to access an instruction that does not exist.

I do not know of why this would happen and have ruled out antivirus and permissions interferences.

Edit: For further reference, the full method.

public void LoadArmoryData(String simcString)
{
    SimC simc;
    Label = "Acquiring SimC Executable";
    ThreadPool.QueueUserWorkItem((_) =>
    {
        try
        {
            simc = SimCManager.AcquireSimC();
        }
        catch (Exception e)
        {
            MessageBox.Show("Could not acquire SimC because of an Exception: " + e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            App.Current.Dispatcher.Invoke(() =>
            {
                PreloadingFailed(this, new EventArgs());
            });
            return;
        }
        Label = "Generating SimC Profile";
        Regex nameRegex = new Regex("(warrior|paladin|hunter|rogue|priest|deathknight|shaman|mage|warlock|monk|druid|demonhunter)+=\"?([^\r\n\"]+)\"?");
        String name = nameRegex.Match(simcString).Groups[2].Value;
        String text = simcString + "\nsave=./characters/" + name + ".simc";
        try
        {
            if (!Directory.Exists("characters"))
            {
                Directory.CreateDirectory("characters");
            }
            if (File.Exists("characters/" + name))
            {
                File.Delete("characters/" + name);
            }
            File.WriteAllText("characters/" + name + ".simc", text);
        }
        catch (UnauthorizedAccessException e)
        {
            MessageBox.Show("Could not write character profile due to a permissions issue. Please retry, running as Administrator." + e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            App.Current.Dispatcher.Invoke(() =>
            {
                PreloadingFailed(this, new EventArgs());
            });
            return;
        }
        if (simc.RunSim("characters/" + name + ".simc"))
        {
            Label = "SimC Profile Generated";
            charName = name;
            App.Current.Dispatcher.Invoke(() =>
            {
                PreloadingComplete(this, new EventArgs());
            });
        }
        else
        {
            Label = "Failed to Generate Profile";
            MessageBox.Show("Failed to generate profile. Please check your input and try again.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);

            App.Current.Dispatcher.Invoke(() =>
            {
                PreloadingFailed(this, new EventArgs());
            });
        }
    });
}

EDIT 2 (For completeness):

In fact, 29b refers to b__0, not LoadArmoryData.

If you look at the correct method in ILSpy you see the following sequence of commands which implies the clear issue and potential solution.

// string contents = simcString + "\nsave=./characters/" + value + ".simc";
IL_00ce: ldarg.0
IL_00cf: ldfld string magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::simcString
IL_00d4: ldstr "\nsave=./characters/"
IL_00d9: ldloc.0
IL_00da: ldstr ".simc"
IL_00df: call string [mscorlib]System.String::Concat(string, string, string, string)
IL_00e4: stloc.1
// File.WriteAllText("characters/" + value + ".simc", contents);
IL_00e5: ldstr "characters/"
IL_00ea: ldloc.0
IL_00eb: ldstr ".simc"
IL_00f0: call string [mscorlib]System.String::Concat(string, string, string)
IL_00f5: ldloc.1
IL_00f6: call void [mscorlib]System.IO.File::WriteAllText(string, string)
// if (simc.RunSim("characters/" + value + ".simc"))
IL_00fb: ldarg.0
IL_00fc: ldfld class magicsim.SimC magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::simc
IL_0101: ldstr "characters/"
IL_0106: ldloc.0
IL_0107: ldstr ".simc"
IL_010c: call string [mscorlib]System.String::Concat(string, string, string)
IL_0111: callvirt instance bool magicsim.SimC::RunSim(string)
// (no C# code)
IL_0116: brfalse.s IL_0163
3
  • @MickyD added for reference. Commented Aug 27, 2018 at 4:23
  • This is a wild guess, But... I'm wondering if the object representing the lambda is getting garbage collected because of very high memory pressure. I'm not sure about the lifetime characteristics for delegates like that. Maybe this comment will trigger someone who will say "hey, wait a minute, yeah" (or more likely "no, Flydog, that can't be it"). Commented Aug 27, 2018 at 4:44
  • Not sure if it's relevant but there's no need for simc to be declared outside the lambda. Commented Aug 27, 2018 at 5:06

1 Answer 1

2

I think the analysis in the question is flawed (e.g. the exception is coming from <LoadArmoryData>b__0(System.Object), which represent a lambda expression in LoadArmoryData whose signature expects an object parameter; but you are looking at the IL for LoadArmoryData, which is a method whose signature expects a string parameter).

This is just a straightforward NullReferenceException, probably coming from nameRegex.Match(simcString).Groups[2].Value; when the Regex doesn't match.

Sign up to request clarification or add additional context in comments.

1 Comment

You are correct, I realized this after posting this and was trying to confirm with the customer. If you look at <LoadArmoryData>b__0 in ILSpy you find that the IL_00fb is the start of if (simc.RunSim("characters/" + value + ".simc")) which, given the domain of the problem, simc is potentially null in this case. I have to confirm with the customer why this dependency is not being resolved correctly (and to also validate in case value is also null).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.