4

I'm trying to learn how to write a wrapper around a DLL and I've hit a bit of a road-block. I've got a struct declared as such:

[StructLayout(LayoutKind.Sequential)]
unsafe struct SDL_Surface
{
    public readonly UInt32 flags;             
    public readonly SDL_PixelFormat* format;  
    public readonly int w, h;                  
    public readonly int pitch;               
    public void* pixels;             

    /// <summary>Application data associated with the surface</summary>
    public void* userdata;           

    /// <summary>information needed for surfaces requiring locks</summary>
    public readonly int locked;              
    public readonly void* lock_data;           

    /// <summary>clipping information</summary>
    public readonly SDL_Rect clip_rect;        

    /// <summary>info for fast blit mapping to other surfaces</summary>
    private SDL_BlitMap *map;    // <--- Cannot take the address of, get the size of, or declare a pointer to a managed type 

    /// <summary>Reference count -- used when freeing surface</summary>
    public int refcount;             
}

When I try to compile my project, it gives the above error.

But you will notice above it, I do have a pointer to another struct. I'm trying to figure out what the difference between these two structs is that makes one work but the other doesn't, but I'm not sure; they're both unsafe structs. They are as follows:

[StructLayout(LayoutKind.Sequential)]
unsafe struct SDL_PixelFormat
{
    UInt32 format;
    SDL_Palette *palette;
    byte BitsPerPixel;
    byte BytesPerPixel;
    fixed byte padding [2];
    UInt32 Rmask;
    UInt32 Gmask;
    UInt32 Bmask;
    UInt32 Amask;
    byte Rloss;
    byte Gloss;
    byte Bloss;
    byte Aloss;
    byte Rshift;
    byte Gshift;
    byte Bshift;
    byte Ashift;
    int refcount;
    SDL_PixelFormat *next;
}

unsafe internal delegate int SDL_blit(SDL_Surface* src, SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect);

[StructLayout(LayoutKind.Sequential)]
unsafe struct SDL_BlitMap
{
    SDL_Surface* dst;
    int identity;
    SDL_blit blit;
    void* data;
    SDL_BlitInfo info;

    /* the version count matches the destination; mismatch indicates
       an invalid mapping */
    UInt32 dst_palette_version;
    UInt32 src_palette_version;
}

[StructLayout(LayoutKind.Sequential)]
struct SDL_Rect
{
    int x, y;
    int w, h;
}

So what do I have to change to make this compile?


I believe it's the reference to SDL_blit in SDL_BlitMap that's causing the problem. I've declared it as a delegate; is there something else I should be declaring it as? It's defined as this, in C:

typedef int (*SDL_blit) (struct SDL_Surface * src, SDL_Rect * srcrect,
                         struct SDL_Surface * dst, SDL_Rect * dstrect);
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • Can you show us your declaration for `SDL_Rect`? – Simon Whitehead Jul 08 '13 at 04:55
  • What about `SDL_Surface`? This is something breaking down the chain.. because so far, this compiles for me. – Simon Whitehead Jul 08 '13 at 05:00
  • @SimonWhitehead: Added the rest of the class at the top. – mpen Jul 08 '13 at 05:09
  • Could it be that you don't have "Allow unsafe types" checked? – Cole Tobin Jul 08 '13 at 05:10
  • @ColeJohnson: Is that different than "allow unsafe code"? That is checked. – mpen Jul 08 '13 at 05:29
  • I commented out the reference to `SDL_blit` in `SDL_BlitMap` and the error went away. It doesn't like the delegate... How is one supposed to declare a function-pointer then? – mpen Jul 08 '13 at 05:31
  • 1
    You'll probably need to make sure your delegate has the correct calling convention (`cdecl`). Please see [this question](http://stackoverflow.com/questions/5155180/changing-a-c-sharp-delegates-calling-convention-to-cdecl) for an example of how to do that. – rossipedia Jul 08 '13 at 06:01
  • @Mark: as `IntPtr`. A delegate is not a function pointer. It is possible to marshal one to another (see [GetDelegateForFunctionPointer](http://msdn.microsoft.com/en-us/library/zdx6dyyh.aspx)) but they are completely different things. – Anton Tykhyy Jul 08 '13 at 07:00
  • @rossipedia: Doesn't like it even when I add the `[UnmanagedFunctionPointer]` attribute. – mpen Jul 08 '13 at 07:17

2 Answers2

5

Any struct that contains a managed type cannot have its address taken. Delegates are a reference type, therefore they are also a managed type. This means that an SDL_Blitmap is a managed type because it contains a managed reference to an SDL_blit, thus you cannot get a pointer to it without fixing it first.

If the function you are trying to invoke is already available in the dll, I suggest you have a look at the DllImportAttribute.(http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute(v=vs.100).aspx)

By combining a public static extern declaration with a DllImportAttribute, you can invoke any global function declared in the dll you are interop-ing.

Alternatively you'll need to create a C/C++ side function that takes a function pointer and invokes it, which could get messy.

Pharap
  • 3,826
  • 5
  • 37
  • 51
  • I'm not trying to invoke it though; the struct contains a function-pointer, and I'm trying to figure out how to declare that. I thought declaring it as a delegate would be the way to go, but it doesn't seem happy about that. – mpen Jul 08 '13 at 07:15
  • 1
    In that case, all you can really do is declare it as an int or IntPtr - Anything of the same size to act as padding. – Pharap Jul 08 '13 at 07:31
2

What if you don't rely on unsafe code? It may affect affect performance though if your code is performance/speed critical. Something along the lines of this:

[StructLayout(LayoutKind.Sequential)]
struct SDL_PixelFormat
{
    UInt32 format;
    IntPtr palettePtr;
    byte BitsPerPixel;
    byte BytesPerPixel;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    byte padding[];
    UInt32 Rmask;
    UInt32 Gmask;
    UInt32 Bmask;
    UInt32 Amask;
    byte Rloss;
    byte Gloss;
    byte Bloss;
    byte Aloss;
    byte Rshift;
    byte Gshift;
    byte Bshift;
    byte Ashift;
    int refcount;
    IntPtr nextPtr;
}

[StructLayout(LayoutKind.Sequential)]
struct SDL_Surface
{
    public readonly UInt32 flags;
    public readonly IntPtr format;  
    public readonly int w, h;                  
    public readonly int pitch;               
    public IntPtr pixels;             

    /// <summary>Application data associated with the surface</summary>
    public IntPtr userdata;           

    /// <summary>information needed for surfaces requiring locks</summary>
    public readonly int locked;              
    public readonly IntPtr lock_data;           

    /// <summary>clipping information</summary>
    public readonly SDL_Rect clip_rect;        

    /// <summary>info for fast blit mapping to other surfaces</summary>
    private IntPtr mapPtr;

    /// <summary>Reference count -- used when freeing surface</summary>
    public int refcount;             
}

[StructLayout(LayoutKind.Sequential)]
struct SDL_BlitMap
{
    IntPtr dstPtr;
    int identity;
    SDL_blit blit;
    IntPtr data;
    SDL_BlitInfo info;

    /* the version count matches the destination; mismatch indicates
       an invalid mapping */
    UInt32 dst_palette_version;
    UInt32 src_palette_version;
}

[StructLayout(LayoutKind.Sequential)]
struct SDL_Rect
{
    int x, y;
    int w, h;
}

[UnmanagedFunctionPointer(CallingConvention.ToBeAdded)]
internal delegate int SDL_blit(ref SDL_Surface src, ref SDL_Rect srcrect, ref SDL_Surface dst, ref SDL_Rect dstrect);

When you need to read any structure pointers:

var palette = (SDL_Palette) Marshal.PtrToStructure(surface.palettePtr, typeof (SDL_Pallete));

Otherwise, if you wish to stick to your current code, try declaring the function pointer as an IntPtr instead of a delegate and create the delegate during runtime using Marshal.GetDelegateForFunctionPointer.

master131
  • 431
  • 3
  • 4
  • Speed is critical; these are used in drawing routines. I think I'll take your `IntPtr` suggestion; that seems to work fine and I can't see where I'd actually want to override the blit function anyway, although this is supposed to be a generic library. Marshalling the delegate once is better than marshalling every time one of these functions is called. – mpen Jul 08 '13 at 18:28