У меня проблемы с вызовом функций собственной библиотеки из управляемого кода C #. Я занимаюсь разработкой компактного фреймворка 3.5 (Windows Mobile 6.x) на всякий случай, если это что-то изменит.
Я работаю с функциями waveIn * из coredll.dll (я полагаю, они находятся в winmm.dll в обычной Windows). Вот что я придумал:
// 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), когда буфер заполнен, структура hdr, переданная обратно в wavehdr, очевидно, повреждена. Вот пример того, как структура выглядит в этой точке:
- 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
Это явно не то, что я ожидал пройти. Я явно обеспокоен порядком полей в представлении. Я не знаю, заботится ли Visual Studio .NET о реальном порядке памяти при отображении записи в «локальном» виде, но они явно не отображаются в порядке, указанном мной в структуре.
Тогда указатель на данные отсутствует, а поле bufferLength находится слишком далеко. Интересно, что поле bytesRecorded точно равно 64000 - bufferLength и bytesRecorded, хотя я ожидаю, что оба будут 64000. Я не знаю, что именно не так, может быть, кто-то может мне помочь в этом. Я абсолютный новичок в программировании управляемого кода и маршаллинге, поэтому, пожалуйста, не будьте слишком резкими со мной из-за всех глупостей, которые я, вероятно, сделал.
О, вот определение кода C для WAVEHDR, которое я нашел здесь , я полагаю, что, возможно, что-то не так в определении структуры 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 не поддерживает директиву Pack = 2 ^ x в [StructLayout].
Я попытался [StructLayout (LayoutKind.Explicit)] и использовал 4 байта и 8 байтов выравнивания. Выравнивание в 4 байта дало мне тот же результат, что и приведенный выше код, а выравнивание в 8 байтов только ухудшило ситуацию, но я ожидал этого.
Интересно, если я переместлю код из setupBuffer в setupWaveIn и не объявляю GCHandle в контексте класса, но в локальном контексте setupWaveIn структура, возвращаемая функцией обратного вызова, кажется, не повреждена. Однако я не уверен, почему это так и как я могу использовать эти знания для исправления своего кода. Забудьте об этом. Я перепутал вещи со старым кодом, который использовал.
Я бы очень признателен за любые хорошие ссылки на маршаллинг, вызов неуправляемого кода из C # и т. Д. Тогда я был бы очень рад, если бы кто-то мог указать на мои ошибки. Что я делаю неправильно? Почему я не получаю то, что ожидаю.