c# - 在 C# 中编码到 native 库

标签 c# unmanaged marshalling native winmm

我在从托管 C# 代码中调用 native 库的函数时遇到问题。我正在针对 3.5 紧凑框架 (Windows Mobile 6.x) 进行开发,以防万一这会产生任何影响。

我正在使用 coredll.dll 中的 waveIn* 函数(我相信这些函数位于常规 Windows 的 winmm.dll 中)。这就是我想到的:

// namespace winmm; class winmm
[StructLayout(LayoutKind.Sequential)]
public struct WAVEFORMAT
{
    public ushort wFormatTag;
    public ushort nChannels;
    public uint nSamplesPerSec;
    public uint nAvgBytesPerSec;
    public ushort nBlockAlign;
    public ushort wBitsPerSample;
    public ushort cbSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct WAVEHDR
{
    public IntPtr lpData;
    public uint dwBufferLength;
    public uint dwBytesRecorded;
    public IntPtr dwUser;
    public uint dwFlags;
    public uint dwLoops;
    public IntPtr lpNext;
    public IntPtr reserved;
}

public delegate void AudioRecordingDelegate(IntPtr deviceHandle, uint message, IntPtr instance, ref WAVEHDR wavehdr, IntPtr reserved2);

[DllImport("coredll.dll")]
public static extern int waveInAddBuffer(IntPtr hWaveIn, ref WAVEHDR lpWaveHdr, uint cWaveHdrSize);
[DllImport("coredll.dll")]
public static extern int waveInPrepareHeader(IntPtr hWaveIn, ref WAVEHDR lpWaveHdr, uint Size);
[DllImport("coredll.dll")]
public static extern int waveInStart(IntPtr hWaveIn);

// some other class
private WinMM.WinMM.AudioRecordingDelegate waveIn;
private IntPtr handle;
private uint bufferLength;

private void setupBuffer()
{
    byte[] buffer = new byte[bufferLength];
    GCHandle bufferPin = GCHandle.Alloc(buffer, GCHandleType.Pinned);
    WinMM.WinMM.WAVEHDR hdr = new WinMM.WinMM.WAVEHDR();
    hdr.lpData = bufferPin.AddrOfPinnedObject();
    hdr.dwBufferLength = this.bufferLength;
    hdr.dwFlags = 0;

    int i = WinMM.WinMM.waveInPrepareHeader(this.handle, ref hdr, Convert.ToUInt32(Marshal.SizeOf(hdr)));
    if (i != WinMM.WinMM.MMSYSERR_NOERROR)
    {
        this.Text = "Error: waveInPrepare";
        return;
    }
    i = WinMM.WinMM.waveInAddBuffer(this.handle, ref hdr, Convert.ToUInt32(Marshal.SizeOf(hdr)));
    if (i != WinMM.WinMM.MMSYSERR_NOERROR)
    {
        this.Text = "Error: waveInAddrBuffer";
        return;
    }
}

private void setupWaveIn()
{
    WinMM.WinMM.WAVEFORMAT format = new WinMM.WinMM.WAVEFORMAT();
    format.wFormatTag = WinMM.WinMM.WAVE_FORMAT_PCM;
    format.nChannels = 1;
    format.nSamplesPerSec = 8000;
    format.wBitsPerSample = 8;
    format.nBlockAlign = Convert.ToUInt16(format.nChannels * format.wBitsPerSample);
    format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
    this.bufferLength = format.nAvgBytesPerSec;
    format.cbSize = 0;

    int i = WinMM.WinMM.waveInOpen(out this.handle, WinMM.WinMM.WAVE_MAPPER, ref format, Marshal.GetFunctionPointerForDelegate(waveIn), 0, WinMM.WinMM.CALLBACK_FUNCTION);
    if (i != WinMM.WinMM.MMSYSERR_NOERROR) 
    {
        this.Text = "Error: waveInOpen";
        return;
    }

    setupBuffer();

    WinMM.WinMM.waveInStart(this.handle);
}

过去几天我读了很多有关编码的内容,但我没有让这段代码工作。当缓冲区已满时调用我的回调函数(waveIn)时,wavehdr 中传回的 hdr 结构显然已损坏。以下是此时结构的示例:

-      wavehdr {WinMM.WinMM.WAVEHDR}   WinMM.WinMM.WAVEHDR
         dwBufferLength 0x19904c00  uint
         dwBytesRecorded    0x0000fa00  uint
         dwFlags    0x00000003  uint
         dwLoops    0x1990f6a4  uint
+       dwUser  0x00000000  System.IntPtr
+       lpData  0x00000000  System.IntPtr
+       lpNext  0x00000000  System.IntPtr
+       reserved    0x7c07c9a0  System.IntPtr

This obiously is not what I expected to get passed. I am clearly concerned about the order of the fields in the view. I do not know if Visual Studio .NET cares about actual memory order when displaying the record in the "local"-view, but they are obviously not displayed in the order I speciefied in the struct.

Then theres no data pointer and the bufferLength field is far to high. Interestingly the bytesRecorded field is exactly 64000 - bufferLength and bytesRecorded I'd expect both to be 64000 though. I do not know what exactly is going wrong, maybe someone can help me out on this. I'm an absolute noob to managed code programming and marshalling so please don't be too harsh to me for all the stupid things I've propably done.

Oh here's the C code definition for WAVEHDR which I found here, I believe I might have done something wrong in the C# struct definition:

/* wave data block header */
typedef struct wavehdr_tag {
    LPSTR       lpData;                 /* pointer to locked data buffer */
    DWORD       dwBufferLength;         /* length of data buffer */
    DWORD       dwBytesRecorded;        /* used for input only */
    DWORD_PTR   dwUser;                 /* for client's use */
    DWORD       dwFlags;                /* assorted flags (see defines) */
    DWORD       dwLoops;                /* loop control counter */
    struct wavehdr_tag FAR *lpNext;     /* reserved for driver */
    DWORD_PTR   reserved;               /* reserved for driver */
} WAVEHDR, *PWAVEHDR, NEAR *NPWAVEHDR, FAR *LPWAVEHDR;

如果您习惯于使用所有这些低级工具(例如指针算术、强制类型转换等),那么开始编写托管代码是一件很痛苦的事情。这就像双手被绑在背上试图学习如何游泳一样。 我尝试过一些事情(没有效果): .NET 紧凑框架似乎不支持 [StructLayout] 中的 Pack = 2^x 指令。 我尝试了 [StructLayout(LayoutKind.Explicit)] 并使用 4 字节和 8 字节对齐。 4 字节对齐给了我与上面代码相​​同的结果,而 8 字节对齐只会让事情变得更糟 - 但这正是我所期望的。 有趣的是,如果我将代码从 setupBuffer 移到 setupWaveIn 中,并且不在类的上下文中声明 GCHandle,但在 setupWaveIn 的本地上下文中,回调函数返回的结构似乎没有损坏。但我不确定为什么会出现这种情况,以及如何利用这些知识来修复我的代码。 忘了吧。我将一些东西与我使用的更旧的代码混合在一起。

我真的很感激有关编码、从 C# 调用非托管代码等方面的任何好的链接。如果有人能指出我的错误,我会非常高兴。我究竟做错了什么?为什么我没有得到我所期望的。

最佳答案

您的 P/Invoke 声明是无可挑剔的。然而,您发布的 WAVEHDR 转储有一些非常的奇怪之处。它缺少 lpData 字段。如果插入,所有数字都会正确排列(即 lpData = 0x19904c00、dwBufferLength = 0x0000fa00 等)。

不知道这是怎么发生的,但它不知何故使用了错误的 WAVEHDR 声明。 WinMM.WinMM 应该是一个提示。

关于c# - 在 C# 中编码到 native 库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2644336/

相关文章:

java - JAXB 包装所有字段

c# - VSTS - 使用 API 获取所有项目的列表,而不仅仅是前 100 个

c# - 在 asp.net web 应用程序中使用后无法删除托管 dll

.net - 托管堆和非托管堆

c++ - 将托管 C# DLL 包含到非托管 C++ DLL 中——全部在一个文件中

java - 使用 JAXB 仅编码选定的字段

c# - .net 中的转换 : Native Utf-8 <-> Managed String

c# - 如何实现带有备份选项的树?

c# - ASP.Net Core 3.0 依赖注入(inject)忽略工厂方法?

c# - 从 Azure 中提取显示在 MainPage 和 ItemPage 上的数据