虽然研究这个问题,我发现以下情形多提到网上,总是作为编程论坛上悬而未决的问题。我希望在这里张贴这至少将用于记录我的发现。为什么waveOutWrite()会在调试堆中导致异常?
一,症状:
[email protected]()
[email protected]() + 0x28 bytes
[email protected]() + 0x113 bytes
[email protected]() + 0x96 bytes
[email protected]() + 0x32743 bytes
[email protected]() + 0x3a bytes
[email protected]() + 0x40 bytes
[email protected]() + 0x9c bytes
[email protected]() + 0x37 bytes
虽然明显的嫌疑人将是一个堆损坏:在运行使用waveOutWrite()输出PCM音频非常标准的代码,我有时会在调试器下运行时,得到这个在代码的其他地方,我发现情况并非如此。此外,我能够重现使用下面的代码这个问题(这是一个基于对话框的MFC应用程序的一部分:)
void CwaveoutDlg::OnBnClickedButton1()
{
WAVEFORMATEX wfx;
wfx.nSamplesPerSec = 44100; /* sample rate */
wfx.wBitsPerSample = 16; /* sample size */
wfx.nChannels = 2;
wfx.cbSize = 0; /* size of _extra_ info */
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
waveOutOpen(&hWaveOut,
WAVE_MAPPER,
&wfx,
(DWORD_PTR)m_hWnd,
0,
CALLBACK_WINDOW);
ZeroMemory(&header, sizeof(header));
header.dwBufferLength = 4608;
header.lpData = (LPSTR)GlobalLock(GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT, 4608));
waveOutPrepareHeader(hWaveOut, &header, sizeof(header));
waveOutWrite(hWaveOut, &header, sizeof(header));
}
afx_msg LRESULT CwaveoutDlg::OnWOMDone(WPARAM wParam, LPARAM lParam)
{
HWAVEOUT dev = (HWAVEOUT)wParam;
WAVEHDR *hdr = (WAVEHDR*)lParam;
waveOutUnprepareHeader(dev, hdr, sizeof(WAVEHDR));
GlobalFree(GlobalHandle(hdr->lpData));
ZeroMemory(hdr, sizeof(*hdr));
hdr->dwBufferLength = 4608;
hdr->lpData = (LPSTR)GlobalLock(GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT, 4608));
waveOutPrepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
waveOutWrite(hWaveOut, hdr, sizeof(WAVEHDR));
return 0;
}
在任何人对这个评论,是 - 示例代码播放未初始化的内存。不要试图将您的扬声器翻过来。
某些调试揭示了以下信息:waveOutPrepareHeader()使用指向至少包含两个指针作为其前两个成员的结构的指针填充header.reserved。第一个指针设置为NULL。在调用waveOutWrite()之后,该指针被设置为在全局堆上分配的指针。在伪代码,这将是这个样子:
struct Undocumented { void *p1, *p2; } /* This might have more members */
MMRESULT waveOutPrepareHeader(handle, LPWAVEHDR hdr, ...) {
hdr->reserved = (Undocumented*)calloc(sizeof(Undocumented));
/* Do more stuff... */
}
MMRESULT waveOutWrite(handle, LPWAVEHDR hdr, ...) {
/* The following assignment fails rarely, causing the problem: */
hdr->reserved->p1 = malloc(/* chunk of private data */);
/* Probably more code to initiate playback */
}
通常情况下,报头由waveCompleteHeader返回给应用程序(),内部功能到wdmaud.dll。 waveCompleteHeader()尝试通过调用GlobalHandle()/ GlobalUnlock()和朋友解除分配)的waveOutWrite(分配的指针。有时候,GlobalHandle()炸弹,如上所示。
现在,GlobalHandle()炸弹不是由于堆腐败造成的,正如我最初怀疑的 - 这是因为waveOutWrite()返回时没有将内部结构中的第一个指针设置为有效指针。我怀疑它在返回之前释放了该指针指向的内存,但我还没有反汇编它。
这只在波形回放系统在缓冲区低时才会出现,这就是为什么我使用单个标题来重现这一点。
在这一点上我对这个是我的应用程序中的错误相当不错的情况 - 毕竟,我的应用程序甚至没有运行。有没有人见过这个?
我在Windows XP SP2看到这一点。声卡来自SigmaTel,驱动程序版本为5.10.0.4995。
注:
为了防止将来出现混乱,我想指出的是,答案提示问题出在使用malloc(的)/免费()来管理正在播放的缓冲区简直是错的。你会注意到我改变了上面的代码来反映这个建议,以防止更多的人犯同样的错误 - 这没有什么不同。 waveCompleteHeader()释放的缓冲区不是包含PCM数据的缓冲区,释放PCM缓冲区的责任在于应用程序,并且不要求以任何特定方式分配它。
另外,我要确保我没有使用waveout的API调用的失败。
我目前假设,这是无论是在Windows中的漏洞,或在音频驱动程序。反对意见总是受欢迎的。
我依稀记得见过类似的东西在Windows CE应用程序。 Deleaker报告说,一些波***功能有内存泄漏。最后,一切都很好,关闭时,我只需要释放资源。虽然我没有这里的代码。 – OregonGhost 2008-10-12 17:11:52