4

I have a C++ DLL with unmanaged code and a C# UI. There's a function imported from C++ DLL that takes a written-by-me struct as parameter.

After marshalling the written-by-me struct (MyImage) from C# to C++ I can access the content of the int[] array inside of it, but the content is different. I do not know what I am missing here as I spent quite some time and tried a few tricks to resolve this (obviously not enough).

MyImage struct in C#:

[StructLayout(LayoutKind.Sequential)]
struct MyImage
{
    public int width;
    public int height;
    public int[] bits; //these represent colors of image - 4 bytes for each pixel
}

MyImage struct in C++:

struct MyImage
{
    int width;
    int height;
    Color* bits; //typedef unsigned int Color;

    MyImage(int w, int h)
    {
         bits = new Color[w*h];
    }

    Color GetPixel(int x, int y)
    {
          if (x or y out of image bounds) return UNDEFINED_COLOR;
          return bits[y*width+x];
    }
}

C# function declaration with MyImage as parameter:

[DLLImport("G_DLL.dll")]
public static extern void DisplayImageInPolygon(Point[] p, int n, MyImage texture, 
                                                  int tex_x0, int tex_y0);

C++ implementation

DLLEXPORT void __stdcall DisplayImageInPolygon(Point *p, int n, MyImage img,
                                               int imgx0, int imgy0)
{
    //And below they have improper values (i don't know where they come from)
    Color test1 = img.GetPixel(0,0);
    Color test2 = img.GetPixel(1,0);
}

So when debugging the problem I noticed that the MyImage.bits array in c++ struct holds different data.

How can I fix it?

3 Answers 3

4

Since the bits field is a pointer to memory allocated in the native code, you are going to need to declare it as IntPtr in the C# code.

struct MyImage
{
    public int width;
    public int height;
    public IntPtr bits;
}

If you want to access individual pixels in the C# code you'll need to write a GetPixel method, just as you did in the C++ code.

Note that since the bits field is a pointer to memory allocated in the native code, I'd expect the actual code to have a destructor for the struct that calls delete[] bits. Otherwise your code will leak.

This also means that you are going to need to create and destroy instances in the native code, and never do so in the managed code. Is this the policy you currently follow? I suspect not based on the code that I can see here.

You also need to reconsider passing the struct by value. Do you really want to take a copy of it when you call that function? Doing so means you've got two instances of the struct whose bits fields both point to the same memory. But, which one owns that memory? This structure really needs to be passed by reference.

I think you've got some problems in your design, but I can't see enough of the code, or know enough about your problem to be able to give you concrete advice.


In comments you state that your main goal is to transfer these bits from your C# code to the C++ code. I suggest you do it like this:

MyImage* NewImage(int w, int h, Color* bits)
{
    MyImage* img = new MyImage;
    img->w = w;
    img->h = h;
    img->bits = new Color[w*h];
    for (int i=0; i<w*h; i++)
        img->bits[i] = bits[i];
    return img;
}

void DeleteImage(MyImage* img)
{
    delete[] img->bits;
    delete img;
}

void DoSomethingWithImage(MyImage* img)
{
    // do whatever it is you need to do
}

On the C# side you can declare it like this:

[DllImport(@"dllname.dll", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr NewImage(int w, int h, int[] bits);

[DllImport(@"dllname.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void DeleteImage(ImtPtr img);

[DllImport(@"dllname.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void DoSomethingWithImage(ImtPtr img);
Sign up to request clarification or add additional context in comments.

4 Comments

Is it possible to create an IntPtr to int[] array without unsafe code? This structure has only 1 purpose of existence -> to get an image over to c++ layer where I can work on it. Are you saying i should just pass the entire structure in an IntPtr?
In that case I would not declare the struct in C# at all. Just call a function in the native code that passes the data, and creates the native struct.
What you said worked for me. 1 more question: do i see it right, that the int[] array gets marshalled to int* without problems when passed as function parameter, and this does not work when the array is passed inside of structure as int[]? Can't we force the program to treat this array inside of struct as if it was gonna be passed as parameter to function?
I think you get it right. It's plausible that you could add an attribute to the field that would marshal it as pointer to first element. ButI don't think that would be useful to you. Because that way you have the struct that you worked with in the native code operating on managed memory. So you'd have to pin that memory. It would get messy. Taking a copy as per the code in my answer keeps the lines of responsibility cleaner.
-1

The first thing you should try is declaring your C# code with unsigned int types as well. It is possible that one bit is being interpreted as a sign for your int.

So in C# something like this (just note the bits is now uint[]):

[StructLayout(LayoutKind.Sequential)]
struct MyImage
{
    public int width;
    public int height;
    public uint[] bits; //these represent colors of image - 4 bytes for each pixel
}

Comments

-1

You can use the PInvoke Interop Assistant. You simply paste your struct and function declaration and it will generate the C# code for you. It has helped me a lot quite a few times.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.