我正在尝试使用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.\r\n\r\nError: 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.\r\n\r\nError: 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(我相信在Windows 8上为2.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专家可以告诉我是否做错了,但是到目前为止,以上内容对我来说一直有效。

祝好运!

07-24 12:50