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