Figure 3

Figure 3 MetaViewer.cs
//==========================================
// Matt Pietrek
// MSDN Magazine, March 2001
// FILE: MetaViewer.cs
//==========================================
namespace MetaViewerFormNamespace
{
    using System;
    using System.Drawing;
    using System.ComponentModel;
    using System.WinForms;
    using System.Reflection;
    using System.Text.RegularExpressions;

    // An enum for associating treeview nodes with the appropriate
    // icons
    enum TreeViewBitmapEnum
    {
        Unknown = 0,
        Type = 1,
        Interface = 2,
        ValueClass = 3,
        Method = 4,
        Field = 5,
        Property = 6,
        Event = 7
    }

    public class MetaViewerForm : System.WinForms.Form
    {

        private string m_strFileName;
        private Assembly m_assembly = null;

        private System.ComponentModel.Container components;
        public System.WinForms.ImageList imageList1;
        private System.WinForms.TextBox text_details;
        private System.WinForms.TreeView treeView1;
        private System.WinForms.TextBox text_search;
        private System.WinForms.Button button_search;    
        private System.WinForms.Button button_assembly;
            
        public MetaViewerForm( string strFileName )
        {
            m_strFileName = strFileName;

            // Required for Win Form Designer support
            InitializeComponent();

            DisplayMetaData( );
        }
            
        public override void Dispose()
        {
            base.Dispose();
            components.Dispose();
        }

        public static void Main(string[] args)
        {
            if (args.Length < 1 )
            {
                MessageBox.Show( "Syntax: MetaViewer <filename>" );
                return;
            }

            try
            {
                Application.Run(new MetaViewerForm(args[0]));
            }
            catch( Exception e )
            {
                e.ToString();   // Kill compiler warning about "unused e" 
                                // :-)
            }
        }

        private void InitializeComponent()
        {
            System.Resources.ResourceManager resources =  new 
                System.Resources.ResourceManager(typeof(MetaViewerForm));

            this.components = new System.ComponentModel.Container();
            this.imageList1 = new System.WinForms.ImageList();
            this.text_details = new System.WinForms.TextBox();
            this.text_search = new System.WinForms.TextBox();
            this.treeView1 = new System.WinForms.TreeView();
            this.button_search = new System.WinForms.Button();
            this.button_assembly = new System.WinForms.Button();
                            
            imageList1.ImageSize = new System.Drawing.Size(16, 16);
            imageList1.ImageStream = (System.WinForms.ImageListStreamer)
                                resources.GetObject("imageList1.ImageStream");
            imageList1.ColorDepth = System.WinForms.ColorDepth.Depth8Bit;
            imageList1.TransparentColor = System.Drawing.Color.Transparent;
            
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.Text = "MetaViewer";
            this.ClientSize = new System.Drawing.Size(728, 412);

            text_details.Location = new System.Drawing.Point(504, 8);
            text_details.Text = "";
            text_details.Multiline = true;
            text_details.TabIndex = 0;
            text_details.ReadOnly = true;
            text_details.TabStop = false;
            text_details.Size = new System.Drawing.Size(216, 392);
            
            treeView1.Location = new System.Drawing.Point(8, 8);
            treeView1.Text = "treeView1";
            treeView1.Size = new System.Drawing.Size(480, 352);
            treeView1.TabIndex = 0;
            treeView1.AfterSelect += new
               System.WinForms.TreeViewEventHandler(treeView1_AfterSelect);
            treeView1.ImageList = imageList1;
            treeView1.HideSelection = false;
                        
            text_search.Location = new System.Drawing.Point(232, 376);
            text_search.TabIndex = 3;
            text_search.Size = new System.Drawing.Size(224, 20);
            
            button_search.Location = new System.Drawing.Point(128, 368);
            button_search.Size = new System.Drawing.Size(96, 40);
            button_search.TabIndex = 2;
            button_search.Text = "Search";
            button_search.Click+=new 
                System.EventHandler(button_search_Click);

            button_assembly.Location = new System.Drawing.Point(16, 368);
            button_assembly.Size = new System.Drawing.Size(96, 40);
            button_assembly.TabIndex = 1;
            button_assembly.Text = "Assembly Info...";
            button_assembly.Click +=
                new System.EventHandler(button_assembly_Click);

            this.Controls.Add(text_details);
            this.Controls.Add(treeView1);
            this.Controls.Add(text_search);
            this.Controls.Add(button_search);
            this.Controls.Add(button_assembly);
        }
        
        //===========================================================
        // Search for a Type that contains the string in the text control
        //===========================================================
        protected void button_search_Click(object sender, 
                                           System.EventArgs e)
        {
            int currentIndex = treeView1.SelectedNode.Index;
            int maxIndex = treeView1.GetNodeCount(false);
        
            // 2nd param "i" to Regex means case insensitive search
            Regex regex = new Regex( text_search.Text, "i" );
            TreeNode node = treeView1.Nodes[currentIndex];

            if ( regex.IsMatch(node.Text) )
                currentIndex++;

            int i;
            for ( i = currentIndex; i < maxIndex; i++ )
            {
                node = treeView1.Nodes[i];

                // if ( node.Text == text_search.Text )
                if ( regex.IsMatch(node.Text) )
                {
                    treeView1.SelectedNode = node;
                    ActiveControl = treeView1;
                    break;
                }
            }

            if ( i == maxIndex )
                MessageBox.Show( "No matches found" );
        }

        //===========================================================
        // When the "Assembly Info..." button is pressed, show the window
        //===========================================================
        protected void button_assembly_Click(   object sender,
                                                System.EventArgs e)
        {
            AssemblyInfoForm assemblyInfoForm =
                new AssemblyInfoForm( m_assembly );
            assemblyInfoForm.Show();
        }

        //===========================================================
        // When a treeview node is selected, update the details pane
        //===========================================================
        protected
        void treeView1_AfterSelect( object sender,
                                    System.WinForms.TreeViewEventArgs e)
        {
            text_details.Clear();

            if ( e.node is TypeNode )
            {
                ShowTypeDetails( ((TypeNode)e.node).m_type );
            }
            else if ( e.node is MemberInfoNode )
            {
                ShowMemberInfoDetails(((MemberInfoNode)e.node).m_memberInfo);
            }
        }

        //===========================================================
        // Top level routine called to display metadata for an assembly
        //===========================================================
        private void DisplayMetaData()
        {
            try
            {
                m_assembly = Assembly.Load( m_strFileName );
            }
            catch( Exception e )
            {
                MessageBox.Show( e.Message );
                throw e;  // Application.Exit doesn't seem to work here...
            }

            // Iterate through each of the modules in the assembly
            foreach ( Module module in m_assembly.GetModules() )
            {
                // Add the module's types to the TreeView
                PopulateTypes( module.GetTypes() );

                // Add module level methods and properties
                PopulateMembers( module.GetMethods(), treeView1.Nodes );
                PopulateMembers( module.GetFields(), treeView1.Nodes );
            }

            this.Text = "MetaViewer: " + m_strFileName;
        }

        //===========================================================
        // Fill in the main treeview with the types in a module
        //===========================================================
        protected void PopulateTypes( Type[] arTypes )
        {
            foreach ( Type t in arTypes )
            {
                TypeNode typeNode = new TypeNode( t );

                TreeViewBitmapEnum treeViewBitmapEnum;
                        
                if ( t.IsClass )
                    treeViewBitmapEnum = TreeViewBitmapEnum.Type;
                else if ( t.IsInterface )
                    treeViewBitmapEnum = TreeViewBitmapEnum.Interface;
                else if ( t.IsValueType )
                    treeViewBitmapEnum = TreeViewBitmapEnum.ValueClass;
                // For now, treat unmanaged and managed value classes the
                // same
                else if ( t.IsUnmanagedValueType )      
                    treeViewBitmapEnum = TreeViewBitmapEnum.ValueClass;
                else
                    treeViewBitmapEnum = TreeViewBitmapEnum.Unknown;

                typeNode.ImageIndex = typeNode.SelectedImageIndex =
                                            (int)treeViewBitmapEnum;

                treeView1.Nodes.Add( typeNode );

                MemberInfo[] arMemberInfo = t.GetMembers(
                                                BindingFlags.LookupAll |
                                                BindingFlags.DeclaredOnly 
                                                );
        
                PopulateMembers( arMemberInfo, typeNode.Nodes );
            }

        }

        //===========================================================
        // Add the members of a type to the appropriate node in the 
        // treeview
        //===========================================================
        protected void PopulateMembers( MemberInfo[] arMemberInfo,
            TreeNodeCollection treeNodeCollection)
        {
            foreach( MemberInfo member in arMemberInfo )
            {
                MemberInfoNode memberInfoNode = new MemberInfoNode(member);
                
                TreeViewBitmapEnum treeViewBitmapEnum2;

                if ( member is MethodInfo || member is ConstructorInfo )
                    treeViewBitmapEnum2 = TreeViewBitmapEnum.Method;
                else if ( member is FieldInfo )
                    treeViewBitmapEnum2 = TreeViewBitmapEnum.Field;
                else if ( member is PropertyInfo )
                    treeViewBitmapEnum2 = TreeViewBitmapEnum.Property;
                else if ( member is EventInfo )
                    treeViewBitmapEnum2 = TreeViewBitmapEnum.Event;
                else
                    treeViewBitmapEnum2 = TreeViewBitmapEnum.Unknown;
        
                memberInfoNode.ImageIndex =   
                    memberInfoNode.SelectedImageIndex
                    = (int)treeViewBitmapEnum2;
        
                treeNodeCollection.Add( memberInfoNode );
            }
        }

        //===========================================================
        // Updates the details pane with info about the selected Type
        //===========================================================
        protected void ShowTypeDetails( Type t )
        {           
            text_details.AppendText(
                    String.Format("Namespace: {0}\r\n",  t.Namespace) );

            // Show derivation hierarchy, back to System.Object
            text_details.AppendText( "Derives from:\r\n" );

            while ( true )
            {
                Type baseType = t.BaseType;
                if ( baseType == null )
                    break;

                text_details.AppendText( "\t" + baseType.Name + "\r\n" );
                t = t.BaseType;
            }
        }

        //===========================================================
        // Updates the details pane with info about the selected member
        //===========================================================
        protected void ShowMemberInfoDetails( MemberInfo memberInfo )
        {
            if ( memberInfo is MethodBase )
            {
                MethodBase methodBase = (MethodBase)memberInfo;

                if ( methodBase.IsVirtual )
                    text_details.AppendText( "Virtual\r\n" );
                if ( methodBase.IsStatic )
                    text_details.AppendText( "Static\r\n" );
                if ( methodBase.IsPublic )
                    text_details.AppendText( "Public\r\n" );
                if ( methodBase.IsPrivate )
                    text_details.AppendText( "Private\r\n" );
                if ( (methodBase.Attributes &
                   (MethodAttributes)MethodAttributes.PinvokeImpl) != 0 )
                {
                    text_details.AppendText( "PInvoke\r\n" );
                }

                if ( memberInfo is MethodInfo )
                {
                    text_details.AppendText(
                        String.Format("returns {0}\r\n",
                            ((MethodInfo)memberInfo).ReturnType.Name) );
                }
                                        
                ParameterInfo [] arParamInfo = methodBase.GetParameters();

                if ( arParamInfo.Length > 0 )
                    text_details.AppendText( "Parameters:\r\n" );

                foreach ( ParameterInfo paramInfo in arParamInfo )
                {
                    text_details.AppendText( String.Format("\t{0} {1}\r\n",
                            paramInfo.ParameterType.Name, paramInfo.Name));
                }
            }
            else if ( memberInfo is FieldInfo )
            {
                FieldInfo fieldInfo = (FieldInfo)memberInfo;

                if ( fieldInfo.IsStatic )
                    text_details.AppendText( "Static\r\n" );
                if ( fieldInfo.IsPublic )
                    text_details.AppendText( "Public\r\n" );
                if ( fieldInfo.IsPrivate )
                    text_details.AppendText( "Private\r\n" );

                text_details.AppendText( memberInfo.GetType().Name );
            }
            else if ( memberInfo is PropertyInfo )
            {
                PropertyInfo propertyInfo = (PropertyInfo)memberInfo;

                if ( propertyInfo.CanRead )
                    text_details.AppendText( "Readable\r\n" );
                if ( propertyInfo.CanWrite )
                    text_details.AppendText( "Writeable\r\n" );

                text_details.AppendText( memberInfo.GetType().Name );
            }
        }

    }               // End class class MetaViewerForm
}                   // End namespace
Figure 5 AssemblyInfoForm.cs
//==========================================
// Matt Pietrek
// MSDN Magazine, March 2001
// FILE: AssemblyInfoForm.cs
//==========================================
namespace MetaViewerFormNamespace
{    
    using System;
    using System.WinForms;
    using System.Reflection;

    public class AssemblyInfoForm : System.WinForms.Form
    {
        private System.ComponentModel.Container components;
        private System.WinForms.Button button_ok;
        private System.WinForms.TextBox text_assemblyInfo;

        private Assembly m_assembly;

        public AssemblyInfoForm(Assembly assembly)
        {
            m_assembly = assembly;

            // Required for Win Form Designer support
            InitializeComponent();

            DisplayAssemblyInfo();
        }

        public override void Dispose() {
            base.Dispose();
            components.Dispose();
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.button_ok = new System.WinForms.Button();
            this.text_assemblyInfo = new System.WinForms.TextBox();
            
            button_ok.Location = new System.Drawing.Point(256, 320);
            button_ok.Size = new System.Drawing.Size(64, 32);
            button_ok.TabIndex = 0;

            button_ok.Text = "OK";
            button_ok.Click += new System.EventHandler(button_ok_Click);
            text_assemblyInfo.Location = new System.Drawing.Point(8, 8);
            text_assemblyInfo.ReadOnly = true;
            text_assemblyInfo.Multiline = true;
            text_assemblyInfo.TabIndex = 0;
            text_assemblyInfo.TabStop = false;
            text_assemblyInfo.Size = new System.Drawing.Size(560, 296);
            text_assemblyInfo.ScrollBars = ScrollBars.Both;
            
            this.Text = "Assembly Info";
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(576, 356);
            
            this.Controls.Add(button_ok);
            this.Controls.Add(text_assemblyInfo);
        }

        protected void button_ok_Click(object sender, System.EventArgs e)
        {
            this.Close();
        }

        //===========================================================

        protected void DisplayAssemblyInfo()
        {
            // Dislay list of modules in the assembly
            foreach ( Module module in m_assembly.GetModules() )
            {
                text_assemblyInfo.AppendText(
                    String.Format("Module: {0}\r\n", module.Name) );
            }

            //==================================================
            // Display the list of assemblies referenced by this assembly

            text_assemblyInfo.AppendText( "\r\n" );
            text_assemblyInfo.AppendText( "Referenced Assemblies:\r\n" );

            foreach( AssemblyName assemblyName in
                        m_assembly.GetReferencedAssemblies() )
            {
                text_assemblyInfo.AppendText( String.Format("\t{0}\r\n",
                                              assemblyName.Name) );
            }

            //==================================================
            // Display the files in this asssembly

            text_assemblyInfo.AppendText( "\r\n" );
            text_assemblyInfo.AppendText( "Files:\r\n" );

            foreach ( String strResourceName in
                        m_assembly.GetManifestResourceNames() )
            {
                text_assemblyInfo.AppendText( String.Format("\t{0}\r\n",
                                              strResourceName) );
            }
        }
    }
}
Figure 6 MemberNode.cs
//==========================================
// Matt Pietrek
// MSDN Magazine, March 2001
// FILE: MemberNode.cs
//==========================================
namespace MetaViewerFormNamespace
{
    using System.WinForms;
    using System.Reflection;

    public class MemberInfoNode : TreeNode
    { 
        // The System.Reflection.MemberInfo associated with this node
        public MemberInfo m_memberInfo;
                    
        public MemberInfoNode(MemberInfo memberInfo)
        {
            m_memberInfo = memberInfo;
            Text = m_memberInfo.Name;
        }
    }
}