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?
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);
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
}
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.
Related
This code gets different scaling depending on which computer I run it on.
Metafile image;
IntPtr dib;
var memoryHdc = Win32Utils.CreateMemoryHdc(IntPtr.Zero, 1, 1, out dib);
try
{
image = new Metafile(memoryHdc, EmfType.EmfOnly);
using (var g = Graphics.FromImage(image))
{
Render(g, html, left, top, maxWidth, cssData, stylesheetLoad, imageLoad);
}
}
finally
{
Win32Utils.ReleaseMemoryHdc(memoryHdc, dib);
}
Going into the Render method, the Metafile object has a PixelFormat of DontCare and consequently does not have valid vertical or horizontal resolutions.
Coming out of the Render method, it has a value of Format32bppRgb and PhysicalDimension.Width and PhysicalDimension.Height have increased to accommodate the rendered image.
How can I make scaling independent of local settings?
Here's the implementation of CreateMemoryHdc (I didn't write it, it's from an OSS library).
public static IntPtr CreateMemoryHdc(IntPtr hdc, int width, int height, out IntPtr dib)
{
// Create a memory DC so we can work off-screen
IntPtr memoryHdc = CreateCompatibleDC(hdc);
SetBkMode(memoryHdc, 1);
// Create a device-independent bitmap and select it into our DC
var info = new BitMapInfo();
info.biSize = Marshal.SizeOf(info);
info.biWidth = width;
info.biHeight = -height;
info.biPlanes = 1;
info.biBitCount = 32;
info.biCompression = 0; // BI_RGB
IntPtr ppvBits;
dib = CreateDIBSection(hdc, ref info, 0, out ppvBits, IntPtr.Zero, 0);
SelectObject(memoryHdc, dib);
return memoryHdc;
}
As you can see, the width, height and bit depth passed to the DC constructor are constant. Creating the metafile produces different physical dimensions. Right after executing this
image = new Metafile(memoryHdc, EmfType.EmfOnly);
the metafile has PhysicalDimension.Height (and width) of 26.43 on my workstation and 31.25 on the server to which I am deploying, so the difference in scaling is already evident and therefore probably not a consequence of anything in the rendering.
This may be relevant. BitMapInfo is defined in the OSS library and looks like this:
internal struct BitMapInfo
{
public int biSize;
public int biWidth;
public int biHeight;
public short biPlanes;
public short biBitCount;
public int biCompression;
public int biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public int biClrUsed;
public int biClrImportant;
public byte bmiColors_rgbBlue;
public byte bmiColors_rgbGreen;
public byte bmiColors_rgbRed;
public byte bmiColors_rgbReserved;
}
so possibly setting biXPelsPerMeter and biYPelsPerMeter will help. The above code doesn't set them and may be allowing platform values.
Unfortunately, setting these values doesn't seem to make any difference. msdn says
biXPelsPerMeter
The horizontal resolution, in pixels-per-meter, of the
target device for the bitmap. An application can use this value to
select a bitmap from a resource group that best matches the
characteristics of the current device.
So these settings are used when loading a bitmap from a resource. No help here.
This all looks pertinent https://www.codeproject.com/articles/177394/%2fArticles%2f177394%2fWorking-with-Metafile-Images-in-NET
It may help to know that this code does not run in an application. It renders HTML as a metafile for printing, and it lives inside a Web API webservice.
There is no user interface so I'm not sure how to interpret the question of whether it is DPI Aware. The evidence suggests it's DPI affected so the question may be pertinent.
GDI doesn't scale. Use GDI+ for device independence. You will lose antialiasing but most print devices are high DPI anyway.
Does the library in use have an option to use GDI+ instead?
(In my own case, yes. Problem solved.)
I'm currently working on a C# compatible DLL for Box2D, and am trying to make two separate methods - one for creating a Shape and another for creating a Fixture.
The fixture needs to be given a shape in order to be initialised, so once the Shape is created in its method, it is cast to a void pointer and sent back to C# to be stored as an IntPtr. Then this is passed to the Fixture creation method when required and converted back into a body from a pointer. The code I'm using is this:
extern "C" __declspec(dllexport) void* CreateBoxShape(float width, float height,
float centreX, float centreY, float angle) {
b2Vec2 centre = b2Vec2(centreX, centreY);
b2PolygonShape* shape;
shape->SetAsBox(width, height, centre, angle);
return static_cast<void*>(shape);
}
extern "C" __declspec(dllexport) void* AddFixture(void* bodyPointer, void* shapePointer, float density) {
b2Body* m_body = static_cast<b2Body*>(bodyPointer);
b2Fixture* m_fixture;
b2PolygonShape* aShape = static_cast<b2PolygonShape*>(shapePointer);
b2PolygonShape shape = *aShape;
m_fixture = m_body->CreateFixture(&shape, density);
return static_cast<void*>(m_fixture);
}
As you can guess, it's not working. The shape is getting modified somewhere along the way. I'm not used to working with void pointers or static_casts so I'd appreciate any help/suggestions. The constructor for CreateFixture is:
b2Fixture * CreateFixture (const b2Shape *shape, float32 density)
I'm using an unmanaged library, which generates grayscale images (about 100x200 pixels, more or less). An image is contained within a struct, which looks like this in C:
typedef struct abs_image {
ABS_DWORD Width;
ABS_DWORD Height;
ABS_DWORD ColorCount;
ABS_DWORD HorizontalDPI;
ABS_DWORD VerticalDPI;
ABS_BYTE ImageData[ABS_VARLEN];
} ABS_IMAGE
typedef unsigned int ABS_DWORD;
typedef unsigned char ABS_BYTE;
And here my C# wrapper struct:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ABS_IMAGE {
public uint Width;
public uint Height;
public uint ColorCount;
public uint HorizontalDPI;
public uint VerticalDPI;
public IntPtr ImageData;
}
Grabbing the image and marshallign the ABS_IMAGE struct works just fine. In a previous version, I tried using a byte array with fixed length for ImageData, which crashed sometimes. This happened, I think, because the image size is not fix.
Now I try to read the image byte array at later time, when I can calulate the real array length before. Here the relevant code:
ABS_Type_Defs.ABS_IMAGE img =
(ABS_Type_Defs.ABS_IMAGE)Marshal.PtrToStructure(
pImage,
typeof(ABS_Type_Defs.ABS_IMAGE));
int length = ((int)img.Height - 1) * ((int)img.Width - 1);
byte[] data = new byte[length];
Marshal.Copy(img.ImageData, data, 0, length);
Now my problem: Every time I want to execute Marshal.Copy to read the image bytes, I get an AccessViolationException.
Does anyone have an idea?
This is what is happening. Your struct is what is known as a variable length struct. The pixel data is containined inline in the struct, starting at the offset to ImageData.
typedef struct abs_image {
ABS_DWORD Width;
ABS_DWORD Height;
ABS_DWORD ColorCount;
ABS_DWORD HorizontalDPI;
ABS_DWORD VerticalDPI;
ABS_BYTE ImageData[ABS_VARLEN];
} ABS_IMAGE
Your API returns pImage which is an IntPtr that points to unmanaged data of type ABS_IMAGE. However, if you look at the native code then you will see that ABS_VARLEN is equal to 1. This is because the struct has to be defined statically at compile time. In reality the pixel data will have length determined by the height, width and colour count fields.
You can carry on using Marshal.PtrToStructure to get at most of the fields. But you can't get at the ImageData field that way. That's going to need a little more work.
Declare the struct like this instead:
[StructLayout(LayoutKind.Sequential)]
public struct ABS_IMAGE {
public uint Width;
public uint Height;
public uint ColorCount;
public uint HorizontalDPI;
public uint VerticalDPI;
public byte ImageData;
}
When you need to get the image data do this:
IntPtr ImageData = pImage + Marshal.OffsetOf(typeof(ABS_IMAGE), "ImageData");
Marshal.Copy(ImageData, data, 0, length);
If you are not yet on .net 4 then you need casting to make the arithmetic compile:
IntPtr ImageData = (IntPtr) (pImage.ToInt64() +
Marshal.OffsetOf(typeof(ABS_IMAGE), "ImageData").ToInt64());
Finally, I think you are calculating length incorrectly. Surely you need to use Height*Width. Also you have not accounted for the color depth. For example, 32 bit color will be 4 bytes per pixel.
How can I upload pixels from a simple byte array to an OpenGl texture ?
I'm using glTexImage2D and all I get is a white rectangle instead of a pixelated texture. The 9th parameter (32-bit pointer to the pixel data) is IMO the problem. I tried lots of parameter types there (byte, ref byte, byte[], ref byte[], int & IntPtr + Marshall, out byte, out byte[], byte*). glGetError() always returns GL_NO_ERROR. There must be something I'm doing wrong because it's never some gibberish pixels. It's always white. glGenTextures works correct. The first id has the value 1 like always in OpenGL. And I draw colored lines without any problem. So something is wrong with my texturing. I'm in control of the DllImport. So I can change the parameter types if necessary.
GL.glBindTexture(GL.GL_TEXTURE_2D, id);
int w = 4;
int h = 4;
byte[] bytes = new byte[w * h * 4];
for (int i = 0; i < bytes.Length; i++)
bytes[i] = (byte)Utils.random(256);
GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, w, h, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, bytes);
[DllImport(GL_LIBRARY)] public static extern void glTexImage2D(uint what, int level, int internalFormat, int width, int height, int border, int format,
int type, byte[] bytes);
A common mistake is not change the MIN filter, since the default is mipmapped, which makes textures incomplete. Do this:
GL.glBindTexture(GL_TEXTURE_2D, id);
GL.glTexParameteri(GL_TEXTURE_2D, GL_MIN_FILTER, GL_NEAREST);
Then draw the texture.
A texture remaining white despite something has been uploaded is a indicator for either not all mipmap levels being uploaded properly, or filter settings not set correctly.
Of interest is then
glTexParameteri(GL_TEXTURE_..., GL_MIN_FILTER, GL_...);
With GL_NEAREST or GL_LINEAR to disable mipmapping. Mipmapping is enabled by default.
The other important thing is to set the structure of the data before uploading, i.e. calling glTexImage. For this you use the function glPixelStorei to set the GL_UNPACK_... parameters. You need to set things like alignment, stride, and so on. I refer you to the documentation.
Your P/Invoke declaration is wrong.
The short answer is:
[System.Runtime.InteropServices.DllImport(Library, EntryPoint = "glTexImage2D", ExactSpelling = true)]
internal extern static void glTexImage2D(int target, int level, int internalformat, Int32 width, Int32 height, int border, int format, int type, IntPtr pixels);
This P/Invoke declaration is a safe one (not using directly pointers, but IntPtr).
The problem is the .NET memory management. Memory blocks are not fixed on a certain memory address: the garbage collector (GC) is free to move memory everywhere (for example, it can move a stack allocated memory in the heap space and viceversa).
Indeed, what you need is to tell to the .NET GC that the memory shall not moved. To do so, you shall use the fixed statement or other garbage collector related method.
For example:
public static void TexImage2D(int target, int level, int internalformat, Int32 width, Int32 height, int border, int format, int type, object pixels) {
GCHandle pp_pixels = GCHandle.Alloc(pixels, GCHandleType.Pinned);
try {
if (Delegates.pglTexImage2D != null)
Delegates.pglTexImage2D(target, level, internalformat, width, height, border, format, type, pp_pixels.AddrOfPinnedObject());
else
throw new InvalidOperationException("binding point TexImage2D cannot be found");
} finally {
pp_pixels.Free();
}
}
The object parameter of the TexImage2D function is meant to be used with any array of data (those objects implementing the Array class (that is, byte[], short[], int[] and so on).
Essentially the code above tell to the GC: take the address of pixels, and don't move it untill I call Free() on the memory handle.
Usin the fixed statement is another option, but requires an unsafe P/Invoke declaration and it's a little more verbose to use it in the code (for each call you have to define an unsafe and fixed statements).
I'm actually translating some C++ code (which I know very little about, and have never really used) to C#. Normally in C# I wouldn't find myself doing something like this, as it does seem a little odd, but with the way the code in C++ is setup, I find it hard not to do it this way. Admittedly, I'm not very experienced with programming at all, but for the amount of time I've been doing it I've been able to grasp the concepts fairly well.
Anyway, here's the C++ code. It's in a header file, too.
#ifndef _SPRITE_H_
#define _SPRITE_H_
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifndef NULL
#define NULL ((void *) 0)
#endif
typedef struct {
unsigned char *data;
int len;
int width;
int height;
} SpriteImage;
typedef struct {
unsigned char b;
unsigned char g;
unsigned char r;
unsigned char unused;
} SpritePalette;
typedef struct {
char *filename;
unsigned int nimages;
SpriteImage *images;
unsigned int palette_size;
SpritePalette *palette;
} Sprite;
typedef enum {
/* Developer errors */
SE_BADARGS,
/* sprite_new() errors */
SE_CANTOPEN,
SE_INVALID,
/* sprite_to_bmp(), sprite_to_bmp_file() and sprite_to_rgb() errors */
SE_INDEX,
/* sprite_to_bmp_file() errors */
SE_CANTWRITE
} SpriteError;
//Funcion para hacer uso de reverse_palette desde el exterior
SpritePalette * get_pal(SpritePalette *palette,int palette_len);
/* Open sprite file */
Sprite *sprite_open (const char *fname, SpriteError *error);
Sprite *sprite_open_from_data (const unsigned char *data, unsigned int size, SpriteError *error);
/* Change palette of sprite*/
void change_palete(Sprite *sprite, const char *fname, SpriteError *error);
/* Converts a sprite to bitmap file in memory */
void *sprite_to_bmp (Sprite *sprite, int i, int *size, SpriteError *error);
/* Like sprite_to_bmp(), but saves the result to a file */
int sprite_to_bmp_file (Sprite *sprite, int i, const char *writeToFile, SpriteError *error);
/* Converts a sprite to raw RGB data. The rowstride/pitch is 3*width. */
void *sprite_to_rgb (Sprite *sprite, int i, int *size, SpriteError *error);
/* Frees a Sprite* pointer */
void sprite_free (Sprite *sprite);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _SPRITE_H_ */
By the way, does anyone know what the deal is with the '#' reference?
I have no idea what these refer to.
And here's the C#:
interface Sprite
{
public class SpriteImage
{
private byte *data;
private int length;
private int width;
private int height;
}
public class SpritePalette
{
byte b;
byte g;
byte r;
byte unused;
}
public class Sprite
{
string fileName;
uint nImages;
uint palette_size;
SpriteImage image;
SpritePalette palette;
}
public enum SpriteErrors
{
None, //--default value
BadArguments, //--dev errors
/*--errors derived from any instance/call of the NewSprite() method */
CantOpen,
Invalid,
/*SpriteToBMP(), SpriteToBMPFile(), and SpriteToRGB() errors*/
Index,
CantWrite //--SpriteToBMPFile() errors
}
public interface ISprite
{
SpritePalette GetPalette(SpritePalette palette, int paletteLength);
Sprite SpriteOpen(string firstName, SpriteErrors* error);
Sprite SpriteOpenFromData(byte* data, uint size, SpriteErrors* error);
}
I'm sure you can connect the dots here. Keep in mind that this isn't my code, obviously, so I don't really know much about it. If anyone needs anymore material though I'd be happy to provide it if necessary.
a couple of points:
1) there is your types shouldn't be inside of an interface
2) pointers should either be converted to memebers as you hve in the sprite class or to arrays as you should have in the SpriteImage struct
3) unless its a trivial port it is going to be very difficult to write without having a good understanding of both languages and the code to be ported
You appear to be trying to port this SourceForge project from C++ to C#:
Ragnarok Online Sprite Viewer
That viewer is not only written in C++ but is also based on the Qt Toolkit.
I know your question is about this translating this particular header file from C++ to C# and what is the best approach, but my opinion is that unless you are very comfortable with C++ and are willing to learn a lot about Qt, your chances of success at this porting project are not very good. This is a big project even for a programmer seasoned in both C++ and C#.
However, if you still want to do this thing, then the approach you should take is to create a single large SpriteUtility static class and put all of the free C++ functions you find into that class as static C# methods. Yes, you can also put the C++ structs you see as nested classes. You don't need any interfaces whatsoever.
It doesn't need to be beautiful C# code; you are trying to port it verbatim doing as little damage to it as possible. Once it is working you can refactor it to make it more object-oriented in the traditional C# style.