マネージ 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# からのアンマネージ コードの呼び出しなどに関する良いリンクがあれば、本当に感謝しています。私は何を間違っていますか?期待したものが得られないのはなぜですか。