当前位置:  开发笔记 > 编程语言 > 正文

为什么waveOutWrite()会在调试堆中导致异常?

如何解决《为什么waveOutWrite()会在调试堆中导致异常?》经验,为你挑选了0个好方法。

在研究这个问题时,我在网上发现了以下场景的多个提及,总是作为编程论坛上未解答的问题.我希望在此发布此内容至少可以记录我的发现.

首先,症状:在运行使用waveOutWrite()输出PCM音频的相当标准代码时,我有时会在调试器下运行时得到这个:

 ntdll.dll!_DbgBreakPoint@0()   
 ntdll.dll!_RtlpBreakPointHeap@4()  + 0x28 bytes    
 ntdll.dll!_RtlpValidateHeapEntry@12()  + 0x113 bytes   
 ntdll.dll!_RtlDebugGetUserInfoHeap@20()  + 0x96 bytes  
 ntdll.dll!_RtlGetUserInfoHeap@20()  + 0x32743 bytes    
 kernel32.dll!_GlobalHandle@4()  + 0x3a bytes   
 wdmaud.drv!_waveCompleteHeader@4()  + 0x40 bytes   
 wdmaud.drv!_waveThread@4()  + 0x9c bytes   
 kernel32.dll!_BaseThreadStart@8()  + 0x37 bytes    

虽然明显的嫌疑人会在代码中的其他地方出现堆损坏,但我发现事实并非如此.此外,我能够使用以下代码重现此问题(这是基于对话框的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()/ free()来管理正在播放的缓冲区是完全错误的.你会注意到我改变了上面的代码来反映这个建议,以防止更多的人犯同样的错误 - 这没有什么区别.waveCompleteHeader()释放的缓冲区不是包含PCM数据的缓冲区,释放PCM缓冲区的责任在于应用程序,并且不要求以任何特定方式分配它.

另外,我确保我使用的waveOut API调用都没有失败.

我目前假设这是Windows中的错误或音频驱动程序中的错误.欢迎持不同意见.

推荐阅读
携手相约幸福
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有