Figure 1 MetaInfoOutput.txt TypeRef #1 (01000001)
Token: 0x01000001
ResolutionScope: 0x23000001
TypeRefName: System.Windows.Forms.Form
MemberRef #1
Member: (0a000002) .ctor:
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
MemberRef #2
Member: (0a000005) Dispose:
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
TypeRef #2 (01000002)
Token: 0x01000002
ResolutionScope: 0x23000001
TypeRefName: System.Windows.Forms.TreeNode
MemberRef #1
Member: (0a00002f) get_Nodes:
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Class System.Windows.Forms.TreeNodeCollection
No arguments.
MemberRef #2
Member: (0a000050) get_Text:
CallCnvntn: [DEFAULT]
hasThis
ReturnType: String
No arguments.
MemberRef #3
Member: (0a000056) .ctor:
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
Figure 3 MetaDataHelper.cpp // This is the main DLL file.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "MetaDataImportWrapper.h"
#define ARRAYSIZE( x ) (sizeof(x) / sizeof(x[0]))
#using <mscorlib.dll>
using namespace System;
using namespace System::Runtime::InteropServices;
namespace Wheaty
{
namespace UnmanagedMetaDataHelper
{
__gc public class MemberRefInfo
{
public:
String * m_strMemberName;
Byte m_signatureBlob[];
};
__gc public class TypeRefInfo
{
public: Int32 m_mdTypeRef;
String* m_strTypeName;
String* m_strAssemblyName;
MemberRefInfo* m_arMemberRefs[];
TypeRefInfo(){ m_mdTypeRef = 0;
m_strTypeName = S"";
m_strAssemblyName = S"";
m_arMemberRefs = 0; }
};
typedef TypeRefInfo* TYPEREFINFO [];
__gc public class TypeRefInfoHelper
{
public:
TYPEREFINFO public GetTypeRefInfo( String * strFilename )
{
MetaDataImportWrapper * pMetaDataWrapper = 0;
IntPtr memAnsiIntPtr = 0;
try
{
memAnsiIntPtr = Marshal::StringToCoTaskMemAnsi(strFilename);
char *pszFilename = (char *)memAnsiIntPtr.ToPointer();
// New a CMetaDataImportHelper class. This lets us
// catch the exception if the metadata isn't loaded
// properly by the constructor
pMetaDataWrapper = new MetaDataImportWrapper(pszFilename);
// Free this up, now that we don't need it any more
Marshal::FreeCoTaskMem( memAnsiIntPtr );
memAnsiIntPtr = 0;
IMetaDataImport * pIMetaData =
pMetaDataWrapper->m_pIMetaDataImport;
HCORENUM hEnum = 0;
mdTypeDef rTypeRefs[2048];
ULONG cTypeRefs = ARRAYSIZE(rTypeRefs);
HRESULT hr = pIMetaData->EnumTypeRefs( &hEnum,
rTypeRefs,
cTypeRefs,
&cTypeRefs );
if ( FAILED(hr) )
return 0;
// We don't need the HCORENUM open anymore
pIMetaData->CloseEnum( hEnum );
TypeRefInfo * arTypeRefs[] = new TypeRefInfo* [cTypeRefs];
for ( unsigned i = 0; i < cTypeRefs; i++ )
{
arTypeRefs[i] = new TypeRefInfo;
arTypeRefs[i]->m_mdTypeRef = rTypeRefs[i];
wchar_t wszTypeRef[512];
ULONG cchTypeRef = ARRAYSIZE(wszTypeRef);
mdToken tkResolutionScope;
HRESULT hr =
pIMetaData->GetTypeRefProps( rTypeRefs[i],
&tkResolutionScope,
wszTypeRef,
cchTypeRef,
&cchTypeRef);
if ( FAILED(hr) )
continue;
wchar_t wszAssemblyName[512] = { 0 };
ULONG cchAssemblyName = ARRAYSIZE(wszAssemblyName);
if (TypeFromToken(tkResolutionScope) == mdtAssemblyRef)
{
pMetaDataWrapper->m_pIMetaDataAssemblyImport->
GetAssemblyRefProps(
tkResolutionScope,
0,
0,
wszAssemblyName,
cchAssemblyName,
&cchAssemblyName,
0,
0,
0,
0 );
if ( FAILED(hr) )
continue;
}
arTypeRefs[i]->m_strTypeName = wszTypeRef;
arTypeRefs[i]->m_strAssemblyName = wszAssemblyName;
// Now spin through all the member refs of this type
hEnum = 0;
mdMemberRef rMemberRefs[2048];
ULONG cMemberRefs = ARRAYSIZE(rMemberRefs);
hr = pIMetaData->EnumMemberRefs(&hEnum,
rTypeRefs[i],
rMemberRefs,
cMemberRefs,
&cMemberRefs );
if ( FAILED(hr) )
continue;
// We don't need the HCORENUM open anymore
pIMetaData->CloseEnum( hEnum );
arTypeRefs[i]->m_arMemberRefs =
new MemberRefInfo * [ cMemberRefs ];
// Spin through all the MemberRefs
for ( ULONG j = 0; j < cMemberRefs; j++ )
{
wchar_t wszMember[512];
ULONG cchMember = ARRAYSIZE(wszMember);
PCCOR_SIGNATURE pvSigBlob;
ULONG cbSig;
// Get member name and signature blob
hr = pIMetaData->GetMemberRefProps(
rMemberRefs[j],
0,
wszMember,
cchMember,
&cchMember,
&pvSigBlob,
&cbSig );
if ( FAILED(hr) )
continue;
arTypeRefs[i]->m_arMemberRefs[j] =
new MemberRefInfo;
arTypeRefs[i]->m_arMemberRefs[j]->m_strMemberName
= wszMember;
// Create a managed Byte array to hold the
// signature blob
Byte mgdSigBlob[] = new Byte[cbSig];
// Copy the bytes from the unmanaged array to the
// managed array
for ( ULONG k = 0; k < cbSig; k++ )
mgdSigBlob[k] = *pvSigBlob++;
arTypeRefs[i]->m_arMemberRefs[j]->m_signatureBlob
= mgdSigBlob;
}
}
// Return all our results to the caller
return arTypeRefs;
}
catch( ... ) // Ooops!
{
if ( memAnsiIntPtr != 0 )
Marshal::FreeCoTaskMem( memAnsiIntPtr );
}
delete pMetaDataWrapper;
return 0;
}
}; // end class definition
};
} // end namespace definition
Figure 4 MetaDataImportWrapper.cpp #define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "MetaDataImportWrapper.h"
namespace Wheaty
{
namespace UnmanagedMetaDataHelper
{
MetaDataImportWrapper::MetaDataImportWrapper( LPCSTR pszFileName ) :
m_pIMetaDataDispenser( 0 ),
m_pIMetaDataImport( 0 ),
m_pIMetaDataAssemblyImport( 0 )
{
CoInitialize( 0 );
HRESULT hr;
// Create the IMetaDataDispenser instance. We need this to create
// the IMetaDataImport and IMetaDataAssemblyImport interfaces
hr = CoCreateInstance( CLSID_CorMetaDataDispenser, 0,
CLSCTX_INPROC_SERVER,
IID_IMetaDataDispenser,
(LPVOID *)&m_pIMetaDataDispenser );
if ( FAILED(hr) )
throw "Unable to create IMetaDataDispenser";
wchar_t wszFileName[MAX_PATH];
mbstowcs( wszFileName, pszFileName, lstrlen(pszFileName)+1 );
// Create the IMetaDataImport interface
hr = m_pIMetaDataDispenser->OpenScope( wszFileName, ofRead,
IID_IMetaDataImport,
(LPUNKNOWN *)&m_pIMetaDataImport );
if ( FAILED(hr) )
throw "Unable to create IID_IMetaDataImport";
// Create the IMetaDataAssemlyImport interface
hr = m_pIMetaDataDispenser->OpenScope( wszFileName, ofRead,
IID_IMetaDataAssemblyImport,
(LPUNKNOWN *)&m_pIMetaDataAssemblyImport);
if ( FAILED(hr) )
throw "Unable to create IID_IMetaDataAssemblyImport";
}
MetaDataImportWrapper::~MetaDataImportWrapper()
{
// Clean up our interface instances
if ( m_pIMetaDataImport )
{
m_pIMetaDataImport->Release();
m_pIMetaDataImport = 0;
}
if ( m_pIMetaDataAssemblyImport )
{
m_pIMetaDataAssemblyImport->Release();
m_pIMetaDataAssemblyImport = 0;
}
if ( m_pIMetaDataDispenser )
{
m_pIMetaDataDispenser->Release();
m_pIMetaDataDispenser = 0;
}
}LPCSTR MetaDataImportWrapper::TokenTypeName( mdToken token )
{
token = TypeFromToken( token );
#define TokenToName(x) case mdt##x: return #x;
switch( token )
{
TokenToName( Module )
TokenToName( TypeRef )
TokenToName( TypeDef )
TokenToName( FieldDef )
TokenToName( MethodDef )
TokenToName( ParamDef )
TokenToName( InterfaceImpl )
TokenToName( MemberRef )
TokenToName( CustomAttribute )
TokenToName( Permission )
TokenToName( Signature )
TokenToName( Event )
TokenToName( Property )
TokenToName( ModuleRef )
TokenToName( TypeSpec )
TokenToName( Assembly )
TokenToName( AssemblyRef )
TokenToName( File )
TokenToName( ExportedType )
TokenToName( ManifestResource )
TokenToName( String )
TokenToName( Name )
TokenToName( BaseType )
default: return "<unknown>";
}
}
}
}
Figure 5 TypeRefViewer.cs using System;
using System.Drawing;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Forms;
using System.Reflection;
using System.IO;
using System.Text;
using Wheaty.UnmanagedMetaDataHelper;
namespace TypeRefViewer
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.TreeView treeView1;
private System.Windows.Forms.Button buttonBrowse;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button buttonExport;
// A collection for storing all the namespaces we've
// seen before, along with their index into the
// treeview.
protected ListDictionary imported_namespaces;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
imported_namespaces = new ListDictionary();
String [] args = Environment.GetCommandLineArgs();
if ( args.Length > 1 )
DisplayTypeRefsFromFile( args[1] );
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
public override void Dispose()
{
base.Dispose();
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.Resources.ResourceManager resources =
new System.Resources.ResourceManager(typeof(Form1));
this.treeView1 = new System.Windows.Forms.TreeView();
this.buttonExport = new System.Windows.Forms.Button();
this.buttonBrowse = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.treeView1.Location = new System.Drawing.Point(16, 64);
this.treeView1.Size = new System.Drawing.Size(776, 432);
this.treeView1.TabIndex = 2;
this.treeView1.Anchor =
AnchorStyles.Left | AnchorStyles.Right |
AnchorStyles.Top | AnchorStyles.Bottom;
this.treeView1.AfterSelect +=
new System.Windows.Forms.TreeViewEventHandler
(treeView1_AfterSelect);
this.buttonExport.Location = new System.Drawing.Point(16, 504);
this.buttonExport.Size = new System.Drawing.Size(136, 40);
this.buttonExport.TabIndex = 0;
this.buttonExport.Text = "Export...";
this.buttonExport.Click +=
new System.EventHandler(this.buttonExport_Click);
this.buttonExport.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;
this.buttonBrowse.Location = new System.Drawing.Point(16, 8);
this.buttonBrowse.Size = new System.Drawing.Size(136, 40);
this.buttonBrowse.TabIndex = 0;
this.buttonBrowse.Text = "Browse...";
this.buttonBrowse.Click
+= new System.EventHandler(this.buttonBrowse_Click);
this.label1.Location = new System.Drawing.Point(176, 16);
this.label1.Size = new System.Drawing.Size(608, 32);
this.label1.TabIndex = 1;
this.label1.Text =
"Click the Browse button to select a .NET file to display";
this.label2.Location = new System.Drawing.Point(176, 512);
this.label2.Size = new System.Drawing.Size(608, 32);
this.label2.TabIndex = 5;
this.label2.Text = "assembly name";
this.label2.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(803, 551);
this.Controls.AddRange(new System.Windows.Forms.Control[]
{ this.buttonExport,
this.treeView1,
this.label1,
this.label2,
this.buttonBrowse});
this.Text = "TypeRefViewer - Matt Pietrek 2001";
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
protected bool DisplayTypeRefsFromFile( String filename )
{
// Prepare for a fresh start. Clear out the tree control
// and any namespaces we saw for the previously viewed
// assembly
treeView1.Nodes.Clear();
imported_namespaces.Clear();
// Get an instance of our helper class in the C++ DLL
// This class will call the unmanaged metadata APIs for us
TypeRefInfoHelper helper = new TypeRefInfoHelper();
// Call the helper to return an array of TypeRefInfo's for
// the given assembly
TypeRefInfo[] typeRefs = helper.GetTypeRefInfo( filename );
if ( typeRefs == null ) // Make sure there's something to display!
{
MessageBox.Show("Could not find .NET metadata in this file");
return false;
}
label1.Text = filename; // Looks OK to set this now
// Disable treeview redraws while we're stuffing it with data
treeView1.BeginUpdate();
// For each imported .NET Type...
foreach( TypeRefInfo tr in typeRefs )
{
try
{
String strQualifiedTypeName;
// If we have the name of the assembly DLL containing the
// Type, create a partially qualified name. The
// Type.GetType() method (below) works better this way!
if ( tr.m_strAssemblyName.Length > 0 )
strQualifiedTypeName = tr.m_strTypeName + ","
+ tr.m_strAssemblyName;
else
strQualifiedTypeName = tr.m_strTypeName;
// Get an instance of the Type, given its name
Type mytype = Type.GetType( strQualifiedTypeName );
// Get the namespace from the Type instance
String strNamespace = mytype.Namespace;
// Variable for keeping track of where we inserted
// this TypeRefInfo into the treeview
int iTreeNodeIndex;
// Have we already seen other types in this namespace? If
// yes, then get the treeview index so that we can
// insert this type under the namespace node
if ( imported_namespaces.Contains( strNamespace ) )
{
// Fix this cast!
iTreeNodeIndex =(int)imported_namespaces[strNamespace];
}
else // A new namespace we haven't seen before, so
{ // create a new namespace node for it
TypeRefTreeNode node = new
TypeRefTreeNode(strNamespace,tr.m_strAssemblyName);
iTreeNodeIndex = treeView1.Nodes.Add( node );
// Add knowledge of the new namespace to Dictionary
imported_namespaces[strNamespace] = iTreeNodeIndex;
}
// Figure out the index of the namespace node that we'll
// add the new Type to.
TypeRefTreeNode namespaceNode =
(TypeRefTreeNode)treeView1.Nodes[iTreeNodeIndex];
// Add the new Type under the namespace node
TypeRefTreeNode typeNode = new
TypeRefTreeNode(mytype.Name, tr.m_strAssemblyName);
namespaceNode.Nodes.Add( typeNode );
// Iterate through each imported member of the imported
// Type, and insert it under the Type node we just added.
foreach ( MemberRefInfo memberRef in tr.m_arMemberRefs )
{
// Call reflection method to return all methods
// with the specified name
MemberInfo[] members =
mytype.GetMember( memberRef.m_strMemberName );
// If just one member is returned, we know we got
// the right one
if ( members.Length == 1 )
{
// Call helper function to do pretty things with
// the member name before addding it.
AddMemberNodeToTree( members[0], typeNode,
memberRef.m_strMemberName );
}
else // This method is overloaded.
{ // See if we can find the right member
// Get number of params from signature
int cParams = memberRef.m_signatureBlob[1];
// If we find a matching method, remember it
MethodBase matchingMethod = null;
// Examine each overloaded method to see if it's
// the one we're looking for. We'll compare each
// method to the one returned in the
// MemberRefInfo struct
foreach ( MethodBase mb in members )
{
// Call reflection method to get the parameters
// for this method
ParameterInfo[] paramInfo = mb.GetParameters();
// Does the number of params (as seen via
// reflection) match the number of params from
// the signature? If so, we may have a match.
if ( paramInfo.Length == cParams )
{
// If this is the first match, we *may*
// have found a match.
if ( matchingMethod == null )
{
matchingMethod = mb;
}
else // A 2nd match. We didn't find
{ // it, so bail out
matchingMethod = null;
break;
}
}
}
if ( matchingMethod != null )
{
AddMemberNodeToTree( matchingMethod, typeNode,
memberRef.m_strMemberName );
}
else
{
typeNode.Nodes.Add(new TypeRefTreeNode
(memberRef.m_strMemberName
+ " (???) - Overloaded", strNamespace));
}
}
}
}
catch ( Exception e ) // Something went wrong!
{
String strException = e.ToString();
treeView1.Nodes.Add( new TypeRefTreeNode(
String.Format("Error with {0}.{1}",
tr.m_strAssemblyName,
tr.m_strTypeName), "" ) );
}
}
treeView1.EndUpdate(); // Let the treeview redraw itself again
return true;
}
static void AddMemberNodeToTree( MemberInfo member,
TypeRefTreeNode typeNode,
string strMemberName )
{
// Given a MemberInfo, decorate it with parameters and return value
int memberNode;
if ( (member.MemberType == MemberTypes.Method)
|| (member.MemberType == MemberTypes.Constructor) )
{
MethodBase method = (MethodBase)member;
String strParams =
FormatParameterString(method.GetParameters());
if ( member.MemberType == MemberTypes.Method ) // Normal method
{
TypeRefTreeNode n =
new TypeRefTreeNode( strMemberName + strParams +
" returns " +
((MethodInfo)method).ReturnType.ToString(),
typeNode.m_strAssembly );
memberNode = typeNode.Nodes.Add( n );
}
else // Constructor
{
TypeRefTreeNode n =
new TypeRefTreeNode( strMemberName + strParams,
typeNode.m_strAssembly );
memberNode = typeNode.Nodes.Add( n );
}
}
else // Not a method or constructor
{
memberNode = typeNode.Nodes.Add(
new TypeRefTreeNode(strMemberName,
typeNode.m_strAssembly) );
}
}
static String FormatParameterString( ParameterInfo[] arParameters )
{
StringBuilder str = new StringBuilder();
str.Append( "(" );
int paramNumber = 0;
foreach ( ParameterInfo param in arParameters )
{
if ( paramNumber > 0 ) // Tack on a comma before adding the
str.Append( ", " ); // next parameter. But not for the
// 0'th param
str.Append( param.ParameterType );
String strParamName = param.Name;
if ( strParamName != null )
{
str.Append( " " );
str.Append( strParamName );
}
paramNumber++;
}
str.Append( ")" );
return str.ToString();
}
private void buttonBrowse_Click(System.Object sender,
System.EventArgs e)
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.Filter =
"Executable files (*.exe)|*.exe|DLL files
(*.dll)|*.dll|All Files (*.*)|*.*";
openFileDialog1.FilterIndex = 1;
openFileDialog1.RestoreDirectory = true ;
if ( openFileDialog1.ShowDialog() == DialogResult.OK )
{
DisplayTypeRefsFromFile(openFileDialog1.FileName);
}
}
private void buttonExport_Click(System.Object sender,
System.EventArgs e)
{
// Get the name of the file to write to
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.RestoreDirectory = true ;
if ( saveFileDialog1.ShowDialog() != DialogResult.OK )
return;
TextWriter writer = new StreamWriter( saveFileDialog1.FileName );
TreeNodeCollection assemblyNodes = treeView1.Nodes;
// Emit some basic header / copyright type info
writer.Write( "TypeRefViewer - Matt Pietrek, 2001\n\n" );
writer.Write( "File: {0}\n\n", label1.Text );
// Iterate through each tree node, and dump it to the file
// Just minimal formatting is done here
foreach ( TypeRefTreeNode assemblyNode in assemblyNodes )
{
writer.Write( "\n" );
writer.Write( "Namespace: {0}\n", assemblyNode.Text );
foreach ( TypeRefTreeNode classNode in assemblyNode.Nodes )
{
writer.Write( "\tclass: {0}\t\tassembly: {1}\n",
classNode.Text, classNode.m_strAssembly );
foreach( TypeRefTreeNode memberNode in classNode.Nodes )
{
writer.Write( "\t\t{0}\n", memberNode.Text );
}
writer.Write( "\n" );
}
}
writer.Close();
}
private void treeView1_AfterSelect( object sender,
TreeViewEventArgs e)
{
// Set the form's bottom label to the name of the assembly
// that we stored in the node.
label2.Text = ((TypeRefTreeNode)e.Node).m_strAssembly;
}
}
}
Figure 6 TypeRefTreeNode.cs //==========================================
// Matt Pietrek
// FILE: TypeRefTreeNode.cs
//==========================================
namespace TypeRefViewer
{
using System;
using System.Windows.Forms;
public class TypeRefTreeNode : TreeNode
{
// The System.Reflection.MemberInfo associated with this node
public String m_strAssembly;
public TypeRefTreeNode(String caption, String strAssembly)
{
Text = caption;
m_strAssembly = strAssembly;
}
}
}
|