Tuesday, June 06, 2006

C# Interop: unsigned char* C .lib

I ran into a situation where there was some C code in a .lib file that I wanted to access from C#. don't try this unless you have no other choice. ideally, you'll use a DLL instead of .LIB file.

I've created a separate post about the process of creating a wrapper: Calling a C++ lib from C#.

most of the .Net Interop examples don't include pointers being passed as out parameters, so it took me some time to work out the exact syntax for accessing the functions that I needed. hopefully, the next person who needs to do this will find this post.

the solution was to use a C++ wrapper DLL (create a C++ Win32 DLL Project from VS.Net - remember to export symbols - http://www.codersource.net/win32_dlls.html) and call the wrapped functions from C#.

you'll save yourself some hassle if you follow the steps in this tutorial for calling a sample function from a wrapper: http://www.codersource.net/win32_dlls.html.

note: I changed one project setting to get it to compile. I changed the general project configuration setting "Use of MFC" to "Use MFC in a shared DLL."

here is some sample code:

C .lib
-------------------
//create() will fill an unsigned char array with 100*scale*scale bytes
//this function will malloc room if image is NULL
//returns -1 if problem, 0 otherwise

int create(int id, int scale, unsigned char *image);

C++ Wrapper .cpp
-------------------
WRAPPER_API int create_wrapped(int id, int scale, void** image)
{
int retCode = -1;
unsigned char* ptrImage = NULL;
if ( NULL != *image )
{
ptrImage = (unsigned char*)GlobalLock(*image);
retCode = create(id, scale, ptrImage);
GlobalUnlock(*image);
}
else
{
retCode = create(id, scale, ptrImage);
*image = (void*)ptrImage;
}
return retCode;
}

C++ Wrapper .def
-------------------
LIBRARY Wrapper
; Wrapper.def : Declares the module parameters for the DLL.
DESCRIPTION 'Wrapper Windows Dynamic Link Library'
EXPORTS
; Explicit exports can go here
; Exporting the .lib functions
init_wrapped @1
close_wrapped @2
create_wrapped @3

C++ Wrapper.h
-------------------
// Add reference to library file
#pragma comment(lib, "library.lib")

C# Accessing the wrapper function
-------------------
// create
[DllImport("Wrapper.dll", EntryPoint="create_wrapped")]public static extern int create_wrapped(int id, int scale, [In][Out] ref IntPtr image);

C# Using the data
-------------------
// Allocate unmanaged memory - must be freed with Marshal.FreeHGlobal(ptrImage)
IntPtr ptrImage = Marshal.AllocHGlobal(scale * scale * 100);
// Copy the unmanaged bytes to a managed array
try
{
// Get a pointer to an unmanaged byte array which contains the image
// returns 0 for success
label1.Text = create_wrapped(0, 5, ref Image).ToString();
byte[] ManagedArray = new byte[scale * scale * 100];
// Copy pointer into managed array
Marshal.Copy(Image, ManagedArray, 0, ManagedArray.Length);
// custom function to Write the image out to a file
writeImageToFile(ManagedArray);
}
catch (ArgumentNullException ex)
{
MessageBox.Show(ex.Message, "Marshal Copy Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
finally
{
// Free the unmanaged memory
Marshal.FreeHGlobal(Image);
}

8 comments:

Jef said...

Hello
I have a situation more or less like your's
i have a dll writen in C which defines the function : signed int anticollision (unsigned char* anticollisionData, int* lenght);

I dont get how this wrapper DLL u created, calls the function in the lib or dll file. you dont say anything about wich .lib file or .dll file to use.
I'm really a noob in programming (not totaly but compared to most users here i am).

would this be the correct way to make the cpp?
=================================
wrapper.cpp
=================================
WRAPPER_API signed int anticollision_wrapped (void** anticollisionData, void** lenght)
{
signed int retCode = -1;
unsigned char* ptranticollision = NULL;
int* ptrlenght = NULL;
if (NULL != *anticollisionData && NULL != *lenght)
{
ptranticollision = (unsigned char*)GlobalLock(*anticollisionData);
ptrlenght = (int*)GlobalLock(*lenght);
retCode = anticollision(anticollisionData, lenght);
GlobalUnlock(*anticollisionData);
GlobalUnlock(*lenght);
}
else
{
if (NULL != anticollisionData)
{
ptranticollision = (unsigned char*)GlobalLock(*anticollisionData);
retCode = anticollision(anticollisionData, lenght);
GlobalUnlock(*anticollisionData)
}
else
{
if (NULL != lenght)
{
ptrlenght = (int*)GlobalLock(*lenght);
retCode = anticollision(anticollisionData, lenght);
GlobalUnlock(*lenght);
}
else
{
retCode = anticollision(anticollisionData, lenght);
*anticollisionData = (void*)ptranticollision;
*lenght = (void*)ptrlenght;
}
}
}
return retCode;

}

============================
Any help on this subject would be appreciated thx in advance

Stephen Cawood said...

jef, you'll see at the top of the post that this topic applies to accessing a function in a .lib file. if you already have a .dll, then you don't need a wrapper. try to google accessing a C .dll from the language that you're trying to use (I assume it's c# since you found my blog).

but to answer your first question... the reason that I don't mention a specific .lib, or .dll file in my post is that the post only addresses what to do if you have a .lib that you need to wrap in a .dll. therefore the .lib is the file that you have and the .dll is the file that you need to create.

Jef said...

ah ok :-S i had the same problem though as you had. Accesing an unsigned char * used by a dll (for your problem a .lib) in a C# programm. I must have misunderstood some of your english (since im not native english speaking) i thought a dll could be wrapped in a new dll and so use the unsigned char*. (I have a sdk package with a dll programmed in C from which i dont posses the source) I'll google some answers and reply here so more people having the same problem might find their solution easier.
Thx for the blog and rapid answer anyhow.
Greets

Stephen Cawood said...

yes, your question is a standard interop problem, what you want to do is marshal an unsigned char* into your program (IntPtr for C#).

take another look at the "C# Using the data" section of my post. that's how I handled the unsigned char* data type in C#. you will want to do something like that.

I found that the .Net Interop newsgroups were useful. if you're using .Net, you can go there for help.

Jef said...

Hey good news :-) I found a .lib file which probably can be used in the same way as the dll file.
But since i'm really noobish in programming, i'm going to ask for some help:)

First of all:
When I call a function in the .cpp(by example: "create(id, scale, ptrImage);" in your code. i get an error saying 'create': identifier not found.
It means that the function "create" im trying to call, is not recognised. Where should i place the identifier? Or where should I tell the compiler he needs to use the function from the lib file?

Another thing is it normal the .lib file is somewhat machine language.
IC_getSpecialData@12__imp__LEGIC_
getSpecialData@12__imp__wait4Credential@8
with next to it hexadecimal numbers corresponding to the functions on the right

again TY verry much

Stephen Cawood said...

jef, you don't want to use the .lib -- use the .dll. if you use the .lib you are literally adding work.

in other words, don't use a wrapper unless you have to. you would be adding an unnecessary layer to your app. even if you use the .lib, you still have to marshal the data in your code. use the .dll and figure out the marshaling.

Jef said...

Ok, I think i get it now.
the wrapper dll i would make would be the same as the one in my possesion
google is the answer :-)

thx for your answers and your patience. Would you like me to report back my progress?

Stephen Cawood said...

jef, yes that would be good. please let me know how you solve it and include any helpful links.