简体   繁体   中英

How to marshal size_t cross-platform, using semantic types

According to my understanding, MarshalAsAttribute(UnmanagedType.SysUInt) is supposed to marshal a platform-specific unsigned integer type (either 32 or 64 bytes) into the managed type (ulong).

     /// Return Type: size_t->unsigned int
    ///bgr: uint8_t*
    ///width: int
    ///height: int
    ///stride: int
    ///output: uint8_t**
    [DllImportAttribute("libwebp.dll", EntryPoint = "WebPEncodeLosslessBGR")]
    [return: MarshalAsAttribute(UnmanagedType.SysUInt)]
    public static extern ulong WebPEncodeLosslessBGR([InAttribute()] IntPtr bgr, int width, int height, int stride, ref IntPtr output);

But it doesn't work - I get this error:

Cannot marshal 'return value': Invalid managed/unmanaged type combination (Int64/UInt64 must be paired with I8 or U8).

I know I can switch the return type to IntPtr , but that's very un-intuitive on the people using my API.

Why is SysUInt not working?

You could keep the PInvoke method private with UIntPtr , and implement another method with your preferred signature, that call the PInvoke mapping everything correctly, and this one would be public:

/// Return Type: size_t->unsigned int
///bgr: uint8_t*
///width: int
///height: int
///stride: int
///output: uint8_t**
public static ulong WebPEncodeLosslessBGR([InAttribute()] IntPtr bgr, int width, int height, int stride, ref IntPtr output)
{
    return (ulong)_WebPEncodeLosslessBGR(bgr, width, height, stride, ref output);
}

[DllImportAttribute("libwebp.dll", EntryPoint = "WebPEncodeLosslessBGR")]
[return: MarshalAsAttribute(UnmanagedType.SysUInt)]
private static extern UIntPtr _WebPEncodeLosslessBGR([InAttribute()] IntPtr bgr, int width, int height, int stride, ref IntPtr output);

When frameworks gets to difficult to deal with... just don't use them. Marshaling is a pain, I tend to just use the things that I already know... everything else, I just go around.

EDIT

It is not working because the marshaler is not smart enough to see that every SysUInt type fits in a ulong type. It is checking the return, the same as arguments.

It is true that you cannot use ulong and SysUInt for an argument, but you could for the return... it is just not smart to see the difference. =\\

What alternatives are there?

UIntPtr seems to be the best choice... but there are other choices: implement a custom marshaler, using interface ICustomMarshaler ... and use UnmanagedType.CustomMarshaler :

[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CustomMarshalerType))]

ICustomMarshaler implementation

With this implementation of ICustomMarshaler you may be abled to do what you want. I did not test it, since I don't have an unmanaged library to do the test, but it is straight forward, and very simple... so I think it will work as it is, without any changes. If it don't, just leave a comment, and I'll revise it.

public class CustomMarshalerType : ICustomMarshaler
{
    public object MarshalNativeToManaged(IntPtr pNativeData)
    {
        return (ulong)Marshal.ReadIntPtr(pNativeData).ToInt64();
    }

    public IntPtr MarshalManagedToNative(object ManagedObj)
    {
        throw new InvalidOperationException();
    }

    public void CleanUpNativeData(IntPtr pNativeData)
    {
    }

    public void CleanUpManagedData(object ManagedObj)
    {
    }

    public int GetNativeDataSize()
    {
        return IntPtr.Size;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM