Sunday, March 22, 2009

Copy Constructor

It's strange, but I did not have to deal with copy constructors in C# until today. Here is the problem: in the application I'm working on, a user can retrieve customer's details from the database, change some values on several separate tabs, and then choose to save or discard changes. Now, discarding did not work properly till today. The problem is that the old values were not saved anywhere, so no way to access them was implemented.

Two approaches came to my mind: First, to reload data from the database, and second, to keep a 'backup' copy of the customer. Since an extra trip to the database takes several seconds, the 'backup' approach was chosen.

Now, when the customer details are retrieved from the database, they are immediately copied into the 'backup' customer object. Obviously, if you try to create a copy like this

Customer cust = SelectCustomerFromDB(id);
Customer cust2 = cust;

You will end up with two references to the same object. Now, if you change anything in 'cust' object, your 'cust2' is useless, cause it references the same object and will reflect the changes. This MSDN page gives a brief idea of the proper way to create a copy of the object

How to: Write a Copy Constructor (C# Programming Guide)

The above sample would now look more like this

class Customer
{
private string name;

// Copy constructor.
public Customer(Customer previousCustomer)
{
name = previousCustomer.name;
}

...
}

...

Customer cust = SelectCustomerFromDB(id);
Customer cust2 = new Customer(cust);

What if your customer has a list of names though?

class Customer
{
private List names;

// Copy constructor.
public Customer(Customer previousCustomer)
{
names = previousCustomer.names;
}

...
}

...

Customer cust = SelectCustomerFromDB(id);
Customer cust2 = new Customer(cust);

Now try changing something in cust.names. Ooops, the cust2.names reflected the changes. Unfortunately, the article linked above only shows the approach to copy the object with members of value types. Now, if you have members of reference types in your class, you would have to copy each one properly.

For the List type, for example, the following approach would work:

class Customer
{
private List names;

// Copy constructor.
public Customer(Customer previousCustomer)
{
names = new List(previousCustomer.names.ToArray());
}

...
}

I came across a few smart ways to implement the copy constructor so that you would not have to specify each class field separately, like in this exapmle

An Intelligent Copy Constructor In C# Using Reflection

public MyClass( MyClass rhs )
{
// get all the fields in the class
FieldInfo[] fields_of_class = this.GetType().GetFields(
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance );

// copy each value over to 'this'
foreach( FieldInfo fi in fields_of_class )
{
fi.SetValue( this, fi.GetValue( rhs ) );
}
}

This is great, because if you add a new field to the class and forget to add it to a copy constructor, your code will compile without warnings but you will get troubles sooner or later.

Unfortunately, this approach also works for value type fields only so I could not benefit from it. I could not quickly find a way to use this approach to reference type class members so I'll leave it for myself as a TODO task.

by . Also posted on my website

Thursday, March 19, 2009

DllImport Adventures

More fun with the scanner. Now we want to get the first name and last name in chinese from the certain types of passports. There is a dll written in C++ which supposedly provides this functionality. The function in this dll takes a path to an image as a parameter and returns a structure that contains first name and last name.

Here is the C++ signature of the function


bool recoCHN_P_Name(char *imgPath,RECO_DATA *o_data);

Here is the RECO_DATA struct

struct RECO_DATA{
wchar_t FirstName[200];
wchar_t Surname[200];
};

Now I have to call the C++ method from my C# code. The 'DllImport' thingy comes to mind. There are heaps of tutorials on that, for example

Call Unmanaged Code. Part 1 - Simple DLLImport

So I quickly come up with the first version:

This is the 'wrapper' class

public class cnOCRsdk
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RECO_DATA{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=200)]
public string FirstName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 200)]
public string Surname;
}

[DllImport(@"cnOCRsdk.dll", EntryPoint="recoCHN_P_Name")]
public static extern bool recoCHN_P_Name(byte[] imgPath, RECO_DATA o_data);
}

This is the call to the function in the wrapper class

cnOCRsdk.RECO_DATA recoData = new cnOCRsdk.RECO_DATA();

string path = @"C:\WINDOWS\twain_32\twainrgb.bmp";

System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] bytes = encoding.GetBytes(path);

bool res = cnOCRsdk.recoCHN_P_Name(bytes, recoData);

Unfortunately, this gives me a "Unable to find an entry point named 'recoCHN_P_Name' in DLL 'cnOCRsdk.dll'." error. My first guess was that I am converting types from C++ to C# incorrectly. I asked for some help

Unable to find an entry point named [function] in dll

I got a suggestion to use the 'dumpbin' tool

DUMPBIN

and also a link to the page that explained C++ name mangling

C++ Name Mangling/Demangling

my function had a mangled name of '?recoCHN_P_Name@CcnOCRsdk@@QAE_NPADPAURECO_DATA@@@Z', but how did that help me?

Anyway, I came across another article on calling C++ from C#

Inheriting From a Native C++ Class in C#

and noticed an interesting thing in it:

[DllImport("cppexp.dll", EntryPoint = "?M1@CSimpleClass@@QAEXXZ", CallingConvention = CallingConvention.ThisCall)]
private static extern void _M1(__CSimpleClass* ths);

this guy is using the mangled function name as an entry point! Why shouldn't I try this approach?

Long story short, the 'unable to find entry point' error went away and after a few more tweaks I made this work. Unfortunately, the function always returns me false so far, which is probably because all the images I try are 'bad', but at least it does not break anymore.

public class cnOCRsdk
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RECO_DATA
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 200)]
public string FirstName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 200)]
public string Surname;
}

[DllImport(@"cnOCRsdk.dll", EntryPoint="?recoCHN_P_Name@CcnOCRsdk@@QAE_NPADPAURECO_DATA@@@Z")]
public static extern bool recoCHN_P_Name(ref string imgPath, ref RECO_DATA o_data);
}
cnOCRsdk.RECO_DATA recoData = new cnOCRsdk.RECO_DATA();
recoData.FirstName = new string(new char[200]);
recoData.Surname = new string(new char[200]);

string path = @"C:\WINDOWS\twain_32\twainrgb.bmp";

bool res = cnOCRsdk.recoCHN_P_Name(ref path, ref recoData);
by . Also posted on my website

Sunday, March 15, 2009

Image file handle adventures.

Today I had an issue with not being able to delete a file programmatically.
While looking for the reason the file was 'locked' (the actual error message was "The process cannot access the file 'file.jpg' because it is being used by another process") I discovered a tool which can help finding out the process which is locking the file.

Process Explorer

Anyway, here is how the image was processed by the application:

Bitmap b = (Bitmap)Image.FromFile(_Filepath);
pictureBox1.Image = b;

// later in the code

System.IO.MemoryStream ms = new System.IO.MemoryStream();
pictureBox1.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);

// even later in the code

File.Delete(_Filepath);

At the point where File.Delete() was called, the handle to the file existed (under some circumstances).

Here is how I implemented the fix initially:

public Bitmap getBitmapFromFile(string filename)
{
Image i = null;
using (Stream s = new FileStream(filename, FileMode.Open))
{
i = Image.FromStream(s);
s.Close();
}
return (Bitmap)i;
}

pictureBox1.Image = getBitmapFromFile(_Filepath);

// later in the code

System.IO.MemoryStream ms = new System.IO.MemoryStream();
pictureBox1.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);

// even later in the code

File.Delete(_Filepath);

The approach did not work, however, this time returning me the error "A generic error occurred in GDI+" at the line where I tried to save the image.

Here is the reason why this happened:

Bitmap and Image constructor dependencies

In my case, obviously, "Additionally, if the stream was destroyed during the life of the Bitmap object, you cannot successfully access an image that was based on a stream" - see how I tried to close a stream, trying to release a handle to the file this way?

The possible solutions can be found here:

Understanding "A generic error occurred in GDI+." Error

That's how I fixed my problem eventually:

public Bitmap getBitmapFromFile(string filePath)
{
Image img = Image.FromFile(filePath);
Bitmap bmp = img as Bitmap;
Graphics g = Graphics.FromImage(bmp);
Bitmap bmpNew = new Bitmap(bmp);
g.DrawImage(bmpNew, new Point(0, 0));
g.Dispose();
bmp.Dispose();
img.Dispose();
return bmpNew;
}

pictureBox1.Image = Syco.Common.Util.getBitmapFromFile(_Filepath);

// later in the code

System.IO.MemoryStream ms = new System.IO.MemoryStream();
pictureBox1.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);

// even later in the code

File.Delete(_Filepath);

Now the image is saved properly and no handle is held against the file, so it is deleted properly too.

by . Also posted on my website

Saturday, March 14, 2009

Moving Ahead of Technology.

Remember that Windows Service I wrote not long ago? Well, today it was time to install it into actual testing environment. That did not work exactly as expected. The first thing I get when trying to run the setup project was this error message



Did not take long to find out that the service was developed in VS.NET 2008 and required .NET Framework 3.5 to run. Now, what do you think would take longer in a large company, to rebuild the application in the previous version of VS.NET or obtaining the permission to install .NET Framework on a server? That's what I did ...

Therefore, moving fast and using the 'latest tools' might actually get you into some trouble...

by . Also posted on my website

Friday, March 6, 2009

A Twisted Code Snippet.

The application is able to save images for an ID. When the changes are being saved, the comma-separated list of file names has to be inserted into the XML file. Easy. This small snippet have been sitting in the production code for about two years, until someone had to attach more than 2 images to a single ID.

(Easy to see that the logic works for one or two files, forming a string of '1.jpg,2.jpg'. If more files are to be saved, the string will look like 1.jpg,2.jpg3.jpg4.jpg' and all images except the first one will be lost).

id += "";

bool firstImage = true;
foreach (IDImage image in idType.IDImages)
{
id += image.ImageFilename;
if (firstImage)
{
firstImage = false;
id += ",";
}
}
id += "
";

I like bugs that are easy to fix and it looks like magic to a unsuspecting observer that a bug is fixed in a with a few keystrokes. How did the original developer come up with the idea, though, and why did it pass testing ...

id += "";

foreach (IDImage image in idType.IDImages)
{
id += image.ImageFilename;

//if not last image, add ','
if (idType.IDImages.IndexOf(image) < idType.IDImages.Count - 1)
{
id += ",";
}
}
id += "
";
by . Also posted on my website

C++ Runtime Libraries Adventures

Almost no day passes without one of those "WHAT is happening?" moments. Yesterday there were 3 that happened to me. One required to delete temporary Internet Explorer files. Of course, I was stupid enough to require a colleague's advice on that. Another one, ironically, was caused by that same colleague adding a line of code in the wrong place. This one I fixed myself.

Now the third one was a bit more interesting and looks like a classic "worked on my machine!" situatuion.
Remember the application that uses the sophisticated scanner? Well, now it also uses a webcam. I was preparing the new version to ship. I build the installation package and installed the application on the test computer. When I try to use the webcam, I get our 'generic' error message, that can be caused by almost anything. But it worked on my machine, I swear!

Anyway, there is a way to find out what really happened - Event Log.

So, something is wrong with the webcam library QuickCamLib.dll. First thing that comes to mind - somehow not registered during installation process? Possible or not? Go go, regsvr32

Oh well, this is not my application problem. But what is wrong? The webcam drivers were installed and I can actually make webcam work (outside of the application).
Fortunately, I'm not the first one to have this problem.
This application has failed to start because the application configuration is incorrect

The thread suggests to look in the System part of the Event Log for a 'side by side' error - and there it is!

The thread also contains the solution, which is somewhat complicated but precise, except in my case I have '90' instead of '80'.

"You need to copy CRT DLL into your application local folder together with the manifest. Please take a look on this post on my blog, http://blogs.msdn.com/nikolad/archive/2005/03/18/398720.aspx. Basically go to windows\winsxs folder. Find a folder like x86_Microsoft.VC80.CRT and copy DLL from there to your application local folder. From what I see in your code you need msvcrt80.dll and msvcp80.dll (perhaps msvcrt80d.dll and msvcp80d.dll if this is Debug mode application). Then go to windows\winsxs\manifests folder and copy x86_*_Microsoft.VC80.CRT*.manifest to Microsoft.VC80.CRT.manifest to your application local folder."

I copy the 4 files msvcm90.dll, msvcp90.dll, msvcr90.dll and Microsoft.VC90.CRT.manifest into my application folder on the test computer, and it works like a charm. I add these files to the installation package, reinstall the application and it works again. All is well that ends well I guess.

by . Also posted on my website