C#:具有自定义编组器的对象在PInvoke调用后不包含数据 [英] C#: Object with custom marshaller not containing data after PInvoke call

查看:86
本文介绍了C#:具有自定义编组器的对象在PInvoke调用后不包含数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在调用一些接受WAVEFORMATEX结构作为参数的WinAPI函数时遇到了问题.由于WAVEFORMATEX结构的长度可以变化,因此我实现了一个WaveFormatEX类,该类由自定义编组器类(实现ICustmoMarshaller)编组.这是Aaron Lerch在其博客(第二部分),但我方面做了一些修改.

I am having a problem with PInvoking some WinAPI functions that accept WAVEFORMATEX structures as parameters. Since the length of the WAVEFORMATEX structure can vary, I implemented a WaveFormatEX class that is marshalled by a custom marshaller class (which implements ICustmoMarshaller). This is following an example provided by Aaron Lerch in his Blog (Part 1, Part 2), but with a few modifications from my side.

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

When I call the API function from my code, the methods MarshalManagedToNative and MarshalNativeToManaged of the custom marshaller are called, and at the end of MarshalNativeToManaged, the managed object contains the correct values. But when the execution returns to my calling code, the WaveFormatEx object does not contain the values read during the API call.

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

So the question is: Why does the data that is correctly marshalled back from native to managed not show up in my WaveFormatEx object after the native API call? What am I doing wrong here?

修改:
为了明确起见,函数调用成功,将WaveFormatEx对象编组回托管代码也成功.只是当执行从编组方法返回到调用该方法的作用域时,在该调用作用域中声明的WaveFormatEx对象并不包含结果数据.


To clarify, the function call succeeds, so does the marshalling of the WaveFormatEx object back to managed code. Just when the execution returns from the marshalling method to the scope from where the method was called, the WaveFormatEx object that was declared in that calling scope does not contain the result data.

这是函数原型和WaveFormatEx类:

Here are the function prototype and the WaveFormatEx class:

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

推荐答案

由于代码和声明中存在一些错误,导致该代码无法在64位操作系统上运行.确保将平台目标"设置为x86.

There are several mistakes in the code and the declarations that prevents this code from working on a 64-bit operating system. Be sure to set the Platform Target to x86.

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

Are you sure the native function actually returns data? What is the Result return value? A non-zero value indicates failure.

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

The proper way to call this function is to call it twice. First with the lpFormat argument set to null (IntPtr.Zero) so it tells you how large a buffer it needs (returned by lpbcFormat). Then you create the buffer and call it again.

在第一个调用之后,我将使用Marshal.AllocHGobal创建缓冲区,而不是自定义编组器,并在第二个调用中传递IntPtr作为lpFormat参数返回.然后,如果获得成功返回码,请使用Marshal.PtrToStructure编写WaveFormatEx.还有元帅.复制以获取其他数据.

Instead of a custom marshaller, I would just create the buffer with Marshal.AllocHGobal after the first call and pass the IntPtr it returns as the lpFormat argument in the second call. Then, iff you get a success return code, use Marshal.PtrToStructure to write the WaveFormatEx. And Marshal.Copy to get the additional data.

首先,使用ref会使P/Invoke封送处理程序将WaveFormatEx **传递给该函数,但它希望使用WaveFormatEx *.这将导致它覆盖垃圾收集堆中的数据,从而破坏其内部格式.当CLR注意到这一点时,接下来是kaboom.

Fwiw, using ref causes the P/Invoke marshaller to pass a WaveFormatEx** to the function but it expects a WaveFormatEx*. Which will cause it to overwrite data in the garbage collected heap, destroying its internal format. A kaboom is next when the CLR notices this.

请查看 NAudio项目,这是您自己执行此操作的一个不错的选择.

Check out the NAudio project as a good alternative for doing this yourself.

这篇关于C#:具有自定义编组器的对象在PInvoke调用后不包含数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆