Christophe's profileChristophe Nasarre - "Su...PhotosBlogLists Tools Help
    March 29

    View the Exception hierarchy in .NET

    In his post “Common Exception Types”, Brad Abrams shows the hierarchy of exceptions in .NET. It is not too difficult to get this hierarchy as you can see by yourself.

     

    With Assembly.LoadFrom(), I collect the assemblies from the CLR folder based on the version of mscorlib.dll loaded inside the current AppDomain. For each assembly, all public types are listed using Assembly.GetExportedTypes() and if it derives from Exception, it is added into the hierarchy.

     

    The output is simplistic with the sorting of children based on the type name with the namespace by implementing ISortable (see TypeNode.cs for implementation details). The assembly where the exception is defined is also printed if you add -a to the command line; could be useful if you need to add it as a reference to your project in Visual Studio.

     

    As a final note, the helper classes are built in a way that you could very easily get the hierarchy of any base class (see TypeHierarchy/ExceptionHierarchy for details). I find it useful for the GUI classes related to Control for example.

     

    The next step should be to integrate this knowledge within Visual Studio to offer a list of meaningful exception when you type “throw new”. This is not the case in VS.NET 2005 Beta 1; maybe in Beta 2. This could be a good idea for writing an AddIn…

    March 25

    Obsolete types and members in .NET - part3

    Even though Reflection makes it easy to retrieve information about obsolete types and members, the initial step to get the assembly you really mean is not obvious. My tool is supposed to work only on files and does not dig into the GAC.

     

    I understand that it could be interesting to plan the required changes for the next version of the .NET framework. So, you simply go into the subfolder of <C:\WINDOWS>\Microsoft.NET\Framework corresponding to the version you are interested in and call obso on the right assembly file (don’t forget the file extension).

     

    For a complete view, no wildcard is needed since the Command Shell does it for you: for %1 in (system*.dll) do obso %1 -s.

     

     

    In fact, the .NET Framework sometimes makes it harder to load the assembly you want. Without digging to much into the details about assemblies into the GAC or not, it is clear that if an assembly with the same name as the one you try to load using Assembly.LoadXXX() methods is already loaded into an AppDomain, you’re doomed. Why? Because these methods will return an Assembly but which might not describe the one you have requested, rather the one already loaded.

     

    The first consequence is the fact that you will always see the mscorlib.dll corresponding to the version used to build it. For example, if you try to load mscorlib.dll from the v1.1.4322 subfolder with an application written with Whidbey, you’ll get the one from v2. More surprising the first time you try it, a 1.1 application trying to load mscorlib.dll from v2.0.40607 (version numbers for Beta 1) folder will succeed! But you’ll get mscorlib from 1.1. This is due to the fact that before loading any assembly, a probing step is done by the runtime to find the “right” assembly (see blog references for more details). And “right” for the runtime does not always mean right for your needs.

     

    The “mscorlib” case is solved for me because I’m cheating: I always start the parser built against the same version of the runtime as the one of the target assembly. For the other assemblies, a choice has to be made between Assembly.LoadFrom() and Assembly.LoadFile(). I preferred the name of the latter but there is one tiny limitation you need to be aware of: the path given as parameter must be an “absolute path” or an ArgumentException is thrown with “Absolute path information is required.” as message.

     

    Finally, talking about obsolete members, note that Assembly.LoadWithPartialName() is marked with this attribute: another signal for us that Microsoft is pushing the “strongly named assemblies for all” in a post Whidbey timeframe…

     

     

    References

    Assembly.LoadFrom/LoadFile/Load(byte[]) prefers GAC

    Junfeng Zhang blog

    Mscorlib.dll

    Suzanne Cook

     

    March 17

    Obsolete types and members in .NET - part2

     

    To get the list of obsolete types and members of an assembly, I explained in the previous post that it is better to know which version of the CLR is expected by an assembly and then start the parser application of the same version. The different versions of the parsers binaries are embedded inside the launcher assembly as resource. Nothing complicated here: simply add the two executables to the project, right-click on each to set the Build Action property to “Embedded Resource”.

     

    At runtime, when the expected version of the CLR in the assembly to dissect is known, it is time to extract the right binary resource and copy it as the parser executable to start.

     

        1     private static bool ExtractFile(string resourceName, string filename)
        2     {
        3         Assembly runningAssembly = Assembly.GetExecutingAssembly();
        4         using (
        5             Stream stream =
        6                 runningAssembly.GetManifestResourceStream(resourceName)
        7                 )
        8         {
        9             // copy it in the directory of the application
       10             AssemblyName application =
       11                 Assembly.GetExecutingAssembly().GetName();
       12             string executableFilename =
       13                 application.CodeBase.Substring(
       14                     0,
       15                     application.CodeBase.LastIndexOf(
       16                             string.Format(
       17                             @"/{0}",
       18                             application.Name
       19                             )
       20                         )
       21                     );
       22             executableFilename =
       23                 executableFilename.Substring(
       24                     application.CodeBase.IndexOf("///")+3
       25                     );
       26             executableFilename =
       27                 Path.Combine(
       28                     executableFilename,
       29                     filename
       30                     );
       31 
       32             return(CopyStreamToDisk(stream, executableFilename));
       33         }
       34     }

     

    Given the name of the resource, GetManifestResourceStream() returns a stream to read its content. And the CopyStreamToDisk() helper transfers it to a file in the same directory.

     

        1     private static bool CopyStreamToDisk(Stream reader, string filename)
        2     {
        3         const int BLOCK_SIZE = 4096;
        4         using (
        5             FileStream writer =
        6                 new FileStream(
        7                     filename,
        8                     FileMode.Create,
        9                     FileAccess.Write,
       10                     FileShare.None,
       11                     BLOCK_SIZE,
       12                     false
       13                 )
       14             )
       15         {
       16             try
       17             {
       18                 int size = Convert.ToInt32(reader.Length);
       19                 byte[] block = new byte[size];
       20 
       21                 // read the whole file
       22                 reader.Read(block, 0, size);
       23 
       24                 // write it as a single blob
       25                 writer.Write(block, 0, size);
       26 
       27                 return(true);
       28             }
       29             catch(IOException x)
       30             {
       31                 Debug.WriteLine(
       32                     string.Format(
       33                         "Impossible to copy stream to {0}\r\n {1}",
       34                         filename,
       35                         x.Message
       36                         )
       37                     );
       38                 return(false);
       39             }
       40         }
       41     }

     

    It is not time to run the parser application with the parameters received by the launcher. One tiny detail needs to be fixed: how to get the output of the parser in the open console? I have already explained how to do this in C++ for one of my article. Here is the corresponding managed way to do so. First, keep track of the process into a member variable:

     

        private static Process _parserProcess = null;

     

    Then, start the process with the same command line:

     

        1     // run the dumper and redirect output to the same console
        2     _parserProcess = new Process();
        3     _parserProcess.StartInfo.Arguments = commandLine.ToString();
        4     _parserProcess.StartInfo.FileName = appName;
        5     _parserProcess.StartInfo.CreateNoWindow  = true;
        6     _parserProcess.StartInfo.UseShellExecute = false;
        7     _parserProcess.StartInfo.RedirectStandardOutput = true;
        8     _parserProcess.StartInfo.RedirectStandardError = true;
        9     _parserProcess.Start();
       10 
       11     // start the threads listing to the std out/error pipes
       12     Thread readOutputThread = new Thread(new ThreadStart(ReadStdOut));
       13     readOutputThread.Start();
       14     Thread readErrorThread = new Thread(new ThreadStart(ReadStdError));
       15     readErrorThread.Start();
       16 
       17     _parserProcess.WaitForExit();
       18 
       19     // the threads might still be running
       20     while(!_bEndOfProcess)
       21     {
       22         Thread.Sleep(100);
       23     }

     

    Finally, the two threads are redirecting the errors and standard output to the current console:

     

        1     private static void ReadStdOut()
        2     {
        3         try
        4         {
        5             string line;
        6             while ((line = _parserProcess.StandardOutput.ReadLine()) != null)
        7             {
        8                 Console.WriteLine(line);
        9             }
       10         }
       11         catch
       12         {
       13             Debug.WriteLine("----exception in stdout----");
       14         }
       15 
       16         _bEndOfProcess = true;
       17     }
       18 
       19     private static void ReadStdError()
       20     {
       21         try
       22         {
       23             string line;
       24             while ((line = _parserProcess.StandardError.ReadLine()) != null)
       25             {
       26                 Console.WriteLine(line);
       27             }
       28         }
       29         catch
       30         {
       31             Debug.WriteLine("----exception in stderror----");
       32         }
       33     }

     

    Some tedious details about obtaining the obsolete types and members will be explored in the next post.

     

     

     

    References

    Escape from DLL Hell with Custom Debugging and Instrumentation Tools and Utilities,Part 2

    Christophe Nasarre

    Q190351 "HOWTO: Spawn Console Processes with Redirected Standard Handles"

    MSDN

     

     

    March 16

    Obsolete types and members in .NET - part1

     

    During the 11h49 long flight to reach San Francisco from Paris Charles De Gaulle airport, I finally have the time to finish writing the tiny tool I was thinking about for a couple of weeks. The idea is simple: I’m working with Whidbey Beta 1 and I need to know what type and methods will become obsolete.

     

    The .NET Framework provides the ObsoleteAttribute to tag any type or member and state if this should be an error to use them and an explanation or hint about a possible workaround. As usual, this is not the part of the code which is responsible for retrieving the information out from the attribute that is complicated. Here is the code for a type:

     

        1  ObsoleteAttribute[] attributes =
        2      Type.GetCustomAttributes(typeof(ObsoleteAttribute), false)
        3      as ObsoleteAttribute[];

     

    Call GetMembers() with the right BindingFlags to get the members of a Type and it is then almost the same to get obsolete information for a MemberInfo:

     

        1  ObsoleteAttribute[] attributes =
        2      member.GetCustomAttributes(typeof(ObsoleteAttribute), false)
        3      as ObsoleteAttribute[];

     

    In both cases, if attributes.Length is equal to 1, you simply look at the two properties of attributes[0]:

    • IsError is true when the compiler will refuse to even call this property in your code
    • Message provides a string explaining why this type/member is deprecated and sometimes gives a workaround.

     

     

    But before reaching the right assembly must be loaded to extract the types from. And this is where it hurts. Not 100% of the times but the small percentage that is mandatory to cover. That small percentage is mscorlib.dll.

     

    How to load the mscorlib.dll of a different version of the CLR the application is compiled against or even simply the last installed on the machine? It seems that it is simply not possible. So I would need to build as many versions of my tool as many versions of the CLR I want to be able to scan. In a previous post, I had to compile the same code twice to get two executables; one for Everett (CLR 1.1) and one for Whidbey (CLR 2.0). This time, I decided to find a way to only have one single application.

     

    This statement seems to be incompatible with the fact that only one version of mscorlib.dll can be loaded at a time. Well… I should rather say, “I only want to deploy one single application”. Let’s compile and build one version for Everett and one for Whidbey. Then, embed these two executables as binary resources into a third application which acts as a “launcher”. This application is built against the smallest version of the CLR you want to support (the 1.1 version in my case). I’ll explain how to do that in a forthcoming post.

     

    Let’s suppose I have these two versions as resource embedded into a launcher; how do I know which one to pick and start? I was convinced that it should be somewhere in the PE file itself. Not a bad guess but more complicated as I thought. Starting from the source I have already written in C++, itself based on Matt Pietrek code written in 1997 and updated with the Platform SDK winnt.h header file, I was ready to start.

     

    The first IMAGE_OPTIONAL_HEADER header

        1 [StructLayout(LayoutKind.Sequential)]
        2 struct IMAGE_OPTIONAL_HEADER
        3 {
        4     //
        5     // Standard fields.
        6     //
        7     public ushort Magic;
        8     public byte MajorLinkerVersion;
        9     public byte MinorLinkerVersion;
       10     public uint SizeOfCode;
       11     public uint SizeOfInitializedData;
       12     public uint SizeOfUninitializedData;
       13     public uint AddressOfEntryPoint;
       14     public uint BaseOfCode;
       15     public uint BaseOfData;
       16 
       17     //
       18     // NT additional fields.
       19     //
       20     public uint ImageBase;
       21     public uint SectionAlignment;
       22     public uint FileAlignment;
       23     public ushort MajorOperatingSystemVersion;
       24     public ushort MinorOperatingSystemVersion;
       25     public ushort MajorImageVersion;
       26     public ushort MinorImageVersion;
       27     public ushort MajorSubsystemVersion;
       28     public ushort MinorSubsystemVersion;
       29     public uint Win32VersionValue;
       30     public uint SizeOfImage;
       31     public uint SizeOfHeaders;
       32     public uint CheckSum;
       33     public ushort Subsystem;
       34     public ushort DllCharacteristics;
       35     public uint SizeOfStackReserve;
       36     public uint SizeOfStackCommit;
       37     public uint SizeOfHeapReserve;
       38     public uint SizeOfHeapCommit;
       39     public uint LoaderFlags;
       40     public uint NumberOfRvaAndSizes;
       41// IMAGE_DATA_DIRECTORY[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] DataDirectory;
       42// read one directory after the other
       43 };

    contains a lot of version information but the only one which is different between Everett and Whidbey is the major linker versions: 6 for the former and 8 for the latter. But there must be a more “.NET” based way to make the difference!

     

    Let’s jump to the IMAGE_COR20_HEADER

        1 [StructLayout(LayoutKind.Sequential)]
        2 struct IMAGE_COR20_HEADER
        3 {
        4     // Header versioning
        5     public uint                cb;             
        6     public ushort               MajorRuntimeVersion;
        7     public ushort               MinorRuntimeVersion;
        8 
        9     // Symbol table and startup information
       10     public IMAGE_DATA_DIRECTORY MetaData;       
       11     public uint                Flags;          
       12     public uint                EntryPointToken;
       13 
       14     // Binding information
       15     public IMAGE_DATA_DIRECTORY Resources;
       16     public IMAGE_DATA_DIRECTORY StrongNameSignature;
       17 
       18     // Regular fixup and binding information
       19     public IMAGE_DATA_DIRECTORY CodeManagerTable;
       20     public IMAGE_DATA_DIRECTORY VTableFixups;
       21     public IMAGE_DATA_DIRECTORY ExportAddressTableJumps;
       22 
       23     // Precompiled image info (internal use only - set to zero)
       24     public IMAGE_DATA_DIRECTORY ManagedNativeHeader;

    which contains details related to .NET only. Unfortunately, the Major/Minor runtime version information stays the same…

     

    No problem: find the .NET Metadata documentation provided by Visual Studio and decipher it. After the IMAGE_COR20_HEADER, there are major and minor fields but always with the same 1.1 value…

     

    Just before I gave up, I started to read Steven Pratschner’s book in the flight back from Redmond last week. In addition to providing a great deal of details about how the CLR runtime starts, he mentions that a code sample is available to figure out which version of the .NET Framework was expected for a particular assembly. A few seconds to download it and the information is there: just after the documented COR header... A string with the expected version: “v1.1.4322” for Everett and “v2.0.40607” for Whidbey Beta 1 (read PEHeader.cs in the obso project). Thanks Steven

     

     

     

    References

    Customizing the Microsoft .NET Framework Common Language Runtime book By Steven Pratschner

    Escape from DLL Hell with Custom Debugging and Instrumentation Tools and Utilities

    Christophe Nasarre

    An In-Depth Look into the Win32 Portable Executable File Format

    Matt Pietrek

    “Partition II Metadata.doc” from Program Files\Microsoft Visual Studio ...\SDK\v…\Tool Developers Guide\docs for metadata layout in PE files

    www.pinvoke.net web site for managed signatures of most structures and unamanged API.

     

     

    March 15

    Syntax coloring

    There was a change in my previous post: syntax coloring of the code.

    I finally found a reliable tool to cut code from VS.NET and paste it into MSN Spaces.

    Very very handy  

    What is the right number of members to make a good interface?

    I’m lucky to be attending “Software Developer Conference West in Santa Clara. I spent my whole day listening to Juval Löwy, Microsoft Software Legend in the “Advanced .NET” session.

    During the presentation, Juwal presents some statistics about how big/small an interface should be. But from what kind of big and “well thought” project should we build such a statistics list? Well… The .NET runtime itself seems to be a good starting point; very easy and straightforward to do.

    Step 1 - getting the runtime folder and its version

    If you list the assemblies loaded in the current application domain, you’ll find “mscorlib” from which the CLR folder could be extracted.

        1         // get the path and version from mscorlib already loaded by the application
        2         AppDomain CurrentAppDomain = AppDomain.CurrentDomain;
        3         Assembly[] LoadedAssemblies = CurrentAppDomain.GetAssemblies();
        4         Assembly mscorlibAssembly = null;
        5         foreach(Assembly CurrentAssembly in LoadedAssemblies)
        6         {
        7             string szFullName = CurrentAssembly.FullName;
        8             if (szFullName.StartsWith("mscorlib"))
        9             {
       10                 mscorlibAssembly = CurrentAssembly;
       11                 break;
       12             }
       13         }
       14 
       15         // extract the path from CLR Location
       16         _CLRFolder = Path.GetDirectoryName(mscorlibAssembly.Location);
       17 
       18         // get the version of the first module of mscorlib assembly
       19         // (should be mscorlib.dll)
       20         _CLRVersion =
       21             FileVersionInfo.GetVersionInfo(
       22             mscorlibAssembly.GetModules()[0].FullyQualifiedName
       23             );

    Step 2 - enumerating the right files

    In addition to mscorlib.dll, I’m making the same assumption as Anakrino: all files beginning with “System” are to be considered as part of the .NET Framework. Note that WinCV does not consider the following files:

    System.Configuration.Install

    System.EnterpriseServices

    System.Web.RegularExpression

    as part of its search domain.

        1         // get mscorlib first
        2         string mscorlib = GetFileInCLRFolder("mscorlib.dll");
        3         ComputeAssemblyStatistics(mscorlib);
        4 
        5         // then compute for the other System.xxx files
        6         string[] files = Directory.GetFiles(_CLRFolder, "System*.dll");
        7 
        8         foreach(string file in files)
        9         {
       10             ComputeAssemblyStatistics(file);
       11         }

    Step 3 - loading each assembly and enumerating the types to find the interfaces

    Reflection is used to load each assembly with Assembly.LoadFrom(). Then all types are listed using GetTypes() instead of GetExportedTypes() in order to take internal types into account. The IsInterface property indicates whether a type is an interface or not.

        1         try
        2         {
        3             // load the assembly
        4             Assembly assembly = Assembly.LoadFrom(filename);
        5 
        6             // get all types
        7             Type[] types = assembly.GetTypes();
        8 
        9             // check if it is an interface
       10             foreach(Type type in types)
       11             {
       12                 if (type.IsInterface)
       13                 {
       14                     DumpInterfaceStatistics(type);
       15                 }
       16             }
       17         }
       18         catch
       19         {
       20             // invalid assembly such as System.EnterpriseServices.Thunk.dll
       21         }

    Step 4 - counting members, events, properties and methods

    All members are retrieved by Type.GetMembers(). Since only interfaces are examined, BindingFlags.Instance | BindingFlags.Public is the required parameter.           For each MemberInfo, its MemberType property is checked against MemberTypes.Event/Property/Method to update the counters.

        1     private void DumpInterfaceStatistics(Type type)
        2     {
        3         // count the interface itself
        4         _interfaces++;
        5 
        6         // count public/non static members (default for Interfaces anyway...)
        7         MemberInfo[] members =
        8             type.GetMembers(BindingFlags.Instance | BindingFlags.Public);
        9         _members += members.Length;
       10 
       11         // count methods, events, properties
       12         foreach(MemberInfo member in members)
       13         {
       14             switch(member.MemberType)
       15             {
       16                 case MemberTypes.Event:
       17                     _events++;
       18                 break;
       19 
       20                 case MemberTypes.Property:
       21                     _properties++;
       22                 break;
       23 
       24                 case MemberTypes.Method:
       25                     _methods++;
       26                 break;
       27             }
       28         }

    The rest is simply sums and divisions. Find here the source code and binaries.

    Here is the result for the versions 1.1

     

    Summary for 1.1.4322.573

    ------------------------

    616 interfaces in 23 assemblies

       members     7.893

       methods     7.252

       events      0.058

       properties  0.583

     

    and 2.0 Beta 1

     

    Summary for 2.0.40607.16 (beta1.040607-1600)

    ------------------------

    1181 interfaces in 28 assemblies

       members     9.929

       methods     8.531

       events      0.043

       properties  1.355

     

    As you can see, the average number of members in interfaces is getting larger in 2.0 but more than 2. Also note that version 2.0 of the runtime is significantly bigger than version 1.1 in term of interfaces. However, a few types will be suppressed in Beta 2. The next post will talk about using ObsoleteAttribute to figure out which types and members will disappear and for which reason.

    March 05

    eBooks for free

    APress is giving away some books for free.

    It is a good way to get "COM and .NET Interoperability" by Andrew Troelsen.

    February 16

    No luck

    My home computer died four weeks ago... It took me one week to choose what will be inside the next one. One additional week to receive it and after a trip under NDA in Redmond, it was time to install Windows XP Pro and the right service packs.

    I discovered the pleasure of SATA hard drives and the need to have a nice tiny floppy disk containing the RAID drivers to insert after pushing the F6 key just at the beginning of the installation. No problem: download the right package on the ASUS web site, build the corresponding floppy and go!

    Well... not really. One reboot to let Windows discover its hardware environmenent and one reboot to customize the GUI. But after that, impossible to restart the machine: the hard drive was not recongnized. Let's reinstall everything again to face the same problem. Ok. Let's dig into the BIOS to find out that the boot order was wrong between the two hardrives. Don't want to know why the machine was able to reboot TWICE without any problem but, it was working.

    Installation of XP SP1 was successful. Same for the motherboard drivers (it is a smart motherboard with additional RAID controller, sound card and network controller with integrated firewall) and the graphic card drivers. Everything was lightning fast and the machine was up and running.

    Well... not for long. After a few minutes, the system hang. Oh... Let's download the last BIOS update and flash it. Same effect. Ok... Let's repartition, reformat and reinstall Windows. Just to be sure. Install SP1 - Done. Download and install the latest drivers for the motherboard (compatible with the latest BIOS version) and do the same for the PCI Express 6800 GT graphic card from NVidia. Done. Frozen again.

    Don't panic... Keep cool... Take a long breath...
    Test the 2Gb Corsair lifetime garantied memory with MemoryTest: ended at midnight after 45 minutes of stressssss without any problem.

    Call the support the next day: "send it back to us".
    Bye bye my brand new PC :^(

     

    January 23

    Property Extenders, Part 6

    In my previous post, I presented an enhanced base class for property extenders which implements ISupportInitialize in addition to IExtenderProvider to ensure consistency in setting values of the virtual properties.

     

    If you want to see where these two interfaces are used in the BCL, it is time to start up Reflector on you machine. Press F3 or click on the Search button and enter IExt to choose IExtenderProvider (I love this “partial” search which filters what you are looking for!). Once on the IExtenderProvider node, click on Derived Types to get the list of all types implementing this interface (see picture at the end of this post)

      

    The ErrorProvider type also implements ISupportInitialize interface. Why? For the same reason detailed earlier: in order to be in a consistent state, the extender needs to wait until data binding properties are all set before creating an internal helper member. A boolean member initializing is set to true in BeginInit and the method Set_ErrorManager that needs to be protected checks it. If it is true, it does nothing except setting another boolean setErrorManagerOnEndInit to true. And in EndInit, if this flag has been set, the same Set_ErrorManager is called again with the right values set in the IDE but after having reset the first initializing flag to false.

     

    How was I able to know that Set_ErrorManager was using the initializing flag? Easy: click on the initializing member in the tree pane of Reflector and press CTRL+E or right click to select Callee Graph and get the list of all members which are using it. As you can see it is also working for fields in addition to properties and methods!

     

     

    References

    Reflector and Resourcer tools from Lutz Roeder

    http://www.aisto.com/roeder/dotnet

     

    January 13

    Property Extenders, part 5

    In my previous post, a base class for extenders has been presented. It is now time to use it in a simple example. Let say I want to add a _first and zlast virtual property to any kind of component. Here is the code I need to write.

    First, the type of extendable component is defined in the overridable OnCanExtend method (here, all are accepted):

        protected override bool OnCanExtend(Component extendee)

        {

            return(true);

        }

     

    Second, the extender type is decorated with the appropriate ProvideProperty attributes:

    [

        ProvideProperty("_First", typeof(Control)),

        ProvideProperty("zLast", typeof(Control)),

    ]

    public class Extender : ExtenderBase

    {

       ...

    }

     

    The last part is to define the set/get accessors for the two properties. Since the base class takes care of the physical storage of the values, the implementation is straightforward and no additional member is required:

        [

            DefaultValue("")

        ]

        public string Get_First(Component extendee)

        {

            string propertyValue =

                GetPropertyValue(extendee, "_First") as string;

     

            // Note: don't return null because, since the default value is "",

            //       it would be serialized

            // --> maybe should implement ShouldSerialize_First() which would

            //     always returns false

            if (propertyValue == null)

            {

                propertyValue = string.Empty;

            }

     

            return(propertyValue);

        }

     

    The standard property-related attributes such as DefaultValue or Category can decorate the methods.

    The set accessor is even easier to implement:

        public void Set_First(Component extendee, string propertyValue)

        {

            SetPropertyValue(extendee, "_First", propertyValue);

        }

    Here is the result in the Properties pane for an extended button as show in the attached screenshot.

    And the resulting serialized code is:

        this.extender1.Set_First(this.button1, "first ");

        this.button1.Location = new System.Drawing.Point(36, 28);

        this.button1.Name = "button1";

        this.button1.TabIndex = 0;

        this.button1.Text = "button1";

        this.extender1.SetzLast(this.button1, "last");

    The reason why I have chosen such stupid names for these virtual properties should be obvious now: VS.NET serialized the properties initialization by alphabetical name. So, _First begins with the ‘_’ character which is “less” than any other letter and zLast starts by the letter ‘z’ which is the last of the european alphabet.

    But you simply can’t rely on a naming schema if you need to know the value of a particular property in your virtual property set accessor such as the Name or the Text values that are not know when Set_First is called (only the default values are available at that time).

    In that case, the extender has to implement the two methods of the ISupportInitialize interface:

        public void BeginInit();

        public void EndInit();

    These two methods are now magically called in InitializeComponent at strategic places:

        private void InitializeComponent()

        {

            this.components = new System.ComponentModel.Container();

            this.button1 = new System.Windows.Forms.Button();

            this.extender1 = new Extender(this.components);

            ((System.ComponentModel.ISupportInitialize)(this.extender1)).BeginInit();

            this.SuspendLayout();

            //

            // button1

            //

            this.extender1.Set_First(this.button1, "1");

            this.button1.Location = new System.Drawing.Point(36, 28);

            this.button1.Name = "button1";

            this.button1.TabIndex = 0;

            this.button1.Text = "button1";

            this.extender1.SetzLast(this.button1, "2");

            //

            // MainForm

            //

            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

            this.ClientSize = new System.Drawing.Size(143, 269);

            this.Controls.Add(this.button1);

            this.Name = "MainForm";

            this.Text = "Form2TestExtender";

            ((System.ComponentModel.ISupportInitialize)(this.extender1)).EndInit();

            this.ResumeLayout(false);

     

        }

    As a useful feature, it has been factorized in the base class and now you simply have to override OnBeginInit or OnEndInit. Since the extendee component can not be fully initialized until EndInit gets called, the new IsExtendeeInitialized property allows you to check when your code is called.

    Usually, EndInit is used to put each extendee virtual properties in a consistent state since all its IDE-related properties have been set. What is missing now is a way to get the list of extendees and for each the list of its properties. No problem. The Extendees property returns the extended components as an array:

        protected Component[] Extendees

        {

            get

            {

                // Note: don't return null when no component are extended

                //       instead, return an empty array

                // --> avoid exception when used in a foreach() pattern

                if (_propertiesPerExtendee.Count == 0)

                {

                    return(new Component[0]);

                }

                

                Component[] extendees =

                    new Component[_propertiesPerExtendee.Count];

                int current = 0;

                foreach(DictionaryEntry de in _propertiesPerExtendee)

                {

                    extendees[current] = de.Key as Component;

                    current++;

                }

               

                return(extendees);

            }

        }

    Per extendee, the GetProperties method returns the set names of the set properties as an array of strings:

        protected string[] GetProperties(Component extendee)

        {

            // sanity checks

            if (_propertiesPerExtendee[extendee] == null)

            {

                return(new string[0]);

            }

           

            Hashtable properties =

                _propertiesPerExtendee[extendee] as Hashtable;

            string[] propertyNames = new string[properties.Count];

            int current = 0;

            foreach(DictionaryEntry de in properties)

            {

                propertyNames[current] = de.Key as string;

                current++;

            }

            return(propertyNames);

        }

    Here is an example of using both helpers to get the list of extended components with their properties and values in the OnEndInit override:

        protected override void OnEndInit()

        {

            Component[] extendees = Extendees;

            foreach(Component extendee in extendees)

            {

                Debug.WriteLine(GetExtendeeName(extendee));

                string[] properties = GetProperties(extendee);

                foreach(string property in properties)

                {

                    Debug.WriteLine(

                        string.Format(

                            "   {0} = {1}",

                            property,

                            GetPropertyValue(

                                extendee,

                                property

                                )

                            )

                        );

                }

            }

        }

      

    References

    Design-time integration

    By Chris Sells

    http://www.awprofessional.com/articles/article.asp?p=169528 and his WinForm book

    December 31

    What else can we do?

    With the images of the deadly tsunami rolling again and again into my mind, I can't imagine doing nothing. Want to do something really simple? Go to Amazon and click on the Red Cross!

    December 17

    Property Extenders, part 4

    After having written a few extenders, it becomes obvious that a bunch of code can factorized.

     

    First, it is enough to implement CanExtend to support IExtenderProvider. This method calls the abstract method OnCanExtend which must be overridden. Why adding another layer just changing the name? In fact, from CanExtend to OnCanExtend, the boring cast into Component is removed:

     

    abstract public class ExtenderBase : Component, IExtenderProvider

    {

        // must be implemented

        protected abstract bool OnCanExtend(Component extendee);

     

        // IExtenderProvider implementation

        public bool CanExtend(object extendee)

        {

            Component extendedComponent = extendee as Component;

            if (extendedComponent == null)

            {

                return(false);

            }

           

            return(OnCanExtend(extendedComponent));

        }

    }

     

    Second, the GetExtendeeName helper method is available.

     

    Finally, it is now time to implement the real core of an extender: the properties which are associated to the other components of the container. For each extended component, a set of properties should be virtualized. This is why a Hashtable is used to keep track of the properties for a given component reference:

     

        private Hashtable _propertiesPerExtendee;

     

    Then, two helpers are available to manage the properties values for a component:

     

    protected object GetPropertyValue(Component extendee, string propertyName)

    {

        Hashtable properties = _propertiesPerExtendee[extendee] as Hashtable;

        if (properties == null)

        {

            return(null);

        }

       

        return(properties[propertyName]);

    }

     

    Each property value is then stored into another Hashtable and the key is the property name:

     

    protected void SetPropertyValue(Component extendee, string propertyName, object propertyValue)

    {

        // associate a hashtable per extendee

        // --> each entry maps the properties/values of an extendee

        Hashtable properties = null;

        if (!_propertiesPerExtendee.Contains(extendee))

        {

            properties = new Hashtable();

            _propertiesPerExtendee[extendee] = properties;

     

            OnBindExtendee(extendee);

        }

        else

        {

            properties = _propertiesPerExtendee[extendee] as Hashtable;

        }

     

        properties[propertyName] = propertyValue;

    }

     

     

    The final touch is the virtual OnBindExtendee method that can be used by an extender to do whatever initialization the first time a component is extended such as registering a handler for an event. This is a much safer way than spying the countless calls done to CanExtend by VS.NET

     

    The next post will present an example of extender using this base class but that faces weird problems.

     

    December 16

    Property Extenders, part 3

    Once a property is declared through the ProvidePropertyAttribute by an extender, the corresponding get/set accessors must be explicitly defined by methods implementation with strict but clear naming convention: for the get accessor, add Get to the property name given by ProvidePropertyAttribute and Set for the modifier accessor.

     

    Add the following code to the simple extender presented in the last post:

     

        public string GetVirtualProperty(object extendee)

        {

            Debug.WriteLine(

                string.Format(

                    "{0}.VirtualProperty --> no choice...",

                    GetExtendeeName(extendee)

                    )

                );

            return("no choice...");

        }

       

        public void SetVirtualProperty(object extendee, string propertyValue)

        {

            Debug.WriteLine(

                string.Format(

                    "{0}.VirtualProperty = {1}",

                    GetExtendeeName(extendee),

                    propertyValue

                    )

                );

        }

     

    This is not a valid implementation for a real property since the value is always the same “no choice…” but the purpose is rather to output traces each time an accessor is executed by VS.NET.

     

    I don’t want to spoil the next two pages with the resulting trace output but it is incredible the number of times the get accessor is called! My preferred workflow is when the form is selected and you scroll in the Property pane of VS.NET: I can’t understand why it is needed to know the value of an extended property…

     

     

    When trace is not enough, it is needed to debug the host where the extender runs; Visual Studio .NET. Here are the steps to easily debug an extender within VS.NET:

     

    1. create a solution with a project containing the extender component
    2. add to this solution a simple WinForms project to test the extender and Add Reference --> Project (the extender project)
    3. create another solution with the test application as single project
    4. come back to the extender solution and update the project properties

    ·        Debug Mode is set to Program and Start Application points to devenv.exe

    ·        Command Line Arguments points to the .sln corresponding to the solution created in step 3 (wraps it with “” to avoid spaces character issues)

    ·        Working Directory is set to the same directory (no “” needed)

     

    If the startup project is the extender assembly, when a debug session is started, a new instance of VS.NET is launched and debugged; breakpoints in the extender code are triggered during the Design time experience in the debugged VS.NET.

     

    The next post will present a base class for extenders

    December 15

    Property Extenders, part 2

    The MSDN documentation related to ProvidePropertyAttribute can be misleading. For each property provided by an extender, this attribute must decorate the declaration of the extender type (more on the required Get/Set methods in another post).

     

    The first string parameter is the name of the property and the second does not describe its type but rather the type of the component that can get this property. I don’t see why this second parameter is needed since the purpose of CanExtend is exactly to answer this question.

     

    Note: why the CanExtend parameter is an object? It should simply be a Component  or IComponent…

     

    But worse, all this does not seem to work in synch…

    Here is a very simple extender:

     

    [

        ProvideProperty("VirtualProperty", typeof(Control)),

    ]

    public class SimpleExtender : Component, IExtenderProvider

    {

        public bool CanExtend(object extendee)

        {

            Debug.WriteLine(

                string.Format(

                    "CanExtend({0})",

                    GetExtendeeName(extendee)

                    )

                );

            return(true);

        }

     

        private string GetExtendeeName(object extendee)

        {

            Control control = extendee as Control;

            Component component = extendee as Component;

            if (control != null)

            {

                return(control.Name);

            }

            else

            if (component != null)       

            {

                return(component.Site.Name);

            }

            else

            {

                return(extendee.GetType().Name);

            }

        }

    }

     

    Let’s create a simple form with a SimpleExtender instance and add a Component such as a timer and a Control such as a button. Here is the generated output trace:

       --- simple extender added ---

       CanExtend(simpleExtender1)

       CanExtend(MainForm)

     

       --- timer component added ---

       CanExtend(timer1)

       --- button added ---

       CanExtend(button1)

     

    Even though ProvidePropertyAttribute states that only Controls should be extended, CanExtend is also called for Components. However, without taking care of CanExtend always returning true, the VS.NET Property panel finally takes ProvidePropertyAttribute into account and VirtualProperty is not listed for timer1 component.

     

    CanExtend is systematically called even if no property is declared with ProvidePropertyAttribute.

    December 12

    Property Extenders, part 1

    During the last weeks, I had to dig into one of the possible extension mechanism exposed by Visual Studio .NET for developers: property extenders (I’m using the shorter “extender” word from now on).

     

    When you implement such as class, your goal is to provide additional properties to some or any components of a form. I’m not going to talk bout how to write an extender class from scratch but rather I’ll share some thoughts and questions I had during my research. Feel free to jump to the references at the end of this page for more details and valuable web resources about extenders.

     

    An extender is either a component that appears under the form in the component tray or a control with a visible UI. In both cases, the type implements the IExtenderProvider interface and defines the bool CanExtend(object extendee) method.

     

    Note: both the form and the extender itself are possible extendees since their reference is received by CanExtend.

     

     

    This method is called at design-time only by the Form designer each time a new control/component is added if the extender is already here. Otherwise, when the extender is added, its CanExtend method is called for itself, the form and then the existing controls and components. To know what’s going on, here is a simple implementation of CanExtend that traces the given extendee each time it is called:

     

    protected override bool OnCanExtend(object extendee)

    {

        Control control = extendee as Control;

        Component component = extendee as Component;

        if (control != null)

        {

            Debug.WriteLine(string.Format("extendee is a '{0}' named '{1}'", control.GetType().Name, control.Name));

        }

        else

        if (component != null)       

        {

            Debug.WriteLine(string.Format("extendee is a '{0}' named '{1}'", component.GetType().Name, component.Site.Name));

        }

        else

        {

            Debug.WriteLine(string.Format("extendee is a '{0}'", extendee.GetType().Name));

        }

     

        return(true);

    }

     

    But this is not the only cases when this method is called. For example, if you add another extender to the form such as a tooltip, CanExtend is called again with each existing components/controls, the form, the new extender but not your extender as parameter.

     

    Even more unpredictable, when you open a form with an extender, the CanExtend method is called an arbitrary number of times. For example, if the form contains a single button, here is what you get:

     

      extendee is a 'Extender' named 'extender1'

      extendee is a 'Extender' named 'extender1'

      extendee is a 'Extender' named 'extender1'

      extendee is a 'Extender' named 'extender1'

      extendee is a 'Form' named 'MainForm'

      extendee is a 'Form' named 'MainForm'

      extendee is a 'Form' named 'MainForm'

      extendee is a 'Form' named 'MainForm'

      extendee is a 'Form' named 'MainForm'

      extendee is a 'Form' named 'MainForm'

      extendee is a 'Button' named 'button1'

      extendee is a 'Extender' named 'extender1'

      extendee is a 'Form' named 'MainForm'

      extendee is a 'Form' named 'MainForm'

      extendee is a 'Button' named 'button1'

     

    Note: it is possible to add an extender to a form, or a user control but not to a container inside a form such as a panel.

     

    But how to easily get the Debug.WriteLine() output? Since one of the default listener forwards to the Win32 OutputDebugString() API, you simply have to download DebugView from SysInternals web site and it will list your traces as they are triggered; without even debugging Visual Studio. If you need to write your own version to support insertion of comments within the output for example, you should read “Steve Friedl's Unixwiz.net Tech Tips - Understanding Win32 ‘OutputDebugString’ or “Debug Monitor string in managed code. For C++ developers, simply download the DBMon sample from MSDN.

     

     

     

    References

    Design-time integration

    By Chris Sells

    and his WinForms book

     

    Getting to know IExtenderProvider

    By James T. Johnson

     

    Windows Forms Extender Provider Sample

    from MSDN

     

    Introduction

    My name is Christophe Nasarre and I’m living in Rieux, Oise, France, Europe, Earth. I’m working with Microsoft technologies since 1987 and I always tried to understand it in building my own set of tools.

     

    As a comment about Chris Sells’s “What Makes A Book Successful?, Matt Pietrek points out the following sentence: “"...writing a book that describes how to do your work with the technology instead of that describes how the technology works"”. For books and maybe articles, I have to admit that Chris seems to be right (read his Winforms book to learn what a great “how to do” book is).

     

    However, books and articles written by Matt have completely changed the way I see Windows platform and allow me to “dare” searching beyond the academic paths. I’m convinced that a developer needs both kind of information source to successfully write piece of software.

     

    The Internet seems to follow Chris’s point of view with a majority of “how to do” kind of descriptions. I’ll rather try to provide “how it works” post-its based on personal research about .NET and Win32.

    December 02

    THE Screensaver

    I have finally found the perfect screensaver for me

    Ca devait arriver...

    I was always thinking about bloging...

    No excuse with MSN Spaces now