tags:

views:

173

answers:

3

My coworker and I are debugging an issue in a WCF service he's working on where a string's length isn't being evaluated correctly. He is running this method to unit test a method in his WCF service:

// Unit test method
public void RemoveAppGroupTest()
{
    string addGroup = "TestGroup";
    string status = string.Empty;
    string message = string.Empty;

    appActiveDirectoryServicesClient.RemoveAppGroup("AOD", addGroup, ref status, ref message);
}


// Inside the WCF service
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public void RemoveAppGroup(string AppName, string GroupName, ref string Status, ref string Message)
{
    string accessOnDemandDomain = "MyDomain";

    RemoveAppGroupFromDomain(AppName, accessOnDemandDomain, GroupName, ref Status, ref Message);
}

public AppActiveDirectoryDomain(string AppName, string DomainName)
{
    if (string.IsNullOrEmpty(AppName))
    {
        throw new ArgumentNullException("AppName", "You must specify an application name");
    }
}

We tried to step into the .NET source code to see what value string.IsNullOrEmpty was receiving, but the IDE printed this message when we attempted to evaluate the variable: 'Cannot obtain value of local or argument 'value' as it is not available at this instruction pointer, possibly because it has been optimized away.' (None of the projects involved have optimizations enabled). So, we decided to try explicitly setting the value of the variable inside the method itself, immediately before the length check -- but that didn't help.

// Lets try this again.
public AppActiveDirectoryDomain(string AppName, string DomainName)
{
    // Explicitly set the value for testing purposes.
    AppName = "AOD";

    if (AppName == null)
    {
        throw new ArgumentNullException("AppName", "You must specify an application name");
    }

    if (AppName.Length == 0)
    {
        // This exception gets thrown, even though it obviously isn't a zero length string.
        throw new ArgumentNullException("AppName", "You must specify an application name");
    }
}

We're really pulling our hair out on this one. Has anyone else experienced behavior like this? Any tips on debugging it?


Here's the MSIL for the AppActiveDirectoryDomain object, where the behavior is occuring:

.method public hidebysig specialname rtspecialname instance void .ctor(string AppName, string DomainName) cil managed
{
.maxstack 5
.locals init (
    [0] class [System]System.Net.NetworkCredential ldapCredentials,
    [1] string[] creds,
    [2] string userName,
    [3] class [mscorlib]System.ArgumentNullException exc,
    [4] class [System.DirectoryServices]System.DirectoryServices.ActiveDirectory.DirectoryContext directoryContext,
    [5] class [System.DirectoryServices]System.DirectoryServices.ActiveDirectory.Domain domain,
    [6] class [System.DirectoryServices.Protocols]System.DirectoryServices.Protocols.LdapException V_6,
    [7] class [mscorlib]System.Exception V_7,
    [8] bool CS$4$0000,
    [9] char[] CS$0$0001,
    [10] string[] CS$0$0002)
L_0000: ldarg.0 
L_0001: ldsfld string [mscorlib]System.String::Empty
L_0006: stfld string MyNamespace.MyClass.AppActiveDirectoryDomain::appOU
L_000b: ldarg.0 
L_000c: call instance void [mscorlib]System.Object::.ctor()
L_0011: nop 
L_0012: nop 
L_0013: ldstr "AOD"
L_0018: call bool [mscorlib]System.String::IsNullOrEmpty(string)
L_001d: ldc.i4.0 
L_001e: ceq 
L_0020: stloc.s CS$4$0000
L_0022: ldloc.s CS$4$0000
L_0024: brtrue.s L_0037
L_0026: nop 
L_0027: ldstr "AppName"
L_002c: ldstr "You must specify an application name"
L_0031: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string, string)
L_0036: throw

And the MSIL for the string.IsNullOrEmpty call:

.method public hidebysig static bool IsNullOrEmpty(string 'value') cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: brfalse.s L_000d
    L_0003: ldarg.0 
    L_0004: callvirt instance int32 System.String::get_Length()
    L_0009: ldc.i4.0 
    L_000a: ceq 
    L_000c: ret 
    L_000d: ldc.i4.1 
    L_000e: ret 
}

Edit:

Here is a screenshot of the variable in the 'Watch' window at the moment the ArgumentNullException is thrown: http://imgur.com/xQm4J.png

Also, a second screenshot showing the exception being thrown when checking the length of the string, after explicitly declaring it 5 lines above: http://imgur.com/lSrk9.png

Update #3: We tried changing the name of the local variable and it passes the null check and the length check, but fails when we call string.IsNullOrEmpty. See this screenshot: http://imgur.com/Z57AA.png.


Responses:

  • We don't use any tools that would modify the MSIL. We've performed a cleanup, and also manually deleted all files from the build directories, and forced a rebuild... same outcome.

  • The following statement evaluates as true and enters the if block: if (string.IsNullOrEmpty("AOD")) { /* */ }.

  • The constructor is called like so:

    try { using (AppActiveDirectoryDomain domain = new AppActiveDirectoryDomain(AppName, DomainName)) { } }

This is immediately within the WCF service method itself; AppName and DomainName are parameters to the call. Even bypassing these parameters and using new strings, we still get the errors.


+1  A: 

Unfortunately, your code examples don't show where and how that failing constructor is called, ie. where an object of type AppActiveDirectoryDomain is instantiated. (Not that it should matter, given the last code example you provided.)

That being said, I've come across similar unlogical issues in Visual Studio before, once when playing around with Code Contracts (which re-writes the CIL instructions emitted from the compiler) and another time for a random reason. I never found the true problem, I remember changing some code in an altogether different place and suddenly, the issue was resolved.

I suspect that sometimes, the debugger somehow gets out of sync with the actual program's state.

Some questions:

  • Do you use any tools that re-write the CIL (=MSIL) code after the compiler has run? (such as e.g. Code Contracts or PostSharp)

  • Could it be that your debugger symbol files or the program database is out-of-sync with the compiled code? Did you perform a project clean-up?

You could try this next:

  • Does the statement if ("AOD".Length == 0) throw new ArgumentNullException(...); only throw inside that particular constructor? Or does the issue persist if you move that statement to the caller function? Does it still persist if you move it e.g. to the Main() method?

  • Try your code with a different debugger (links to another question on StackOverflow about MSIL debuggers), if you have one available.

stakx
I added responses to my question.
Justin R.
+2  A: 

I have 2 suggestions

  1. Use ILDASM to take a look at the IL that is being generated, possibly post the IL here so the community can take a look

  2. Change the name of the argument 'AppName' to something very different 'xxxAppName' for example.

I know point 2 might seem pointless, but I have in the past come across seemingly unexplainable situations, only to find that something was not being compiled or there was a scope conflict, while the debugger shows what you expect. And, it won't hurt to try :)

Chris Taylor
We tried using a different name for the local variable, and it succeeds the manual null check, as well as the length check, but then fails in `string.IsNullOrEmpty`. See the third screenshot link I'm posting.
Justin R.
I posted the IL. It doesn't look fishy to either of us, but neither of us are IL experts.
Justin R.
@Justin, the IL looks fine... Can you write some messages to the debug window (Diagnostics.Debug.WriteLine). We see what the debugger is seeing, but this might help to see what the executing code is seeing.
Chris Taylor
A: 

This turned out to be a 64-bit issue. Or at least it’s only an issue running 64-bit. In IIS 7.0 on a 64-bit OS, all websites are hosted in 64-bit processes by default. If we set the service to be hosted in a 32-bit process, the issue goes away.

Justin R.