Tuesday, December 18, 2007

.NET assembly resource browser

Several days ago I needed to retrieve some resources from .NET assemblies.Currently I am porting .NET windows-forms application to web platform. And as part of the work I need to retrieve image and text resources from existing application to place them in my web application source code. Unfortunately there are some difficulties in doing so even using Visual Studio. For example I could not retrieve image resources from ImageList object. Of course first I opened Reflector , which as I remembered had this feature. But when I opened target dll and started searching for "Export" button.. there was no such a feature there for images inside resource files and ImageLists .. So I decided to waste a hour to write some very simple resource reflector for this purpose. I hope it will be useful for you. So how we start ? Lets declare a class level object of type Assembly for reflection purposes: Assembly assembly = null; Also we need a button on a simple form that will allow us to show OpenDialog object and give a user an opportunity to choose the assembly (exe or dll file) which we will open for reflection. Click event for the button looks like this:
   1:          private void button1_Click(object sender, EventArgs e)
   2:          {
   3:              // ofdAssembly - OpenFileDialog component
   4:              if (ofdAssembly.ShowDialog() == DialogResult.OK)
   5:              {
   6:                  // Load assembly using reflection
   7:                  assembly = Assembly.LoadFrom(ofdAssembly.FileName);
   8:                  // call the method that will retrieve all the information
   9:                  // about resources inside our assembly
  10:                  ShowInfo();
  11:              }
  12:          }
Before we continue lets drop on the form TreeView component and call it tvResources ShowInfo Method looks like this:
   1:          private void ShowInfo()
   2:          {
   3:              // Just clean up the tree (if it contains any nodes from
   4:              // previously selected assembly)
   5:              tvResources.Nodes.Clear();
   6:              //check if assembly is assigned
   7:              if (assembly != null)
   8:              {
   9:                  // yes unfortunately I did not found way better than this,
  10:                  // we need to use exception try catch blocks to find out
  11:                  // what resource exactly we are talking to
  12:                  // see next, resource list is just list of strings so we
  13:                  // need to find the type ourselves
  14:                  // and in the case of exception try next type, I hope you
  15:                  // will reply if you know better way
  16:                  try
  17:                  {
  18:                      // set the status label text to point full name of an
  19:                      // assembly
  20:                      lbStatus.Text = assembly.FullName;
  21:                      // enumerate all resources into array of strings
  22:                      string[] resourceNames = assembly.GetManifestResourceNames();
  23:                      // iterate through retrieved list
  24:                      foreach (string resourceName in resourceNames)
  25:                      {
  26:                          // create default "empty" treenode which will stay
  27:                          // the way we set it here if we cannot resolve its
  28:                          // type farther
  29:                          TreeNode tn = new TreeNode(resourceName);
  30:                          tn.ImageIndex = 0;
  31:                          tvResources.Nodes.Add(tn);
  32:                          // create ResourceStream object from current string
  33:                          // representation of resource name
  34:                          // I used IDisposable pattern here to make sure it
  35:                          // is disposed after use
  36:                          using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName))
  37:                          {
  38:                              try
  39:                              {
  40:                                  // try to create ResourceSet object from
  41:                                  // current stream
  42:                                  // it represents simple resource file in
  43:                                  // real VS project
  44:                                  ResourceSet resources = new ResourceSet(resourceStream);
  45:                                  IDictionaryEnumerator enu = resources.GetEnumerator();
  46:                                  //  if we succeeded and exception wasnot
  47:                                  // caught then we are on right way -
  48:                                  // so just enumerate through separate
  49:                                  // resources inside ResourceSet
  50:                                  while (enu.MoveNext())
  51:                                  {
  52:                                      // Create child nodes (second level) and
  53:                                      // assign them name from resource key
  54:                                      TreeNode childNode = new TreeNode(enu.Key.ToString());
  55:                                      tn.Nodes.Add(childNode);
  56:                                      // set default image for node, if
  57:                                      // resolve it type next, we will change it to
  58:                                      // correspond to the type
  59:                                      childNode.ImageIndex = 0;
  60:                                      // current value of resource object of
  61:                                      // course has GetType method
  62:                                      // which will allow us to resolve type
  63:                                      // of current resource
  64:                                      switch (enu.Value.GetType().Name)
  65:                                      {
  66:                                              // resource is Icon
  67:                                          case "Icon":
  68:                                              // Path the Icon value into
  69:                                              // treeNode's tag and set corresponding image to the node
  70:                                              childNode.Tag = enu.Value;
  71:                                              childNode.ImageIndex = 2;
  72:                                              childNode.SelectedImageIndex = 2;
  73:                                              break;
  74:                                              // resource is bitmap
  75:                                          case "Bitmap":
  76:                                              // path the value to the node
  77:                                              childNode.Tag = enu.Value;
  78:                                              // set the same image as we did
  79:                                              // for the Icon - it is image again
  80:                                              childNode.ImageIndex = 2;
  81:                                              childNode.SelectedImageIndex = 2;
  82:                                              break;
  83:                                              // Resource is ImageList
  84:                                          case "ImageListStreamer":
  85:                                              // create ImageList object from
  86:                                              // stream
  87:                                              ImageList il = new ImageList();
  88:                                              il.ImageStream = (ImageListStreamer)enu.Value;
  89:                                              // set the image to the node to
  90:                                              // differentiate it from another types
  91:                                              childNode.ImageIndex = 3;
  92:                                              childNode.SelectedImageIndex = 3;
  93:                                              int ind = 0;
  94:                                              // enumerate through the images
  95:                                              // in the list and create new level of nodes under current one
  96:                                              foreach (Image img in il.Images)
  97:                                              {
  98:                                                  // create subnodes and pass
  99:                                                  // Image objects to Tag property
 100:                                                  TreeNode grandchildNode = new TreeNode(ind.ToString());
 101:                                                  childNode.Nodes.Add(grandchildNode);
 102:                                                  grandchildNode.Tag = img;
 103:                                                  grandchildNode.ImageIndex = 2;
 104:                                                  grandchildNode.SelectedImageIndex = 2;
 105:                                                  ind++;
 106:                                              }
 107:                                              break;
 108:                                          // resource is String
 109:                                          case "String":
 110:                                              childNode.Tag = enu.Value;
 111:                                              childNode.ImageIndex = 1;
 112:                                              childNode.SelectedImageIndex = 1;
 113:                                              break;
 114:                                      }
 115:                                  }
 116:                              }
 117:                              catch (Exception ex)
 118:                              {
 119:                                  // if we are here most probably we have a
 120:                                  // problem creating ResourceSet object,
 121:                                  // so try to create more needed and possible
 122:                                  // types from strings
 123:                                  try
 124:                                  {
 125:                                      // try to create an Image object from
 126:                                      // resource
 127:                                      Image img = new Bitmap(resourceStream);
 128:                                      tn.Tag = img;
 129:                                      tn.ImageIndex = 2;
 130:                                      tn.SelectedImageIndex = 2;
 131:                                  }
 132:                                  catch (Exception exInner)
 133:                                  {
 134:                                      // we cannot (dont need) find out what
 135:                                      // is the type of current resource,
 136:                                      // so just read stream as text and show
 137:                                      // it to the user when he clicks on the node.
 138:                                      // (we pass it to Tag property which
 139:                                      // will hold this value for later use)
 140:                                      try
 141:                                      {
 142:                                          using (TextReader tr = new StreamReader(resourceStream))
 143:                                          {
 144:                                              tn.Tag = tr.ReadToEnd();
 145:                                              tn.ImageIndex = 0;
 146:                                              tn.SelectedImageIndex = 0;
 147:                                          }
 148:                                      }
 149:                                      catch (Exception exTextInner)
 150:                                      {
 151:                                          ; // we ignore errors for now
 152:                                      }
 153:                                  }
 154:                              }
 155:                          }
 156:                      }
 157:                  }
 158:                  catch (Exception ex)
 159:                  {
 160:                      // if we have got an error during calling
 161:                      // GetManifestResourceNames method,
 162:                      // show the error in status label
 163:                      lbStatus.Text = ex.Message;
 164:                  }
 165:              }
 166:              else
 167:              {
 168:                  lbStatus.Text = "";
 169:              }
 170:          }
Also we need to show the contents of each resource to the user. This happens when user clicks on the node. So the AfterSelect event looks like:
   1:          private void tvResources_AfterSelect(object sender, TreeViewEventArgs e)
   2:          {
   3:              ShowOneObject(e.Node.Tag, e.Node.Text);
   4:          }
ShowOneObject method is the following:
   1:          // declare m_filename variable it will be useful when exporting
   2:          // image resources
   3:          string m_filename = "";
   4:          private void ShowOneObject(object obj, string name)
   5:          {
   6:              // set TextBox (tbValue), PictureBox (pbValue)to initial
   7:              // visibility states (hidden),
   8:              // so we will show them if node with corresponding type was clicked
   9:              tbValue.Visible = false;
  10:              pbValue.Visible = false;
  11:              // set "Export" button's Visible property to false, we show it
  12:              // only for images, for strings user can copy the text 
  13:              // right to the clipboard
  14:              btnExport.Visible = false;
  15:              m_filename = "";
  16:              // quick and dirty way to make test tool app not to stay on our
  17:              // way when we want just to do a simple task :)
  18:              // we need just images, we dont try to write a Reflector !
  19:              try
  20:              {
  21:                  // check if passed Tag property of node has value at all
  22:                  if (obj != null)
  23:                  {
  24:                      // different actions for different types
  25:                      switch (obj.GetType().Name)
  26:                      {
  27:                              // Object is Icon - so show picturebox and
  28:                              // convert icon to bitmap to show it on the screen
  29:                          case "Icon":
  30:                              pbValue.Visible = true;
  31:                              pbValue.Dock = DockStyle.Fill;
  32:                              pbValue.Image = ((Icon)obj).ToBitmap();
  33:                              btnExport.Visible = true;
  34:                              // set filename so if we export this moment, we
  35:                              // will get the icon file with original resource's key name
  36:                              m_filename = String.Format("{0}.ico", name);
  37:                              break;
  38:                              // if object is Image or Bitmap - the action it
  39:                              // the same - show it in PictureBox component
  40:                          case "Image":
  41:                          case "Bitmap":
  42:                              pbValue.Visible = true;
  43:                              pbValue.Dock = DockStyle.Fill;
  44:                              pbValue.Image = (Image)obj;
  45:                              btnExport.Visible = true;
  46:                              // set name of file to be exported
  47:                              m_filename = String.Format("{0}.bmp", name);
  48:                              break;
  49:                              // object is string - just show the multiline
  50:                              // TextBox with tecxt from resource
  51:                          case "String":
  52:                              tbValue.Visible = true;
  53:                              tbValue.Dock = DockStyle.Fill;
  54:                              tbValue.Text = obj.ToString();
  55:                              break;
  56:                      }
  57:                  }
  58:              }
  59:              catch (Exception ex)
  60:              {
  61:                  tbValue.Visible = true;
  62:                  tbValue.Dock = DockStyle.Fill;
  63:                  tbValue.Text = ex.Message;
  64:              }
  65:          }
Ok and only one thing left is to create event handler for Click event of our btnExport button:
   1:          private void btnExport_Click(object sender, EventArgs e)
   2:          {
   3:              // set filename property of SaveFileDialog object
   4:              sfdAssembly.FileName = m_filename;
   5:              if (sfdAssembly.ShowDialog() == DialogResult.OK)
   6:              {
   7:                  // just save the resource to the file
   8:                  pbValue.Image.Save(sfdAssembly.FileName);
   9:              }
  10:          }
So we did it, - the quick and dirty application for retrieving resources from managed assemblies. Completed application looks like this : Hope this helps, Kirill

1 comment:

Anonymous said...

Mister,

can I download all code of project, please ??

send me

enrique.prados@a-e.es

Thanks in advanced. Greetings.