XAudio2卸载时访问违反异常

XAudio2 Access Violation Exception when Unloaded

本文关键字:异常 访问 卸载 XAudio2      更新时间:2023-10-16

我正在尝试使用XAudio2音频系统来异步播放循环声音。它起作用,但是当DLL被卸载时(这里的代码是在另一个程序加载的DLL中),我在父应用程序中得到一个访问冲突异常。

我注意到我没有收到任何错误,直到我把母带声音添加到混音中,但我不知道我可能做错了什么。

代码如下:

在soundengine.h

#include <Windows.h>
#include <xaudio2.h>
#include <fstream>
#define SAFE_DELETE(p) if(p) {delete p; p = NULL;}
#define SAFE_DELETE_ARRAY(p) if(p) {delete[] p; p = NULL;}
#define DXTRACE_ERR(str,hr) DXTrace(__FILE__,(DWORD)__LINE__,hr,str, FALSE)
using namespace std;
#define XA2SE_PLAY_LOOP 1
#define XA2SE_PLAY_STD  0
#define XA2SE_ERROR_NOTPLAYING  -1
#define XA2SE_ERROR_COULD_NOT_OPEN_FILE -2
#define XA2SE_ERROR_NOT_WAVE_FILE   -3
#define XA2SE_ERROR_COULD_NOT_SEEK  -4
#define XA2SE_ERROR_COULD_NOT_DESCEND   -5
#define XA2SE_ERROR_COULD_NOT_RESET -6
#define XA2SE_ERROR_RIFF_ERROR  -7
#define XA2SE_ERROR_SIZE_ERROR  -8
#define XA2SE_ERROR_COULD_NOT_READ  -9
#define XA2SE_ERROR_UNEXPECTED_NULL_VALUE   -10
#define XA2SE_ERROR_COULD_NOT_ASCEND    -11
#define XA2SE_ERROR_COULD_NOT_GET_INFO  -12
#define XA2SE_ERROR_COULD_NOT_ADVANCE   -13
#define XA2SE_ERROR_COULD_NOT_READEND   -14
#define XA2SE_ERROR_COULD_NOT_SET_INFO  -15
#define XA2SE_ERROR_COULD_NOT_CREATE_VOICE  -16
#define XA2SE_COULD_NOT_SUBMIT_BUFFER   -17
#define WAVEFILE_READ   0x001
class XA2SoundEngine {
public:
    IXAudio2MasteringVoice* mv;
    IXAudio2SourceVoice* sv;
    IXAudio2* pXA2; 
    XA2SoundEngine();
    ~XA2SoundEngine();
    BOOL Play(LPWSTR file, int mode);   
};
class WaveFile {
public:
    WAVEFORMATEX* m_pwfx;        // Pointer to WAVEFORMATEX structure
    HMMIO m_hmmio;       // MM I/O handle for the WAVE
    MMCKINFO m_ck;          // Multimedia RIFF chunk
    MMCKINFO m_ckRiff;      // Use in opening a WAVE file
    DWORD m_dwSize;      // The size of the wave file
    MMIOINFO m_mmioinfoOut;    
    BYTE* m_pbData;
    BYTE* m_pbDataCur;
    ULONG m_ulDataSize;
    CHAR* m_pResourceBuffer;
protected:
    HRESULT ReadMMIO();
    HRESULT WriteMMIO( WAVEFORMATEX* pwfxDest );
public:
            WaveFile();
            ~WaveFile();
    HRESULT Open( LPWSTR strFileName);    
    HRESULT Close();
    HRESULT Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead );
    HRESULT Write( UINT nSizeToWrite, BYTE* pbData, UINT* pnSizeWrote );
    DWORD   GetSize();
    HRESULT ResetFile();
    WAVEFORMATEX* GetFormat() {
        return m_pwfx;
    };
};
extern XA2SoundEngine* soundengine;
在soundengine.cpp

#include "soundengine.h"
struct async_play_params {
    LPWSTR file;        
    int mode;
};
DWORD _stdcall async_play_routine(void* param);
XA2SoundEngine::XA2SoundEngine() {      
    HRESULT hr;
    CoInitializeEx(NULL,COINIT_MULTITHREADED);
    pXA2 = NULL;    
    sv = NULL;  
    if(FAILED(hr = XAudio2Create(&pXA2))) {
        MessageBoxW(NULL,L"The TFDi 737 Extreme Sound system failed to initialize the XAudio2 engine.rnrnError: FAILED(hr = XAudio2Create(&pXA2, flags))",L"TFDi 737XS Sound Error",MB_OK + MB_ICONERROR);
        return;
    }   
    if(FAILED(hr = pXA2->CreateMasteringVoice(&mv))) {
        MessageBoxW(NULL,L"The TFDi 737 Extreme Sound system failed to initialize the XAudio2 mastering voice.rnrnError: FAILED(hr = pXA2->CreateMasteringVoice(&mv))",L"TFDi 737XS Sound Error",MB_OK + MB_ICONERROR);
        return;
    }   
}
XA2SoundEngine::~XA2SoundEngine() {
    if(sv)
        sv->DestroyVoice();
    if(mv)
        mv->DestroyVoice();
    if(pXA2)
        pXA2->Release();
    CoUninitialize();
}
BOOL XA2SoundEngine::Play(LPWSTR file, int mode) {
        HRESULT hr;
        WaveFile wf;
        if(FAILED(hr = wf.Open(file)))
            return XA2SE_ERROR_COULD_NOT_OPEN_FILE; 
        WAVEFORMATEX* pwfx = wf.GetFormat();
        DWORD wavesize = wf.GetSize();
        BYTE* wavedata = new BYTE[wavesize];    
        if(FAILED(hr = wf.Read(wavedata, wavesize, &wavesize)))
            return XA2SE_ERROR_COULD_NOT_READ;
        if(soundengine->sv != NULL) {
            soundengine->sv->FlushSourceBuffers();
            soundengine->sv->DestroyVoice();            
        }
        if(FAILED(hr = soundengine->pXA2->CreateSourceVoice(&soundengine->sv,pwfx))) {
            SAFE_DELETE(wavedata);
            return XA2SE_ERROR_COULD_NOT_CREATE_VOICE;
        }

        XAUDIO2_BUFFER buffer = {0};
        buffer.pAudioData = wavedata;
        buffer.Flags = XAUDIO2_END_OF_STREAM;
        buffer.AudioBytes = wavesize;
        buffer.LoopBegin = 0;
        double nos = (double)wavesize / ((pwfx->wBitsPerSample * pwfx->nChannels) / pwfx->nSamplesPerSec);
        buffer.LoopLength = (unsigned int)nos;

        buffer.LoopLength = 0;
        buffer.LoopCount = (mode == XA2SE_PLAY_LOOP ? XAUDIO2_LOOP_INFINITE : 0);
        if(FAILED(hr = soundengine->sv->SubmitSourceBuffer(&buffer))) {                     
            SAFE_DELETE_ARRAY(wavedata);
            return XA2SE_COULD_NOT_SUBMIT_BUFFER;
        }
        hr = soundengine->sv->Start(0);     
        //SAFE_DELETE_ARRAY(wavedata);
    return TRUE;
}
WaveFile::WaveFile() {
    m_pwfx = NULL;
    m_hmmio = NULL;
    m_pResourceBuffer = NULL;
    m_dwSize = 0;    
}
WaveFile::~WaveFile() {
    Close();
    SAFE_DELETE_ARRAY(m_pwfx);
}
HRESULT WaveFile::Open( LPWSTR strFileName)
{
    HRESULT hr;
    if( strFileName == NULL )
        return E_INVALIDARG;
    SAFE_DELETE_ARRAY(m_pwfx);
    m_hmmio = mmioOpenW( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ );
    if( NULL == m_hmmio ) {
        return XA2SE_ERROR_COULD_NOT_OPEN_FILE;
    }
    if(FAILED(hr = ReadMMIO())) {
        // ReadMMIO will fail if its an not a wave file
        mmioClose( m_hmmio, 0 );
        return XA2SE_ERROR_NOT_WAVE_FILE;
    }
    if( FAILED( hr = ResetFile() ) )
        return XA2SE_ERROR_COULD_NOT_RESET;
    // After the reset, the size of the wav file is m_ck.cksize so store it now
    m_dwSize = m_ck.cksize;
    return hr;
}
HRESULT WaveFile::ReadMMIO()
{
    MMCKINFO ckIn;           // chunk info. for general use.
    PCMWAVEFORMAT pcmWaveFormat;  // Temp PCM structure to load in.
    memset( &ckIn, 0, sizeof(ckIn) );
    m_pwfx = NULL;
    if((0 != mmioDescend(m_hmmio,&m_ckRiff,NULL,0)))
        return XA2SE_ERROR_COULD_NOT_DESCEND;
    // Check to make sure this is a valid wave file
    if((m_ckRiff.ckid != FOURCC_RIFF) || (m_ckRiff.fccType != mmioFOURCC('W','A','V','E')))
        return XA2SE_ERROR_RIFF_ERROR;
    // Search the input file for for the 'fmt ' chunk.
    ckIn.ckid = mmioFOURCC( 'f', 'm', 't', ' ' );
    if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) )
        return XA2SE_ERROR_COULD_NOT_DESCEND;
    // Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>;
    // if there are extra parameters at the end, we'll ignore them
    if( ckIn.cksize < ( LONG )sizeof( PCMWAVEFORMAT ) )
        return XA2SE_ERROR_SIZE_ERROR;
    // Read the 'fmt ' chunk into <pcmWaveFormat>.
    if( mmioRead( m_hmmio, ( HPSTR )&pcmWaveFormat,
        sizeof( pcmWaveFormat ) ) != sizeof( pcmWaveFormat ) )
        return XA2SE_ERROR_COULD_NOT_READ;
    // Allocate the waveformatex, but if its not pcm format, read the next
    // word, and thats how many extra bytes to allocate.
    if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM )
    {
        m_pwfx = ( WAVEFORMATEX* )new CHAR[ sizeof( WAVEFORMATEX ) ];
        if( NULL == m_pwfx )
            return XA2SE_ERROR_UNEXPECTED_NULL_VALUE;
        // Copy the bytes from the pcm structure to the waveformatex structure
        memcpy( m_pwfx, &pcmWaveFormat, sizeof( pcmWaveFormat ) );
        m_pwfx->cbSize = 0;
    }
    else
    {
        // Read in length of extra bytes.
        WORD cbExtraBytes = 0L;
        if( mmioRead( m_hmmio, ( CHAR* )&cbExtraBytes, sizeof( WORD ) ) != sizeof( WORD ) )
            return XA2SE_ERROR_COULD_NOT_READ;
        m_pwfx = ( WAVEFORMATEX* )new CHAR[ sizeof( WAVEFORMATEX ) + cbExtraBytes ];
        if( NULL == m_pwfx )
            return XA2SE_ERROR_UNEXPECTED_NULL_VALUE;
        // Copy the bytes from the pcm structure to the waveformatex structure
        memcpy( m_pwfx, &pcmWaveFormat, sizeof( pcmWaveFormat ) );
        m_pwfx->cbSize = cbExtraBytes;
        // Now, read those extra bytes into the structure, if cbExtraAlloc != 0.
        if( mmioRead( m_hmmio, ( CHAR* )( ( ( BYTE* )&( m_pwfx->cbSize ) ) + sizeof( WORD ) ),
            cbExtraBytes ) != cbExtraBytes )
        {
            SAFE_DELETE( m_pwfx );
            return XA2SE_ERROR_COULD_NOT_READ;
        }
    }
    // Ascend the input file out of the 'fmt ' chunk.
    if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) )
    {
        SAFE_DELETE( m_pwfx );
        return XA2SE_ERROR_COULD_NOT_ASCEND;
    }
    return S_OK;
}
DWORD WaveFile::GetSize() {
    return m_dwSize;
}
HRESULT WaveFile::ResetFile() {
    if( m_hmmio == NULL )
        return CO_E_NOTINITIALIZED;
    // Seek to the data
    if( -1 == mmioSeek( m_hmmio, m_ckRiff.dwDataOffset + sizeof( FOURCC ),
        SEEK_SET ) )
        return XA2SE_ERROR_COULD_NOT_SEEK;
    // Search the input file for the 'data' chunk.
    m_ck.ckid = mmioFOURCC( 'd', 'a', 't', 'a' );
    if( 0 != mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) )
        return XA2SE_ERROR_COULD_NOT_DESCEND;
        return S_OK;
}
HRESULT WaveFile::Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead ) {
    MMIOINFO mmioinfoIn; // current status of m_hmmio
    if( m_hmmio == NULL )
        return CO_E_NOTINITIALIZED;
    if( pBuffer == NULL || pdwSizeRead == NULL )
        return E_INVALIDARG;
    *pdwSizeRead = 0;
    if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) )
        return XA2SE_ERROR_COULD_NOT_GET_INFO;
    UINT cbDataIn = dwSizeToRead;
    if( cbDataIn > m_ck.cksize )
        cbDataIn = m_ck.cksize;
    m_ck.cksize -= cbDataIn;
    for( DWORD cT = 0; cT < cbDataIn; cT++ )
    {
        // Copy the bytes from the io to the buffer.
        if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
        {
            if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) )
                return XA2SE_ERROR_COULD_NOT_ADVANCE;
            if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
                return XA2SE_ERROR_COULD_NOT_READEND;
        }
        // Actual copy.
        *( ( BYTE* )pBuffer + cT ) = *( ( BYTE* )mmioinfoIn.pchNext );
        mmioinfoIn.pchNext++;
    }
    if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) )
        return XA2SE_ERROR_COULD_NOT_SET_INFO;
    *pdwSizeRead = cbDataIn;
    return S_OK;
}
HRESULT WaveFile::Close() {
    if ( m_hmmio != NULL ) {
        mmioClose( m_hmmio, 0 );
        m_hmmio = NULL;
    }
    SAFE_DELETE_ARRAY( m_pResourceBuffer );
    return S_OK;
}

我碰巧在某些事情上使用XAudio,我检查了我的代码。奇怪的是,我有一个评论说不要在AudioDevice析构函数本身中使用CoUninitialize();,我记得为什么:

你正在用CoInitializeEx( NULL, COINIT_MULTITHREADED );初始化COM。这意味着除了您的应用程序之外,还有其他线程(特别是其他XAudio线程)在部分地超出您的控制。当你在XA2SoundEngine的析构函数中执行CoUninitialize()时,该线程可能还没有退出或清理完它的资源。从本质上讲,你要求COM做一些它可能还没有准备好做的杀戮(这是我最好的猜测,因为我自己一直得到访问违规)。

我修复它的方式只是CoUninitialize()。在XAudio2的新版本中,微软意识到不告诉你它正在使用的线程何时正在生成或死亡是多么愚蠢,所以XAudio2(2.8,我相信,在Windows 8上)为你做了CoInitializeExCoUninitialize。如果你处于这种环境中,你首先就不应该调用这些方法。

如果这发生在DLL中,当DLL附加自身时,您可以安全地调用CoUninitialize和CoInitializeEx。它将运行无错误:

BOOL APIENTRY DllMain ( HANDLE hModule, DWORD dwReason, LPVOID lpReserved ) {
    switch (dwReason) {
    case DLL_PROCESS_ATTACH:
        CoInitializeEx(NULL, COINIT_MULTITHREADED);
        break;
    case DLL_THREAD_ATTACH:
        CoInitializeEx(NULL, COINIT_MULTITHREADED);
        break;
    case DLL_THREAD_DETACH:
        CoUninitialize();
        break;
    case DLL_PROCESS_DETACH:
        CoUninitialize();
        break;
    default:
        // Wat...
        break;
    }
    return TRUE;
}

也许COM专家可以告诉我我是否做错了,但到目前为止,上面的方法对我来说是有效的。

祝你好运!

问题最终变成了一堆事情,包括我在基于DirectX9的环境中使用Windows 8版本的XAudio2,我误用了CoInitialize()函数,以及我从未明确调用"delete soundengine"的事实。从那以后,我对这个类做了一些修改,现在它工作得很好。

谢谢你们了!

遇到了同样的问题(或者至少是一个非常相似的问题),我的解决方案是将IXAudio2*的两个实例作为关闭过程中的最后一个实例适当地释放,如下所示:

SafeRelease(&m_musicEngine);
SafeRelease(&m_soundEffectEngine);

其中SafeRelease是这个实用程序:

template <class T> void SafeRelease(T **ppT)
{
    if (*ppT)
    {
        (*ppT)->Release();
        *ppT = NULL;
    }
}

只有一个delete m_musicEngine;在错误的顺序,我的游戏一直抛出访问违反异常在XAudio_N.dll -这是在Windows 10平台SDK版本10.0.17134.0

现在它干净地退出了。值得注意的是,MS自己在他们的官方SDK示例中包含了DirectX音频的异常处理程序,看起来像这样:

void  _stdcall AudioEngineCallbacks::OnCriticalError(HRESULT Error)

,并给出如下解释:

// This [...] used to tell when the audio system
// is experiencing critial errors.
// XAudio2 gives a critical error when the user unplugs
// the headphones and a new speaker configuration is generated.

所以这种错误显然是预料之中的,即使在音频引擎寿命得到适当维护的情况下。