【问题标题】:C#: Object with custom marshaller not containing data after PInvoke callC#:具有自定义编组器的对象在 PInvoke 调用后不包含数据
【发布时间】:2010-04-14 09:13:17
【问题描述】:

我在调用某些接受WAVEFORMATEX 结构作为参数的 WinAPI 函数时遇到问题。由于WAVEFORMATEX 结构的长度可以变化,我实现了一个WaveFormatEX 类,该类由一个自定义编组器类(实现ICustmoMarshaller)编组。这是 Aaron Lerch 在他的博客 (Part 1, Part 2) 中提供的示例,但在我这边做了一些修改。

当我从我的代码中调用 API 函数时,会调用自定义编组器的方法 MarshalManagedToNativeMarshalNativeToManaged,并且MarshalNativeToManaged 的末尾,托管对象包含正确的值强>。但是当执行返回到我的调用代码时,WaveFormatEx 对象不包含 API 调用期间读取的值

所以问题是:为什么在本地 API 调用之后,正确地从本地编组回托管的数据没有显示在我的 WaveFormatEx 对象中?我在这里做错了什么?

编辑:
澄清一下,函数调用成功了,将WaveFormatEx 对象编组回托管代码也是如此。就在执行从编组方法返回到调用该方法的作用域时,在该调用作用域中声明的WaveFormatEx 对象不包含结果数据。

这里是函数原型和 WaveFormatEx 类:

[DllImport("avifil32.dll")]
public static extern int AVIStreamReadFormat(
    int Stream,
    int Position,
    [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, 
        MarshalTypeRef = typeof(WaveFormatExMarshaler))]
    WaveFormatEx Format,
    ref int Size
    );

[StructLayout(LayoutKind.Sequential)]
public class WaveFormatEx
{
    public int FormatTag;
    public short Channels;
    public int SamplesPerSec;
    public int AvgBytesPerSec;
    public short BlockAlign;
    public short BitsPerSample;
    public short Size;
    public byte[] AdditionalData;

    public WaveFormatEx(short AdditionalDataSize)
    {
        WaveFormat.Size = AdditionalDataSize;
        AdditionalData = new byte[AdditionalDataSize];
    }
}

编组方法如下所示:

public object MarshalNativeToManaged(System.IntPtr NativeData)
{
    WaveFormatEx ManagedObject = new WaveFormatEx(0);
    ManagedObject = (WaveFormatEx)Marshal.PtrToStructure(
       NativeData, typeof(WaveFormatEx));

    ManagedObject.AdditionalData = new byte[ManagedObject.Size];

    // If there is extra data, marshal it
    if (ManagedObject.WaveFormat.Size > 0)
    {
        NativeData = new IntPtr(
            NativeData.ToInt32() + 
            Marshal.SizeOf(typeof(WaveFormatEx)));
        ManagedObject.AdditionalData = new byte[ManagedObject.WaveFormat.Size];
        Marshal.Copy(NativeData, ManagedObject.AdditionalData, 0, 
            ManagedObject.WaveFormat.Size);
    }
    return ManagedObject;
}

public System.IntPtr MarshalManagedToNative(object Object)
{
    WaveFormatEx ManagedObject = (WaveFormatEx)Object;

    IntPtr NativeStructure = Marshal.AllocHGlobal(
        GetNativeDataSize(ManagedObject) + ManagedObject.WaveFormat.Size);

    Marshal.StructureToPtr(ManagedObject, NativeStructure, false);

    // Marshal extra data 
    if (ManagedObject.WaveFormat.Size > 0)
    {
        IntPtr dataPtr = new IntPtr(NativeStructure.ToInt32() 
            + Marshal.SizeOf(typeof(WaveFormatEx)));
        Marshal.Copy(ManagedObject.AdditionalData, 0, dataPtr,  Math.Min(
            ManagedObject.WaveFormat.Size,
            ManagedObject.AdditionalData.Length));
    }
    return NativeStructure;
}

这是我的调用代码:

WaveFormatEx test = new WaveFormatEx(100);
int Size = System.Runtime.InteropServices.Marshal.SizeOf(test);

// After this call, test.FormatTag should be set to 1 (PCM audio), 
// but it is still 0, as well as all the other members
int Result = Avi.AVIStreamReadFormat(AudioStream, 0, test, ref Size);

【问题讨论】:

    标签: c# pinvoke marshalling


    【解决方案1】:

    代码和声明中有几个错误会阻止此代码在 64 位操作系统上运行。请务必将 Platform Target 设置为 x86。

    您确定本机函数实际上返回数据吗?结果返回值是什么?非零值表示失败。

    调用这个函数的正确方法是调用它两次。首先将 lpFormat 参数设置为 null (IntPtr.Zero),这样它会告诉您它需要多大的缓冲区(由 lpbcFormat 返回)。然后创建缓冲区并再次调用它。

    我将在第一次调用后使用 Marshal.AllocHGobal 创建缓冲区,而不是自定义编组器,并在第二次调用中传递它作为 lpFormat 参数返回的 IntPtr。然后,如果您获得成功返回码,则使用 Marshal.PtrToStructure 编写 WaveFormatEx。和 Marshal.Copy 来获取额外的数据。

    Fwiw,使用 ref 会导致 P/Invoke 编组器将 WaveFormatEx** 传递给函数,但它需要 WaveFormatEx*。这将导致它覆盖垃圾收集堆中的数据,破坏其内部格式。当 CLR 注意到这一点时,接下来就是一个 kaboom。

    查看NAudio project 作为自己执行此操作的好选择。

    【讨论】:

    • 1) 目标是 x86 2) 函数成功,我通过单步执行 MarshalNativeToManaged 检查了它的返回数据。该方法返回的对象具有正确的数据。 3)手动分配缓冲区并就地进行编组当然是一种选择,将对其进行研究。 4)这解释了它 - Kaboom 正是那里发生的事情...... 5)我知道 NAudio,但我想自己做这一切(这是一个私人项目,与目标一样学习东西作为创建有用的软件...)无论如何,非常感谢您在回答中付出的努力!
    猜你喜欢
    • 1970-01-01
    • 2013-06-10
    • 1970-01-01
    • 2013-08-10
    • 1970-01-01
    • 2012-06-20
    • 1970-01-01
    • 1970-01-01
    • 2016-11-02
    相关资源
    最近更新 更多