我无法从托管 C# 代码中调用本机库的函数。我正在为 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 + 保留 0x7c07c9a0 System.IntPtr
这显然不是我期望通过的。我显然很关心视图中字段的顺序。我不知道 Visual Studio .NET 在“本地”视图中显示记录时是否关心实际内存顺序,但它们显然没有按照我在结构中指定的顺序显示。
然后没有数据指针并且bufferLength字段远高。有趣的是,bytesRecorded 字段正好是 64000 - bufferLength 和 bytesRecorded 我希望两者都是 64000。我不知道到底出了什么问题,也许有人可以帮助我解决这个问题。我是托管代码编程和编组的绝对菜鸟,所以请不要对我可能做过的所有愚蠢的事情太苛刻。
哦,这是我在这里找到的 WAVEHDR 的 C 代码定义,我相信我可能在 C# 结构定义中做错了:
/* 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# 调用非托管代码等方面的任何好的链接。如果有人能指出我的错误,我会非常高兴。我究竟做错了什么?为什么我没有得到我所期望的。