//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1998 - 1999 // // File: wave.cpp // //-------------------------------------------------------------------------- #include "stdafx.h" #include "Direct.h" #include "dSound.h" #include "dms.h" #include #include // FOURCC codes #undef FOURCC_RIFF #define FOURCC_RIFF 'FFIR' #undef FOURCC_MEM #define FOURCC_MEM ' MEM' #undef FOURCC_WAVE #define FOURCC_WAVE 'EVAW' #undef FOURCC_FORMAT #define FOURCC_FORMAT ' tmf' #undef FOURCC_DATA #define FOURCC_DATA 'atad' #define RPF(level,str,err) \ { char outBuf[MAX_PATH]; \ wsprintf(outBuf,str,err); \ OutputDebugString(outBuf); \ } #define DPFLVL_ERROR 1 /*************************************************************************** * * FillWfx * * Description: * Fills a WAVEFORMATEX structure, given only the necessary values. * * Arguments: * LPWAVEFORMATEX [out]: structure to fill. * WORD [in]: number of channels. * DWORD [in]: samples per second. * WORD [in]: bits per sample. * * Returns: * (void) * ***************************************************************************/ #undef DPF_FNAME void FillWfx(LPWAVEFORMATEX pwfx, WORD wChannels, DWORD dwSamplesPerSec, WORD wBitsPerSample) { pwfx->wFormatTag = WAVE_FORMAT_PCM; pwfx->nChannels = min(2, max(1, wChannels)); pwfx->nSamplesPerSec = min(DSBFREQUENCY_MAX, max(DSBFREQUENCY_MIN, dwSamplesPerSec)); if(wBitsPerSample < 12) { pwfx->wBitsPerSample = 8; } else { pwfx->wBitsPerSample = 16; } pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8; pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign; pwfx->cbSize = 0; } #if 0 HRESULT InternalCreateSoundBuffer(LPDSBUFFERDESC pDsbDesc, byte *pbWaveData,DWORD cbWaveData,LPDIRECTSOUND lpDirectSound, LPDIRECTSOUNDBUFFER *lplpDirectSoundBuffer) { HRESULT hr = DS_OK; HACMSTREAM has = NULL; BOOL fPrep = FALSE; ACMSTREAMHEADER ash; DWORD dwBufferBytes; LPVOID pvWrite; DWORD cbWrite; HMMIO hmm = NULL; MMRESULT mmr; MMIOINFO mmioinfo; MMCKINFO ckiRiff; MMCKINFO cki; LPWAVEFORMATEX pwfxSrcFormat = NULL; LPWAVEFORMATEX pwfxDestFormat = NULL; BOOL bNULLFORMAT = FALSE; ZeroMemory(&mmioinfo, sizeof(mmioinfo)); if(SUCCEEDED(hr)){ mmioinfo.fccIOProc = FOURCC_MEM; mmioinfo.pchBuffer = (HPSTR)pbWaveData; mmioinfo.cchBuffer = cbWaveData; hmm = mmioOpen(NULL, &mmioinfo, MMIO_READ); if(!hmm) { DPF1(1, "Unable to open file via MMIO. Error %lu", mmioinfo.wErrorRet); hr = E_FAIL; } } // Decend into the RIFF chunk if(SUCCEEDED(hr)) { ckiRiff.ckid = FOURCC_RIFF; mmr = mmioDescend(hmm, &ckiRiff, NULL, MMIO_FINDCHUNK); if(MMSYSERR_NOERROR != mmr) { DPF1(1, "Unable to descend into RIFF chunk. Error %lu", mmr); hr = E_FAIL; } } // Verify that this is a wave file if(SUCCEEDED(hr) && FOURCC_WAVE != ckiRiff.fccType) { DPF1(1, "File is not type WAVE %d",GetLastError()); hr = DSERR_BADFORMAT; } // Decend into the format chunk if(SUCCEEDED(hr)) { cki.ckid = FOURCC_FORMAT; mmr = mmioDescend(hmm, &cki, &ckiRiff, MMIO_FINDCHUNK); if(MMSYSERR_NOERROR != mmr) { DPF1(1, "Unable to descend into format chunk. Error %lu", mmr); hr = E_FAIL; //MMRESULTtoHRESULT(mmr); } if(SUCCEEDED(hr)) { pwfxSrcFormat = (LPWAVEFORMATEX)(pbWaveData + cki.dwDataOffset); } } // Ascend out of the format chunk if(SUCCEEDED(hr)) { mmr = mmioAscend(hmm, &cki, 0); if(MMSYSERR_NOERROR != mmr) { DPF(1, "Unable to ascend out of format chunk. Error %lu", mmr); hr = E_FAIL; //MMRESULTtoHRESULT(mmr); } } // Descend into the data chunk if(SUCCEEDED(hr)) { cki.ckid = FOURCC_DATA; mmr = mmioDescend(hmm, &cki, &ckiRiff, MMIO_FINDCHUNK); if(MMSYSERR_NOERROR != mmr) { RPF(DPFLVL_ERROR, "Unable to descend into data chunk. Error %lu", mmr); hr = E_FAIL; //MMRESULTtoHRESULT(mmr); } } // Prepare PCM conversion if(SUCCEEDED(hr)) { if(WAVE_FORMAT_PCM == pwfxSrcFormat->wFormatTag) { // Populate the buffer description dwBufferBytes = cki.cksize; pwfxDestFormat = pwfxSrcFormat; } else { // Open an ACM conversion stream mmr = acmStreamOpen(&has, NULL, (LPWAVEFORMATEX)pwfxSrcFormat, pwfxDestFormat, NULL, 0, 0, 0); if(MMSYSERR_NOERROR != mmr) { RPF(DPFLVL_ERROR, "Unable to open an ACM stream. Error %lu", mmr); hr = E_FAIL; //MMRESULTtoHRESULT(mmr); } // Get the size of the PCM data if(SUCCEEDED(hr)) { mmr = acmStreamSize(has, cki.cksize, &dwBufferBytes, ACM_STREAMSIZEF_SOURCE); if(MMSYSERR_NOERROR != mmr) { RPF(DPFLVL_ERROR, "Unable to determine converted data size. Error %lu", mmr); hr = E_FAIL; //MMRESULTtoHRESULT(mmr); } } // Create the destination format if(SUCCEEDED(hr)) { pwfxDestFormat = (WAVEFORMATEX*)malloc(sizeof(WAVEFORMATEX)); if (pwfxDestFormat==NULL) hr=E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { FillWfx(pwfxDestFormat, pwfxSrcFormat->nChannels, pwfxSrcFormat->nSamplesPerSec, pwfxSrcFormat->wBitsPerSample); } } } LPDIRECTSOUNDBUFFER lpDirectSoundBuffer=NULL; if(SUCCEEDED(hr)) { //hr = InitializeEmpty(pDsbDesc->dwFlags, dwBufferBytes, pwfxDestFormat, NULL); pDsbDesc->dwBufferBytes=dwBufferBytes; if (pDsbDesc->lpwfxFormat){ memcpy(pDsbDesc->lpwfxFormat,pwfxDestFormat,sizeof(WAVEFORMATEX)); } else { pDsbDesc->lpwfxFormat=pwfxDestFormat; } hr=lpDirectSound->CreateSoundBuffer(pDsbDesc,lplpDirectSoundBuffer,NULL); if (*lplpDirectSoundBuffer==NULL) hr= E_FAIL; lpDirectSoundBuffer=*lplpDirectSoundBuffer; } // Lock the buffer in order to write the PCM data to it if(SUCCEEDED(hr)) { hr = lpDirectSoundBuffer->Lock(0, dwBufferBytes, &pvWrite, &cbWrite, NULL, NULL,0); } // Convert to PCM if(SUCCEEDED(hr)) { if(WAVE_FORMAT_PCM == pwfxSrcFormat->wFormatTag) { CopyMemory(pvWrite, pbWaveData + cki.dwDataOffset, cbWrite); } else { // Prepare the conversion header ZeroMemory(&ash, sizeof(ash)); ash.cbStruct = sizeof(ash); ash.pbSrc = pbWaveData + cki.dwDataOffset; ash.cbSrcLength = cki.cksize; ash.pbDst = (LPBYTE)pvWrite; ash.cbDstLength = cbWrite; mmr = acmStreamPrepareHeader(has, &ash, 0); if(MMSYSERR_NOERROR != mmr) { RPF(DPFLVL_ERROR, "Unable to prepare ACM stream header. Error %lu", mmr); hr = E_FAIL; //MMRESULTtoHRESULT(mmr); } fPrep = SUCCEEDED(hr); // Convert the buffer if(SUCCEEDED(hr)) { mmr = acmStreamConvert(has, &ash, 0); if(MMSYSERR_NOERROR != mmr) { RPF(DPFLVL_ERROR, "Unable to convert wave data. Error %lu", mmr); hr = E_FAIL; //MMRESULTtoHRESULT(mmr); } } } } // Unlock the buffer if(SUCCEEDED(hr)) { hr = lpDirectSoundBuffer->Unlock(pvWrite, cbWrite, NULL, 0); } // Clean up if(fPrep) { acmStreamUnprepareHeader(has, &ash, 0); } if(has) { acmStreamClose(has, 0); } if(hmm) { mmioClose(hmm, 0); } if(pwfxDestFormat != pwfxSrcFormat) { free(pwfxDestFormat); } return hr; } #endif /////////////////////////////////////////////////////////////////////////////////////////// HRESULT InternalCreateSoundBuffer(LPDSBUFFERDESC pDsbDesc, byte *pbWaveData, DWORD cbWaveData,LPDIRECTSOUND lpDirectSound, LPDIRECTSOUNDBUFFER8 *lplpDirectSoundBuffer) { HRESULT hr = DS_OK; HACMSTREAM has = NULL; BOOL fPrep = FALSE; DWORD dwBufferBytes = 0; LPVOID pvWrite = NULL; DWORD cbWrite = 0; LPWAVEFORMATEX pwfxFormat = NULL; LPWAVEFORMATEX pwfxSrcFormat = NULL; LPWAVEFORMATEX pwfxDestFormat = NULL; MMRESULT mmr = 0; DWORD dwDataLength = 0; DWORD dwOffset = 0; char *pChunk = NULL; LPDIRECTSOUNDBUFFER lpDirectSoundBuffer = NULL; ACMSTREAMHEADER ash; BOOL bNULLFORMAT =FALSE; BOOL bDirty =FALSE; struct tag_FileHeader { DWORD dwRiff; DWORD dwFileSize; DWORD dwWave; DWORD dwFormat; DWORD dwFormatLength; } FileHeader; ZeroMemory(&FileHeader,sizeof(struct tag_FileHeader)); // If our file is big enough to have a header copy it over // other wise error out if (cbWaveData>sizeof(struct tag_FileHeader)) { memcpy(&FileHeader,pbWaveData,sizeof(struct tag_FileHeader)); } else { hr= E_INVALIDARG; } // File must be a riff file ( 52 R, 49 I, 46 F, 46 F) if (FileHeader.dwRiff != 0x46464952) { DPF(1, "DXVB: not a RIFF file\n"); return E_INVALIDARG; } // must be a WAVE format ( 57 W, 41 A, 56 V, 45 E ) if (FileHeader.dwWave != 0x45564157) { DPF(1, "DXVB: not a WAVE file\n"); return E_INVALIDARG; } // check for odd stuff // note 18bytes is a typical WAVEFORMATEX if (FileHeader.dwFormatLength <= 14) return E_INVALIDARG; if (FileHeader.dwFormatLength > 1000) return E_INVALIDARG; //allocate the waveformat pwfxFormat=(WAVEFORMATEX*)alloca(FileHeader.dwFormatLength); if (!pwfxFormat) return E_OUTOFMEMORY; //copy it to our own data structure pChunk=(char*)(pbWaveData+sizeof (struct tag_FileHeader)); memcpy(pwfxFormat,pChunk,FileHeader.dwFormatLength); // Now look for the next chunk after the WaveFormat pChunk=(char*)(pChunk+FileHeader.dwFormatLength); // Look for option FACT chunk and skip it // (66 F, 61 A, 63 C, 74 T) // this chunk is required for compressed wave files // but is optional for PCM // if ( ((DWORD*)pChunk)[0]==0x74636166) { dwOffset=((DWORD*)pChunk)[1]; dwBufferBytes=((DWORD*)pChunk)[2]; //number of bytes of PCM data pChunk =(char*)(pChunk+ dwOffset+8); } //Look for required data chunk // (64 D, 61 A, 74 T, 61 A) if (((DWORD*)pChunk)[0]!=0x61746164) { DPF(1, "DXVB: no DATA chunk in wave file\n"); return E_INVALIDARG; } dwDataLength=((DWORD*)pChunk)[1]; pChunk=(char*)(pChunk+8); //IF we assume PCM //pcm files are not required to have their fact chunk //so be ware they may missreport the data length dwBufferBytes=dwDataLength; pwfxDestFormat=pwfxSrcFormat=pwfxFormat; // if we are not PCM then we need to do some things first if (pwfxFormat->wFormatTag!=WAVE_FORMAT_PCM) { // source format is from the file pwfxSrcFormat=pwfxFormat; //from file pwfxDestFormat=pDsbDesc->lpwfxFormat ; //from user //pick the format of the file passed in FillWfx(pwfxDestFormat, pwfxSrcFormat->nChannels, pwfxSrcFormat->nSamplesPerSec, pwfxSrcFormat->wBitsPerSample); // Open an ACM conversion stream mmr = acmStreamOpen(&has, NULL, (LPWAVEFORMATEX)pwfxSrcFormat, pwfxDestFormat, NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME ); if(MMSYSERR_NOERROR != mmr) { DPF1(1, "Unable to open an ACM stream. Error %lu\n", mmr); return E_FAIL; } // Get the size of the PCM data mmr = acmStreamSize(has, dwDataLength, &dwBufferBytes, ACM_STREAMSIZEF_SOURCE); if(MMSYSERR_NOERROR != mmr) { DPF1(1, "Unable to determine converted data size. Error %lu\n", mmr); return E_FAIL; //MMRESULTtoHRESULT(mmr); } // Allocate a DestFormat struct //pwfxDestFormat = (WAVEFORMATEX*)alloca(sizeof(WAVEFORMATEX)); //if (!pwfxDestFormat) return E_OUTOFMEMORY; // Fill the format with information from the source but // FillWfx sets the format to PCM //FillWfx(pwfxDestFormat, pwfxSrcFormat->nChannels, pwfxSrcFormat->nSamplesPerSec, pwfxSrcFormat->wBitsPerSample); } // fill the buffer desc the user passed in with the buffer bytes // this is the number of PCM bytes pDsbDesc->dwBufferBytes=dwBufferBytes; // if they provide us a pointer to a waveformatex // copy over the format to the input desc and use it // otherwise have it point to our data format temprarily if (pDsbDesc->lpwfxFormat){ memcpy(pDsbDesc->lpwfxFormat,pwfxDestFormat,sizeof(WAVEFORMATEX)); } else { pDsbDesc->lpwfxFormat=pwfxDestFormat; //make sure we null out the format before passing it back to the user //NOTE: consider the problems in a multithreaded enviroment //where the users data structures are being accesed by multiple //threads... on the other hand if thats going on.. //then the user would need to syncronize things on his or her own //for everything else including calling into apis that fill structures.. bNULLFORMAT=TRUE; } LPDIRECTSOUNDBUFFER dsbTemp = NULL; // Create the buffer if (FAILED(hr=lpDirectSound->CreateSoundBuffer(pDsbDesc,&dsbTemp,NULL) ) ) return hr; hr = dsbTemp->QueryInterface(IID_IDirectSoundBuffer8, (void**) lplpDirectSoundBuffer); dsbTemp->Release(); if (FAILED(hr)) return hr; if (*lplpDirectSoundBuffer==NULL) return E_FAIL; //todo ASSERT this instead.. // for more convenient referencing... lpDirectSoundBuffer=*lplpDirectSoundBuffer; // Lock the buffer in order to write the PCM data to it // cbWrite will contain the number of locked bytes hr = lpDirectSoundBuffer->Lock(0, dwBufferBytes, &pvWrite, &cbWrite, NULL, NULL,0); if FAILED(hr) return hr; // If the sorce format was pcm then copy from the file to the buffer if(WAVE_FORMAT_PCM == pwfxSrcFormat->wFormatTag) { CopyMemory(pvWrite, pChunk, cbWrite); // Unlock the buffer hr = lpDirectSoundBuffer->Unlock(pvWrite, cbWrite, NULL, 0); if (FAILED(hr)) { DPF(1, "DXVB: lpDirectSoundBuffer->Unlock failed.. \n"); return hr; } } // if the source format is compressed then convert first then copy else { // Prepare the conversion header ZeroMemory(&ash, sizeof(ash)); ash.cbStruct = sizeof(ash); ash.pbSrc = (unsigned char*)pChunk; //start of compressed data ash.cbSrcLength = dwDataLength; //number of bytes of compressed data ash.pbDst = (LPBYTE)pvWrite; //where to put the decompressed data ash.cbDstLength = cbWrite; //how big is that buffer mmr = acmStreamPrepareHeader(has, &ash, 0); if(MMSYSERR_NOERROR != mmr) { DPF1(1, "DXVB: Unable to prepare ACM stream header. Error %lu \n", mmr); return E_FAIL; } mmr = acmStreamConvert(has, &ash, 0); if(MMSYSERR_NOERROR != mmr) { DPF1(1, "DXVB: Unable to convert wave data. Error %lu \n", mmr); return hr; } // Unlock the buffer hr = lpDirectSoundBuffer->Unlock(pvWrite, cbWrite, NULL, 0); if (FAILED(hr)) { DPF(1, "DXVB: lpDirectSoundBuffer->Unlock failed.. \n"); return hr; } acmStreamUnprepareHeader(has, &ash, 0); acmStreamClose(has, 0); } if (bNULLFORMAT){ pDsbDesc->lpwfxFormat=NULL; } return hr; } HRESULT InternalCreateSoundBufferFromFile(LPDIRECTSOUND8 lpDirectSound,LPDSBUFFERDESC pDesc,WCHAR *file,LPDIRECTSOUNDBUFFER8 *lplpDirectSoundBuffer) { HRESULT hr=S_OK; HANDLE hFile = NULL; HANDLE hFileMapping = NULL; DWORD cbWaveData; LPBYTE pbWaveData = NULL; #pragma message("CreateFileW should be used for localization why wont it work") //hFile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); USES_CONVERSION; LPSTR pStrA=W2T(file); hFile = CreateFileA(pStrA, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if(INVALID_HANDLE_VALUE == hFile) { hFile = NULL; } if(!hFile) { RPF(DPFLVL_ERROR, "Unable to open file. Error %lu", GetLastError()); hr=STG_E_FILENOTFOUND; return hr; } if(hFile) { cbWaveData = GetFileSize(hFile, NULL); if(-1 == cbWaveData) { RPF(DPFLVL_ERROR, "Unable to get file size. Error %lu", GetLastError()); hr = E_FAIL; //DSERR_FILEREADFAULT; } } if(SUCCEEDED(hr)) { hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, cbWaveData, NULL); if(INVALID_HANDLE_VALUE == hFileMapping) { hFileMapping = NULL; } if(!hFileMapping) { RPF(DPFLVL_ERROR, "Unable to create file mapping. Error %lu", GetLastError()); hr = E_FAIL; //DSERR_FILEREADFAULT; } } if(SUCCEEDED(hr)) { pbWaveData = (LPBYTE)MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, cbWaveData); if(!pbWaveData) { RPF(DPFLVL_ERROR, "Unable to map view of file. Error %lu", GetLastError()); hr = E_FAIL; //DSERR_FILEREADFAULT; } } if(SUCCEEDED(hr)) { hr=InternalCreateSoundBuffer(pDesc, pbWaveData, cbWaveData,lpDirectSound, lplpDirectSoundBuffer); } if(pbWaveData) { UnmapViewOfFile(pbWaveData); } if(hFileMapping) { CloseHandle(hFileMapping); } if(hFile) { CloseHandle(hFile); } return hr; } HRESULT InternalCreateSoundBufferFromResource(LPDIRECTSOUND8 lpDirectSound,LPDSBUFFERDESC pDesc,HANDLE resHandle,WCHAR *resName,LPDIRECTSOUNDBUFFER8 *lplpDirectSoundBuffer) { const LPCSTR apszResourceTypeA[] = { "WAVE", "WAV" }; const LPCWSTR apszResourceTypeW[] = { L"WAVE", L"WAV" }; UINT cResourceType = 2; HRSRC hRsrc = NULL; DWORD cbWaveData; LPBYTE pbWaveData = NULL; HRESULT hr=S_OK; LPCDSBUFFERDESC pDsbDesc=pDesc; while(!hRsrc && cResourceType--) { hRsrc = FindResourceW((HINSTANCE)resHandle, resName, apszResourceTypeW[cResourceType]); } if(!hRsrc) { RPF(DPFLVL_ERROR,"Unable to find resource. Error %lu", GetLastError()); hr = STG_E_FILENOTFOUND; } if(SUCCEEDED(hr)) { cbWaveData = SizeofResource((HINSTANCE)resHandle, hRsrc); if(!cbWaveData) { RPF(DPFLVL_ERROR, "Unable to get resource size. Error %lu", GetLastError()); hr = E_FAIL; } } if(SUCCEEDED(hr)) { pbWaveData = (LPBYTE)LoadResource((HINSTANCE)resHandle, hRsrc); if(!pbWaveData) { RPF(DPFLVL_ERROR, "Unable to load resource. Error %lu", GetLastError()); hr = E_FAIL; } } if(SUCCEEDED(hr)) { hr=InternalCreateSoundBuffer(pDesc, pbWaveData, cbWaveData,lpDirectSound, lplpDirectSoundBuffer); } //loadResource return hr; } HRESULT InternalSaveToFile(IDirectSoundBuffer *pBuff,BSTR file) { WAVEFORMATEX waveFormat; DWORD dwWritten=0; DWORD dwBytes=0; LPBYTE lpByte=NULL; HRESULT hr; HANDLE hFile=NULL; if (!pBuff) return E_FAIL; if (!file) return E_INVALIDARG; pBuff->GetFormat(&waveFormat,sizeof(WAVEFORMATEX),NULL); USES_CONVERSION; LPSTR pStrA=W2T(file); hFile = CreateFile ( pStrA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if (INVALID_HANDLE_VALUE != hFile) { struct tag_FileHeader { DWORD dwRiff; DWORD dwFileSize; DWORD dwWave; DWORD dwFormat; DWORD dwFormatLength; WORD wFormatTag; WORD nChannels; DWORD nSamplesPerSec; DWORD nAvgBytesPerSec; WORD nBlockAlign; WORD wBitsPerSample; DWORD dwData; DWORD dwDataLength; } FileHeader; hr=pBuff->Lock(0,0,(void**)&lpByte,&dwBytes,NULL,NULL,DSBLOCK_ENTIREBUFFER); if FAILED(hr) { CloseHandle(hFile); return hr; } FileHeader.dwRiff = 0x46464952; // RIFF FileHeader.dwWave = 0x45564157; // WAVE FileHeader.dwFormat = 0x20746D66; // fmt_chnk FileHeader.dwFormatLength = 16; FileHeader.wFormatTag = WAVE_FORMAT_PCM; FileHeader.nChannels = waveFormat.nChannels ; FileHeader.nSamplesPerSec = waveFormat.nSamplesPerSec ; FileHeader.wBitsPerSample = waveFormat.wBitsPerSample ; FileHeader.nBlockAlign = FileHeader.wBitsPerSample / 8 * FileHeader.nChannels; FileHeader.nAvgBytesPerSec = FileHeader.nSamplesPerSec * FileHeader.nBlockAlign; FileHeader.dwData = 0x61746164; // data_chnk FileHeader.dwDataLength = dwBytes; FileHeader.dwFileSize = dwBytes + sizeof(FileHeader); WriteFile(hFile, &FileHeader, sizeof(FileHeader), &dwWritten, NULL); WriteFile(hFile, lpByte, dwBytes, &dwWritten, NULL); hr=pBuff->Unlock(lpByte,0,NULL,0); CloseHandle(hFile); } else{ return E_FAIL; } return S_OK; }