【问题标题】:How to marshal size_t cross-platform, using semantic types如何编组 size_t 跨平台,使用语义类型
【发布时间】:2012-10-09 14:20:51
【问题描述】:

据我了解,MarshalAsAttribute(UnmanagedType.SysUInt) 应该将特定于平台的无符号整数类型(32 或 64 字节)编组为托管类型 (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);

但它不起作用 - 我收到此错误:

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

我知道I can switch the return type to IntPtr,但这对于使用我的 API 的人来说是非常不直观的。

为什么 SysUInt 不工作?

【问题讨论】:

  • UIntPtr 肯定是正确的类型。为什么要ulong
  • 语义,它是一个公共 API。
  • 您可以在公共 API 中使用 size_t
  • .Net 没有 size_t 类型,是吗?

标签: c# pinvoke marshalling


【解决方案1】:

您可以使用UIntPtr 将 PInvoke 方法保持为私有,并使用您喜欢的签名实现另一个方法,该方法调用 PInvoke 映射一切正确,这个方法将是公共的:

/// 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);

当框架变得难以处理时......不要使用它们。编组是一种痛苦,我倾向于只使用我已经知道的东西......其他的东西,我只是绕着走。

编辑

它不起作用,因为封送器不够聪明,无法看到每个 SysUInt 类型都适合 ulong 类型。它正在检查返回,与参数相同。

确实,您不能将ulongSysUInt 用作参数,但是您可以用作返回值……看到差异并不聪明。 =\

有哪些替代品?

UIntPtr 似乎是最好的选择...但还有其他选择:实现自定义封送器,使用接口 ICustomMarshaler... 并使用 UnmanagedType.CustomMarshaler

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

ICustomMarshaler 实现

通过 ICustomMarshaler 的这个实现,您可以做您想做的事。我没有测试它,因为我没有一个非托管库来进行测试,但它很简单,而且非常简单......所以我认为它会按原样工作,无需任何更改。如果没有,请发表评论,我会修改它。

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;
    }
}

【讨论】:

  • 每种方法都有 1 个或多个 size_t 参数...我需要公开大约 200 个参数。这将使 LOC 增加三倍...我最好还是坚持使用 UIntPtr...
猜你喜欢
  • 2017-11-15
  • 2012-05-23
  • 1970-01-01
  • 2011-10-17
  • 1970-01-01
  • 2010-10-20
  • 1970-01-01
  • 1970-01-01
  • 2012-04-05
相关资源
最近更新 更多