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);
}