Introduction
Having the images and/or text used through an application packed into a single resource component which is included into your main assembly is very attractive for an application as it makes the deployment set compact. Moreover, loading these images from code is much less prone to errors since otherwise there will be a bunch of images spread out in some folder.
Furthermore when working on full applications, sometimes it is a necessary to mix C# and VB due to either end-user requests or the need to interface with APIs and/or legacy library calls and some of the arguments for those are specific for either language and finding the equivalent is cumbersome or the conversion just does not work.
This article covers both topics as I ran into this and had to dig a bit deep to get an actual working solution. The basics are straightforward but the details are what needed some research and some extra tools/steps as the Microsoft documentation is a bit vague, nothing new there.
1. The Single Resource Component as Part of the Main Assembly
1.1 Creating the Resource Component
First we create a resource component: Right click on the solution and select Add Component on the pop up menu. This will display different templates, select a resource file:
An extra Resource item will be listed on your solution, now click on the newly added resource item. You will get the option to add resources, do add your images, in our case we picked a couple of pre-cooked images. Upon completion you'll see there will also be a resources folder created under your solution, and your images will be listed there. For the method shown here, make sure that each of the images is set as an embedded resource.
1.2 Retrieving the Images from the Resource Component
OK, now the code for calling is listed below. The highlighted section shows the syntax to extract the specific image from the resource. The namespaces needed here are System.Reflection
for Assembly interface and System.IO
for Streaming.
private void btnLoad_Click(object sender, EventArgs e)
{
string start_name = "resource_sample";
displayBuffy = new Bitmap(200,200);
Graphics displayCanvas = Graphics.FromImage(displayBuffy);
Bitmap bmp;
Assembly assembly = Assembly.GetExecutingAssembly();
string strRes = start_name + ".Resources.bandera_checker.gif";
Stream stream = assembly.GetManifestResourceStream(strRes);
bmp = null;
try
{
bmp = new Bitmap(stream);
}
catch { }
stream.Close();
displayCanvas.DrawImage(bmp, new Rectangle(5, 5, bmp.Width / 3, bmp.Height / 3),
new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel);
Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (displayBuffy != null)
{
Graphics g = e.Graphics;
g.DrawImage(displayBuffy, 0, 0);
}
}
1.3 Caveats and Workaround
Up to here nothing exciting. Of course if you only have one or two images, it will all work beautifully, but when building large applications, quite a few images are needed and they usually are modified as the development goes on. But while doing this, the code will eventually not load a specific image no matter how deep your debug goes. To solve this, first we need to double check the name and path of the image in the EXE, due to the many changes made sometimes it either gets moved or is not included. The Visual Studio tool called ILDASM.exe comes pretty handy here, loading your EXE (or DLL for mixed language solutions) will allow you to see the actual path to the included resources, just click on the MANIFEST as shown in the picture below and you will see the actual details for the specific image or if it is actually included in the resource component. The ILDASM.exe is part of the framework SDK. If you don't have it, it can be downloaded from here.
With this, it is straightforward to double check if the specific images were actually included in the resource. As we well know, when the code grows in size and there are a large number of images being modified, it does not take long to get them out of place or hit a glitch in the VS where it ends up skipping them.
2. Mixing C# and VB in the Same Solution
As stated at the beginning, for many reasons it may be necessary to have mixed code, sometimes the end-user has some older code which usually is in VB and wants to still use it and maintain it or some other similar reasons. For this, we can easily add a VB section as a DLL and it is all good. It can go either way, e.g. having the main code in VB and adding the companion as C# or the other way. In the sample here C# will be the main code and the companion code will be in VB.
In essence, all we need to do is add another project to the current solution in the other language (VB in the sample below), add a reference to the companion code and use the proper syntax.
2.1 Creating the VB Section
The first step is to add another project to our current solution. If you have the VS installed by default, you will not see the option to add another project. To enable it, go to Tools->Options->Projects & Solutions and make sure you check always show solution as in the figure below:
So now you will get the solution view, highlight the solution and click Add New Project in the pop up, select the VB smart_device type as shown in the figure below:
On the next screen, we select the type as class library and we are ready to go. This will make the VB section a DLL where the needed VB routines will live.
Our project will now list a class vb file under the newly added VB project. Inside this file we can put our library of VB routines. In a real world scenario where we have many routines, it makes sense if we group routines with some common behaviour and/or handling into a respective namespace. Such code is listed below:
Namespace resource_sample_VB
Public Class VB_Class_1
Public Sub My_add_date(ByRef this_date As Date, ByVal days_2_add As Integer)
Dim tmp_date As Date
tmp_date = this_date
Try
tmp_date = tmp_date.AddDays(days_2_add)
Catch
tmp_date = this_date
End Try
this_date = tmp_date
End Sub
End Class
End Namespace
2.2 Calling VB from the C# Code
Finally we need to call this VB code from the main Project (C#). Since we made the VB code as a class library, the code will be compiled into a DLL. We need this DLL to be available to the main Project. To do so, a reference to the VB DLL needs to be added as in the figure below:
One small issue here is that while browsing to make a reference of the VB DLL at the very beginning there will be no such DLL as the VB project has not been built yet. A quick workaround is to highlight the vb project and build it from the Build Menu, afterwards you can go to the references and browse for the DLL.
Finally from C#, this is the syntax to call it
.
.
.
using System.Reflection;
using System.IO;
using resource_sample_VB_pieces.resource_sample_VB;
.
.
.
private void btn_Add2Date_Click(object sender, EventArgs e)
{
VB_Class_1 vb_tools = new VB_Class_1();
DateTime thisDate = DateTime.Parse (textDate.Text);
vb_tools.My_add_date(ref thisDate, Int32.Parse( textDays.Text ));
LblResult.Text = thisDate.ToString("d");
}
2.3 Simple Output of the Code
In our sample code, the btnLoad_Click
loads the image from the Resource component and the btn_Add2Date_Click
button passes the values on the textbox
es to the VB and the simple date calculation is printed out.
Final Thoughts
Statistically, old VB code is used from legacy code and/or the end-user wants to maintain it. The mixing code shown above allows to cover such a case where the preferred C# runs along with the VB code. Also the resource component or another set of resources could be also placed on the DLL to make it more memory efficient.
Of course, this is not the only way to accomplish this, but it is tested and it works.
History
- 4th June, 2009: Initial post