Rory's profiledotNotedPhotosBlogLists Tools Help

Blog


    September 05

    InternalsVisibleTo needs the whole public key in CLR v2.0 RTM

    Hopefully, this post will help the search engines to set the record straight about the InternalsVisibleTo attribute for creating friend assemblies. A number of notable .Net voices (Oliver, JuvalJames World) used this attribute in pre-RTM code, when the public key token was used, but Microsoft changed it in the release.

    In one post in particular, a commenter, Neil, was trying to say that PublicKeyToken isn't available for InternalsVisibleTo - only PublicKey is. C# MVP Oliver Sturm counters by explaining that it seems pointless to use the whole public key, since it is so much longer to copy than the public key token. While this is true, he apparently was unaware of the change when he wrote this. It was changed in .Net 2.0 RTM. MSDN, after remaining inaccurate after release for some 9 months, now shows the correct syntax.

    Update: David Kean also points this change out, and notes that it was changed in RC1, which if I recall correctly was released right after the PDC and right before the November RTM. David also has a nifty utility which generates the otherwise onerous attribute for you. Nice.

    April 27

    OdbcFactory.Instance.CreateDataSourceEnumerator does nothing

    This method sounds cool - like it would enumerate ODBC sources on a machine.

    However, it's not overridden from its base class, DbProviderFactory, which is implemented like so:

    public virtual DbDataSourceEnumerator CreateDataSourceEnumerator()
    {
        return null;
    }
    
    April 18

    How to return an empty IEnumerable using yield?

    UPDATE:

    IEnumerable<Foo> GetFooItems()
    {
            yield break;
    }

    ...is what I want.


    Given a method that returns an IEnumerable, you can use the C# 2.0 "yield" statement to return the next item in the enumeration. What if you want to return an empty enumeration? The only way I've found to do this is the following:

        if (false) 
              yield return null; 
    

    Any other ideas?

    January 26

    What appear to be, by all observable accounts, hacks in the v2.0 BCL

    I found this while reflectoring. It appears that certain generic types are preloaded in order to prime the JIT compiler, and reduce type allocation (setting up MethodTables and EEClasses) latency.

    A reasonable thing to do, and probably testing of the framework found unsatisfactory results, so hacks were inserted, and clearly labeled. I wonder if a subsequent service pack will eliminate these?

    November 28

    Getting more than 1000 results from a DirectorySearcher query

    Ok, this isn't exactly clear. Need to make a note of this so I don't forget it. The DirectorySeacher only returns 1000 results when the defaults are used. This setting actually comes from an AD server, not the .Net stuff, or even the ADSI or LDAP protocol implementation which are under the covers. So, basically, it's as good as hard-coded. ADSI helps out here, and we can take advantage of it, by just setting the DirectorySearcher.PageSize to a number besides 0. I like to set it to the DirectorySearcher.SizeLimit default which is 1000 (actually, it's 0, but this means 1000). This actually makes ADSI make as many paged requests as needed to get the entire result set, not just the first SizeLimit records. The docs aren't terribly clear on this, and I try to make repeated requests to the server until the resulting SearchResultsCollection's Count property is less than the searcher's PageSize property... which doesn't work since ADSI has done this bit for me and now I'm sitting with all the results in the collection. Here's how to get all the users (with a few properties) in the domain [in IronPython]:

    import clr
    from System import *
    clr.AddReference("System.DirectoryServices")
    from System.DirectoryServices import *
    from System.Collections.Generic import *

    searcher = DirectorySearcher()
    searcher.PageSize = 1000

    def GetDomainUsers():

      searcher.Filter = "(&(objectClass=user)(sAMAccountName=*)(!objectClass=computer))"
      searcher.PropertiesToLoad.Add("userPrincipalName")
      searcher.PropertiesToLoad.Add("userAccountControl")
      searcher.PropertiesToLoad.Add("sAMAccountName")
      l = List[SearchResult]()
      domainResults = searcher.FindAll()

      l.AddRange(domainResults)
      domainResults.Dispose() # Get rid of the unmanaged ADSI resources ASAP

    return l

    November 25

    DirectoryInfo and Path rely on ancient technology - the sad story of MAX_PATH

    NTFS is a very robust and well architected file system. I like it. I've looked at others, like WinFS (not really a core FS, just a layer on NTFS... but good technology idea nonetheless), ReiserFS 4 (my favorite, at least conceptually), XFS and JFS. NTFS plays in the same league with these higher performance creations from the previous age Unix heavyweights, including transactions (Vista and Longhorn), journaling, compression, encryption and a peerless access control system (in my experience). Along with all of this is support for paths which are 2^15 (32768) characters long. This beats the 260 chars of MS-DOS legacy.

    Oddly, this is hardcoded into two places - Win32 and the CRT:

    <WinBase.h> ... #define MAX_PATH 260

    <stdlib.h>      ... #define _MAX_PATH 260 /* max. length of full pathname */

    However, help is at hand... From the MSDN docs:

    Note The C Runtime supports path lengths up to 32768 characters in length, but it is up to the operating system, specifically the file system, to support these longer paths. The sum of the fields should not exceed _MAX_PATH for full backwards compatibility with Windows 98 FAT32 file systems. Windows NT 4.0, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003, and Windows Server 2003 NTFS file system supports paths up to 32768 characters in length, but only when using the Unicode APIs. When using long path names, prefix the path with the characters \\?\ and use the Unicode versions of the C Runtime functions.

    But, try creating a DirectoryInfo prefixed with \\?\ ... you'll throw an exception since it contains an invalid path char (?). Too bad, since people are finally getting the hang of long file names... one more legacy to detract from the shininess of Vista.

    I've opened a Connect issue... if you run into this, be sure to vote for it.

    June 07

    Code Generation in DataSet designer doesn't change project namespace workaround

    .Net adds a bunch of useful features, no doubt. Sometimes they trip over themselves though.
     
    Take, for example, the "global" namepace. This allows you to create "namespaceless" code and probably has some benefits, though I wonder what they are (perhaps they aren't necessarily for C#).
     
    When you create an ASP.Net project, your code is placed in the "global" namespace. This is fine, because mostly you don't care. However, it can cause problems. For instance, when you have an ASP.Net page called "Login", the generated class becomes "Login". In the code-gen which results in the classes that imlement your ASPX pages, there is no way to distinguish the Login class with the Login control which also exists in the global namespace. You get an error during runtime - "CS0030: Cannot convert type 'ASP.login_aspx' to 'System.Web.UI.WebControls.Login'" and it shows the invalid cast which was generated: "((Login)(this)).AppRelativeVirtualPath = "~/Login.aspx";".
     
    One of the ways you get around this is giving your code a namespace, which, in my estimation is a good thing to do to begin with - otherwise you will get runtime issues like these. And runtime issues are never good, especially when you can push those issues into designtime. This is where the problem noted in the title comes in. The DataSet designer isn't aware of the namespace you give your code. There is no way to change the namespace of the project like in 2003. This is a step backward, VS folks.
     
    The only real way around this is to put the DataSet in a separate project. I ended up doing this.
    April 04

    Paths in Asp.Net 2

    It's not immediately evident how to find where you are currently, in terms of the web site and server, in an Asp.Net app. MSDN comes to the rescue here, mostly, with a decoder ring page, but it's not easy to find itself.
     
     
    Basically, the points are:
     
    • Use the tilde character - "~" - in server controls and code, since it parses this out to mean "this application's root URL". Don't try to use this in JS or other client-side stuff, though.
    • HttpRequest.ApplicationPath provides this same info, but is less declarative and more clutter
    • HttpRequest.CurrentExecutionFilePath gives you the app root, any folders and the executing page. This is true even after a Server.Transfer or a Server.Execute.
    • HttpRequest.FilePath is similar, but gives the original (calling) page after Server.Transfer or a Server.Execute.
    • HttpRequest.Path returns the current URL path, exluding the host (and related kit). This also includes any "trailers" after the page, like .../default.aspx/blah/blah/blah
    • HttpRequest.PhysicalApplicationPath is the root path on the disk of the server where the application lives - corresponding to ApplicationPath. Don't send this to the client.
    • HttpRequest.PhysicalPath is the physical path of the currently executing page.

     

    March 01

    Don't ever do this bad thing

    This is bad:
     

    protected override void OnLoad( EventArgs e )

    {

    base.OnLoad(e);

    PubsDataSet.Instance.UpdateFailed += new UpdateFailedDelegate(Instance_UpdateFailed);

    }

     

    As you can see, this is an override from an ASP.Net code behind class. Yep, the .Instance property is a Singleton. I knew it was a no-no to hook up an event listener to a static instance in an Asp.Net solution, because the resulting class which has the listener never gets GC'd, and you get memory leakage in a nasty way.

     

    What's more, if you execute the code above, what happens is that all references to controls on the page return "null" since, due to the way that events are implemented as linked lists, the reference that you are looking at in the event handler is an old instance of the page, which should have been disposed, but isn't because you have a reference outstanding and the GC won't collect it.

     

    However, knowing this didn't stop me from doing it in an example I'm cooking up for a course. This is why experience is valuable - you know when something is wrong because you've felt the pain of doing it wrong, which lends to a much more acute sense of what is right than just studying it. Knowing the symptoms from studying the above analysis earlier helped me kill this bug in under 30 minutes, but having the experience will prevent me from creating the bug.

     

    Here is a good solution which makes use of an Anonymous method to handle the event callback. Notice how the handler is removed when the page is unloaded - this is key:

    private UpdateFailedDelegate updateFailed = delegate( object sender, UpdateFailedEventArgs e )

    {

    EditErrorLabel.Visible =

    true;

    EditErrorLabel.Text = e.FailureReason.ToString();

    };

    protected override void OnLoad( EventArgs e )

    {

    base.OnLoad(e);

    PubsDataSet.Instance.UpdateFailed += updateFailed;

    }

    protected override void OnUnload( EventArgs e )

    {

    base.OnUnload(e);

    PubsDataSet.Instance.UpdateFailed -= updateFailed;

    }

     

    February 24

    Why C++ needs .H files

    I never questioned why C++ needs .h files - I just accepted it. Definitions go into .h files, and code goes in .cpp or .c files. Strange, I know - me just accepting without questioning. Huh. I suppose it was the nice side effect of having the .h file be your API definition. But that's not really why it _has_ to have one. You need it because the C++ compiler is a clunky beast which only does a single pass through the code. Sure, there is some history for this, but there really isn't a tenable argument to be found why it should stay this way.

    I found all this out by trying to resolve a circular reference among objects which I am creating to wrap the Shapefile library by Frank Warmerdam. This post helped me unravel that issue, and deduct the real reason behind .h files.

    So basically, you can do this:

    In ShapeObject.h:

                    // a "stub" definition for the related class which contains ShapeObject
                    ref class ShapeFile;
    
    	public ref class ShapeObject
    	{
    	private:
    		Boolean		_isDisposed;
    		SHPObject*	_internalHandle;
    		Int32		_index;
    		ShapeType	_type;
    
                                    // Now we don't get a compilation error here
    		ShapeFile^	_parentFile;
                                    .
                                    .
                                    .
                    };
    

    And in ShapeFile.h:

                    #include "ShapeObject.h"
    	public ref class ShapeObjectsCollection : public System::Collections::Generic::List
    	{
    	public:
    		ShapeObjectsCollection() { }
    	};
    
    	public ref class ShapeFile
    	{
    	
    	private:
    		ShapeObjectsCollection ^ _shapes;
                                    .
                                    .
                                    .
    
                     };
    

    The important thing is the stub declaration... since the C++ compiler only does one pass, it needs to be declared before hand... implementation can be done later, which is where the .h/.c[pp] split comes in.
    November 24

    Generic fun

    Generics are really cool...
     
    ...
     
    bummer - i thought i might have one of those "You're our lucky 1,000,000th person who said that winner!" banner descend from Javascript heaven.
     
    Anyway I had a hard time using them vs. code-generation of collection classes since code gen had the advantage of non-generic _names_. But I just found out we can do this:
     
     

    public sealed class Role

    {

        private string _roleName;

        public string RoleName

        {

            get { return _roleName; }

        }

    }

     

    public sealed class RolesCollection : List<Role>

    {

       public Role this[string roleName]

       {

          get

          {

              foreach( Role r in this )

                  if( r.RoleName == roleName ) return r;

              return null;

           }

       }

    }

     

    Now I can have my cake and eat it too. Yum.

     

    -rory