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

No comments: