7

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.


Justin Rusbatch
  • 3,992
  • 2
  • 25
  • 43
  • 1
    If you put a breakpoint on the exception that gets thrown, and add `AppName` and `AppName.Length` to watches, what do they show? – Fyodor Soikin Apr 30 '10 at 17:40
  • They show 'AppName == "AOD"' and 'AppName.Length == 3' -- so they evaluate correctly. I've attached a screenshot to the bottom of my question. – Justin Rusbatch Apr 30 '10 at 17:49
  • 1
    You could make the test even less ambiguous: `if ("AOD".Length == 0) throw ...`. This would rule out the variable `AppName` as the source of your issues. If the exception is then still thrown, there might be nothing you can do about it. – stakx - no longer contributing Apr 30 '10 at 17:51
  • Another question: Where between the unit test method and the thrown exception is the constructor of `AppActiveDirectoryDomain` called? Can you show that code, too? – stakx - no longer contributing Apr 30 '10 at 17:55
  • 5/3: Today we're going to try running the tests on a different workstation. If we receive the same error, I may start a bounty. Otherwise, we'll assume his installation of VS2008/.NET is corrupt. – Justin Rusbatch May 03 '10 at 12:41

2 Answers2

2

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
  • 52,623
  • 10
  • 78
  • 89
  • 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 Rusbatch Apr 30 '10 at 18:20
  • I posted the IL. It doesn't look fishy to either of us, but neither of us are IL experts. – Justin Rusbatch Apr 30 '10 at 18:47
  • @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 Apr 30 '10 at 19:00
0

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 Rusbatch
  • 3,992
  • 2
  • 25
  • 43