Groups | Blog | Home
all groups > dotnet interop > may 2006 >

dotnet interop : C# question: dealing with unsigned char* from C++ .lib


Stephen Cawood
5/31/2006 1:57:57 AM
I'm trying to access a C++ .lib from C#.
I have a working wrapper DLL (I can get back simple things like int), but
I'm having issues dealing with complex data types.

For example, the .lib contains this function:

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

In the wrapper DLL I have this function:

WIN32DLL_API int create_wrapped(int id, int scale, unsigned char *image)
{
return create(id, scale, image);
}

I also tried to return the unsigned char* like this:

WIN32DLL_API int create_wrapped_returnimage(int id, int scale, unsigned char
*image)
{
create(id, scale, image);
return image;
}

In C#, I've tried to access the function like this:

[DllImport("Win32DLL.dll",EntryPoint="create_wrapped_returnimage")]
public static extern IntPtr create_wrapped_returnimage(int artag_id, int
scale, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder image);

I've tried various data types to try and deal with the "unsigned char
*image," but I haven't found a solution.
Can someone suggest the best way to deal with this? Should I be converting
the unsigned char* to a different type within the C++ wrapper function? If
so, some sample code would be helpful.

Thanks in advance.


Mattias Sjögren
5/31/2006 10:22:46 PM
Stephen,

[quoted text, click to view]

Is image an input or output parameter? Does it represent text or
binary data?


Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Stephen Cawood
6/1/2006 12:35:24 AM
thanks for the message.
here's some more info...

this code is in my C++ wrapper, it includes a description of the function
and it writes the image out to a file. I was using it to test that the
wrapper was accessing the lib properly. I'd like to be able to do the same
from C#.

//-Call create() to create a bitmap of (10*scale) x (*10*scale) bytes
//create() will fill an unsigned char array with 100*scale*scale bytes
//
//create ID=567, 5 pixels/bit, total image will be 50x50 pixels
//this function will malloc room if image is NULL
//returns -1 if problem, 0 otherwise
//create() will fill an unsigned char array with 100*scale*scale bytes
WIN32DLL_API int create_wrapped(int id, int scale, unsigned char *image)
{
// This code writes the image out to a .PGM file
create(id, scale, image);
char *file_name = "test.pgm";
char *comment = "createimage";
int width = 10*scale;
int height = 10*scale;
FILE *out;
int i,j;
out=(FILE*)fopen(file_name,"wb");
if(out==NULL)
{printf("PGM_FUNCTIONS.C error: Couldn't open %s for
writing\n",file_name);exit(1);}
fprintf(out,"P5\n#%s\n",comment);
fprintf(out,"%d %d\n255\n",width,height);

for(i=0;i<width*height;i++)
{
j=(int)(*(image+i));
fputc(j,out);
}
fclose(out);
return create(id, scale, image);
}


[quoted text, click to view]

Mattias Sjögren
6/1/2006 8:07:44 AM
[quoted text, click to view]

OK so it's an output aray of binary data. Then you should probably
declare the function like this

[DllImport("Win32DLL.dll",EntryPoint="create_wrapped_returnimage")]
public static extern int create_wrapped_returnimage(int artag_id, int
scale, [Out] byte[] image);


Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Stephen Cawood
6/1/2006 3:54:37 PM
thanks for the message. I've tried that, but I get the error "Can not
marshall return type"

this is what I'm trying...


//Call create() to create a bitmap of (10*scale) x (*10*scale) bytes
//create() will fill an unsigned char array with 100*scale*scale bytes

[DllImport("Win32DLL.dll", EntryPoint="create_wrapped_returnimage")]
[return : MarshalAs(UnmanagedType.LPArray)]
public static extern byte[] create_wrapped_returnimage(int id, int scale,
[Out] byte[] image);

then to use it...

//Test with returnImage
byte[] bImage = new byte[2560];
bImage = create_wrapped_returnimage(0,5,bImage);



[quoted text, click to view]

Mattias Sjögren
6/2/2006 12:26:01 AM
Stephen,

[quoted text, click to view]

I don't see the point of returning the pointer in the
create_wrapped_returnimage function. I thought the create_wrapped code
was more appropriate. The array you pass in will be modified directly.
So go with the original create_wrapped and make the return type int on
both sides.

If you want to stick with the create_wrapped_returnimage function, the
managed return type must be an IntPtr, not a byte[].


Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Stephen Cawood
6/2/2006 12:44:06 PM
thanks again for the message. I'm trying this code, but the result is an
array of zeros. I don't seem to be getting the bytes.

wrapper DLL:

WIN32DLL_API int create_wrapped(int id, int scale, unsigned char *image)
{
return create(id, scale, image);
}

test C# form:

[DllImport("Win32DLL.dll", EntryPoint="create_wrapped")]
public static extern int create_wrapped(int id, int scale, [Out] byte[]
image);

byte[] bArray = new byte[2560];
create_wrapped(0,5,bArray);

label1.Text = bArray[0].ToString();

the result is: 0
also, when I look in the debugger, the array is all zeros

BTW - when I check the return value of the function like this...

label1.Text = create_wrapped(0,5,bArray).ToString();

the result is: 0
(which is success)


notes:

/-Call create() to create a bitmap of (10*scale) x (*10*scale) bytes
//create() will fill an unsigned char array with 100*scale*scale bytes
//
//create ID=567, 5 pixels/bit, total image will be 50x50 pixels
//this function will malloc room if image is NULL
//returns -1 if problem, 0 otherwise

[quoted text, click to view]

Stephen Cawood
6/4/2006 10:41:37 PM

it works! wow, this took some time. anyway, here's the winning combination.
thanks again.


C .lib
-------------------
/-Call create() to create a bitmap of (10*scale) x (*10*scale) bytes
//create() will fill an unsigned char array with 100*scale*scale bytes
//
//create ID=567, 5 pixels/bit, total image will be 50x50 pixels
//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
-------------------

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# 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(2500);


// Try to copy the unmanaged bytes to a managed array
try
{
// Get a pointer to an unmanaged byte array which contains the image
label1.Text = create_wrapped(0, 5, ref ptrImage).ToString(); //return 0 for
success
byte[] bManagedArray = new byte[2500];

// Read through entire pointer byte by byte and put into managed array
for (int i = 0; i < bManagedArray.Length; i++)
{
bManagedArray[i] = Marshal.ReadByte(ptrImage, i);
}


// custom function to Write the PGM image out to a file
writeImageToFile(bManagedArray);

}
catch (ArgumentNullException ex)
{
MessageBox.Show(ex.Message, "Marshal Copy Error", MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
finally
{
// Free the unmanaged memory.
Marshal.FreeHGlobal(ptrImage);
}

[quoted text, click to view]

AddThis Social Bookmark Button