552 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			552 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
| #include "..\e2dtool.h"
 | ||
| #include "..\e2dmodule.h"
 | ||
| 
 | ||
| 
 | ||
| #ifndef SAFE_DELETE
 | ||
| #define SAFE_DELETE(p)       { if (p) { delete (p); (p)=nullptr; } }
 | ||
| #endif
 | ||
| 
 | ||
| #ifndef SAFE_DELETE_ARRAY
 | ||
| #define SAFE_DELETE_ARRAY(p) { if (p) { delete[] (p); (p)=nullptr; } }
 | ||
| #endif
 | ||
| 
 | ||
| inline bool TraceError(wchar_t* prompt)
 | ||
| {
 | ||
| 	WARN("Music error: %s failed!", prompt);
 | ||
| 	return false;
 | ||
| }
 | ||
| 
 | ||
| inline bool TraceError(wchar_t* prompt, HRESULT hr)
 | ||
| {
 | ||
| 	WARN("Music error: %s (%#X)", prompt, hr);
 | ||
| 	return false;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| e2d::Music::Music()
 | ||
| 	: opened_(false)
 | ||
| 	, wfx_(nullptr)
 | ||
| 	, hmmio_(nullptr)
 | ||
| 	, buffer_(nullptr)
 | ||
| 	, wave_data_(nullptr)
 | ||
| 	, size_(0)
 | ||
| 	, voice_(nullptr)
 | ||
| 	, callback_()
 | ||
| {
 | ||
| }
 | ||
| 
 | ||
| e2d::Music::Music(const e2d::String & file_path)
 | ||
| 	: opened_(false)
 | ||
| 	, wfx_(nullptr)
 | ||
| 	, hmmio_(nullptr)
 | ||
| 	, buffer_(nullptr)
 | ||
| 	, wave_data_(nullptr)
 | ||
| 	, size_(0)
 | ||
| 	, voice_(nullptr)
 | ||
| 	, callback_()
 | ||
| {
 | ||
| 	this->Open(file_path);
 | ||
| }
 | ||
| 
 | ||
| e2d::Music::Music(const Resource& res)
 | ||
| 	: opened_(false)
 | ||
| 	, wfx_(nullptr)
 | ||
| 	, hmmio_(nullptr)
 | ||
| 	, buffer_(nullptr)
 | ||
| 	, wave_data_(nullptr)
 | ||
| 	, size_(0)
 | ||
| 	, voice_(nullptr)
 | ||
| 	, callback_()
 | ||
| {
 | ||
| 	this->Open(res);
 | ||
| }
 | ||
| 
 | ||
| e2d::Music::~Music()
 | ||
| {
 | ||
| 	Close();
 | ||
| }
 | ||
| 
 | ||
| bool e2d::Music::Open(const e2d::String & file_path)
 | ||
| {
 | ||
| 	if (opened_)
 | ||
| 	{
 | ||
| 		Close();
 | ||
| 	}
 | ||
| 
 | ||
| 	if (file_path.IsEmpty())
 | ||
| 	{
 | ||
| 		WARN("Music::Open error: Invalid file name.");
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	File music_file;
 | ||
| 	if (!music_file.Open(file_name))
 | ||
| 	{
 | ||
| 		WARN("Music::Open error: File not found.");
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	// 用户输入的路径不一定是完整路径,因为用户可能通过 File::AddSearchPath 添加
 | ||
| 	// 默认搜索路径,所以需要通过 File::GetPath 获取完整路径
 | ||
| 	String music_file_path = music_file.GetPath();
 | ||
| 
 | ||
| 	// 定位 wave 文件
 | ||
| 	wchar_t pFilePath[MAX_PATH];
 | ||
| 	if (!FindMediaFileCch(pFilePath, MAX_PATH, (const wchar_t *)music_file_path))
 | ||
| 	{
 | ||
| 		WARN("Failed to Find media file: %s", pFilePath);
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	hmmio_ = mmioOpen(pFilePath, nullptr, MMIO_ALLOCBUF | MMIO_READ);
 | ||
| 
 | ||
| 	if (nullptr == hmmio_)
 | ||
| 	{
 | ||
| 		return TraceError(L"mmioOpen");
 | ||
| 	}
 | ||
| 
 | ||
| 	if (!ReadMMIO())
 | ||
| 	{
 | ||
| 		// 读取非 wave 文件时 ReadMMIO 调用失败
 | ||
| 		mmioClose(hmmio_, 0);
 | ||
| 		return TraceError(L"ReadMMIO");
 | ||
| 	}
 | ||
| 
 | ||
| 	if (!ResetFile())
 | ||
| 		return TraceError(L"ResetFile");
 | ||
| 
 | ||
| 	// 重置文件后,wave 文件的大小是 ck_.cksize
 | ||
| 	size_ = ck_.cksize;
 | ||
| 
 | ||
| 	// 将样本数据读取到内存中
 | ||
| 	wave_data_ = new BYTE[size_];
 | ||
| 
 | ||
| 	if (!Read(wave_data_, size_))
 | ||
| 	{
 | ||
| 		TraceError(L"Failed to read WAV data");
 | ||
| 		SAFE_DELETE_ARRAY(wave_data_);
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	// 创建音源
 | ||
| 	auto xAudio2 = Audio::GetInstance()->GetXAudio2();
 | ||
| 	HRESULT hr = xAudio2->CreateSourceVoice(&voice_, wfx_, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &callback_);
 | ||
| 
 | ||
| 	if (FAILED(hr))
 | ||
| 	{
 | ||
| 		TraceError(L"Create source voice error", hr);
 | ||
| 		SAFE_DELETE_ARRAY(wave_data_);
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	opened_ = true;
 | ||
| 	return true;
 | ||
| }
 | ||
| 
 | ||
| bool e2d::Music::Open(const Resource& res)
 | ||
| {
 | ||
| 	if (opened_)
 | ||
| 	{
 | ||
| 		Close();
 | ||
| 	}
 | ||
| 
 | ||
| 	HRSRC hResInfo;
 | ||
| 	HGLOBAL hResData;
 | ||
| 	DWORD dwSize;
 | ||
| 	void* pvRes;
 | ||
| 
 | ||
| 	if (nullptr == (hResInfo = FindResourceW(HINST_THISCOMPONENT, MAKEINTRESOURCE(res.id), (LPCWSTR)res.type)))
 | ||
| 		return TraceError(L"FindResource");
 | ||
| 
 | ||
| 	if (nullptr == (hResData = LoadResource(HINST_THISCOMPONENT, hResInfo)))
 | ||
| 		return TraceError(L"LoadResource");
 | ||
| 
 | ||
| 	if (0 == (dwSize = SizeofResource(HINST_THISCOMPONENT, hResInfo)))
 | ||
| 		return TraceError(L"SizeofResource");
 | ||
| 
 | ||
| 	if (nullptr == (pvRes = LockResource(hResData)))
 | ||
| 		return TraceError(L"LockResource");
 | ||
| 
 | ||
| 	buffer_ = new CHAR[dwSize];
 | ||
| 	memcpy(buffer_, pvRes, dwSize);
 | ||
| 
 | ||
| 	MMIOINFO mmioInfo;
 | ||
| 	ZeroMemory(&mmioInfo, sizeof(mmioInfo));
 | ||
| 	mmioInfo.fccIOProc = FOURCC_MEM;
 | ||
| 	mmioInfo.cchBuffer = dwSize;
 | ||
| 	mmioInfo.pchBuffer = (CHAR*)buffer_;
 | ||
| 
 | ||
| 	hmmio_ = mmioOpen(nullptr, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ);
 | ||
| 
 | ||
| 	if (nullptr == hmmio_)
 | ||
| 	{
 | ||
| 		return TraceError(L"mmioOpen");
 | ||
| 	}
 | ||
| 
 | ||
| 	if (!ReadMMIO())
 | ||
| 	{
 | ||
| 		// 读取非 wave 文件时 ReadMMIO 调用失败
 | ||
| 		mmioClose(hmmio_, 0);
 | ||
| 		return TraceError(L"ReadMMIO");
 | ||
| 	}
 | ||
| 
 | ||
| 	if (!ResetFile())
 | ||
| 		return TraceError(L"ResetFile");
 | ||
| 
 | ||
| 	// 重置文件后,wave 文件的大小是 ck_.cksize
 | ||
| 	size_ = ck_.cksize;
 | ||
| 
 | ||
| 	// 将样本数据读取到内存中
 | ||
| 	wave_data_ = new BYTE[size_];
 | ||
| 
 | ||
| 	if (!Read(wave_data_, size_))
 | ||
| 	{
 | ||
| 		TraceError(L"Failed to read WAV data");
 | ||
| 		SAFE_DELETE_ARRAY(wave_data_);
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	// 创建音源
 | ||
| 	auto xAudio2 = Audio::GetInstance()->GetXAudio2();
 | ||
| 	HRESULT hr = xAudio2->CreateSourceVoice(&voice_, wfx_, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &callback_);
 | ||
| 
 | ||
| 	if (FAILED(hr))
 | ||
| 	{
 | ||
| 		TraceError(L"Create source voice error", hr);
 | ||
| 		SAFE_DELETE_ARRAY(wave_data_);
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	opened_ = true;
 | ||
| 	return true;
 | ||
| }
 | ||
| 
 | ||
| bool e2d::Music::Play(int loop_count)
 | ||
| {
 | ||
| 	if (!opened_)
 | ||
| 	{
 | ||
| 		WARN("Music::Play Failed: Music must be opened first!");
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	if (voice_ == nullptr)
 | ||
| 	{
 | ||
| 		WARN("Music::Play Failed: IXAudio2SourceVoice Null pointer exception!");
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	XAUDIO2_VOICE_STATE state;
 | ||
| 	voice_->GetState(&state);
 | ||
| 	if (state.BuffersQueued)
 | ||
| 	{
 | ||
| 		Stop();
 | ||
| 	}
 | ||
| 
 | ||
| 	loop_count = std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1);
 | ||
| 	loop_count = (loop_count < 0) ? XAUDIO2_LOOP_INFINITE : loop_count;
 | ||
| 
 | ||
| 	// 提交 wave 样本数据
 | ||
| 	XAUDIO2_BUFFER buffer = { 0 };
 | ||
| 	buffer.pAudioData = wave_data_;
 | ||
| 	buffer.Flags = XAUDIO2_END_OF_STREAM;
 | ||
| 	buffer.AudioBytes = size_;
 | ||
| 	buffer.LoopCount = loop_count;
 | ||
| 
 | ||
| 	HRESULT hr;
 | ||
| 	if (FAILED(hr = voice_->SubmitSourceBuffer(&buffer)))
 | ||
| 	{
 | ||
| 		TraceError(L"Submitting source buffer error", hr);
 | ||
| 		voice_->DestroyVoice();
 | ||
| 		SAFE_DELETE_ARRAY(wave_data_);
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	hr = voice_->Start(0);
 | ||
| 
 | ||
| 	return SUCCEEDED(hr);
 | ||
| }
 | ||
| 
 | ||
| void e2d::Music::Pause()
 | ||
| {
 | ||
| 	if (voice_)
 | ||
| 	{
 | ||
| 		voice_->Stop();
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| void e2d::Music::Resume()
 | ||
| {
 | ||
| 	if (voice_)
 | ||
| 	{
 | ||
| 		voice_->Start();
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| void e2d::Music::Stop()
 | ||
| {
 | ||
| 	if (voice_)
 | ||
| 	{
 | ||
| 		if (SUCCEEDED(voice_->Stop()))
 | ||
| 		{
 | ||
| 			voice_->ExitLoop();
 | ||
| 			voice_->FlushSourceBuffers();
 | ||
| 		}
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| void e2d::Music::Close()
 | ||
| {
 | ||
| 	if (voice_)
 | ||
| 	{
 | ||
| 		voice_->Stop();
 | ||
| 		voice_->FlushSourceBuffers();
 | ||
| 		voice_->DestroyVoice();
 | ||
| 		voice_ = nullptr;
 | ||
| 	}
 | ||
| 
 | ||
| 	if (hmmio_ != nullptr)
 | ||
| 	{
 | ||
| 		mmioClose(hmmio_, 0);
 | ||
| 		hmmio_ = nullptr;
 | ||
| 	}
 | ||
| 
 | ||
| 	SAFE_DELETE_ARRAY(buffer_);
 | ||
| 	SAFE_DELETE_ARRAY(wave_data_);
 | ||
| 	SAFE_DELETE_ARRAY(wfx_);
 | ||
| 
 | ||
| 	opened_ = false;
 | ||
| }
 | ||
| 
 | ||
| bool e2d::Music::IsPlaying() const
 | ||
| {
 | ||
| 	if (opened_ && voice_)
 | ||
| 	{
 | ||
| 		XAUDIO2_VOICE_STATE state;
 | ||
| 		voice_->GetState(&state);
 | ||
| 		if (state.BuffersQueued)
 | ||
| 			return true;
 | ||
| 	}
 | ||
| 	return false;
 | ||
| }
 | ||
| 
 | ||
| IXAudio2SourceVoice * e2d::Music::GetSourceVoice() const
 | ||
| {
 | ||
| 	return voice_;
 | ||
| }
 | ||
| 
 | ||
| bool e2d::Music::SetVolume(float volume)
 | ||
| {
 | ||
| 	if (voice_)
 | ||
| 	{
 | ||
| 		return SUCCEEDED(voice_->SetVolume(volume));
 | ||
| 	}
 | ||
| 	return false;
 | ||
| }
 | ||
| 
 | ||
| void e2d::Music::SetCallbackOnEnd(const Function & func)
 | ||
| {
 | ||
| 	callback_.SetCallbackOnStreamEnd(func);
 | ||
| }
 | ||
| 
 | ||
| void e2d::Music::SetCallbackOnLoopEnd(const Function & func)
 | ||
| {
 | ||
| 	callback_.SetCallbackOnLoopEnd(func);
 | ||
| }
 | ||
| 
 | ||
| bool e2d::Music::ReadMMIO()
 | ||
| {
 | ||
| 	MMCKINFO ckIn;
 | ||
| 	PCMWAVEFORMAT pcmWaveFormat;
 | ||
| 
 | ||
| 	memset(&ckIn, 0, sizeof(ckIn));
 | ||
| 
 | ||
| 	wfx_ = nullptr;
 | ||
| 
 | ||
| 	if ((0 != mmioDescend(hmmio_, &ck_riff_, nullptr, 0)))
 | ||
| 		return TraceError(L"mmioDescend");
 | ||
| 
 | ||
| 	// 确认文件是一个合法的 wave 文件
 | ||
| 	if ((ck_riff_.ckid != FOURCC_RIFF) ||
 | ||
| 		(ck_riff_.fccType != mmioFOURCC('W', 'A', 'V', 'E')))
 | ||
| 		return TraceError(L"mmioFOURCC");
 | ||
| 
 | ||
| 	// 在输入文件中查找 'fmt' 块
 | ||
| 	ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
 | ||
| 	if (0 != mmioDescend(hmmio_, &ckIn, &ck_riff_, MMIO_FINDCHUNK))
 | ||
| 		return TraceError(L"mmioDescend");
 | ||
| 
 | ||
| 	// 'fmt' 块至少应和 PCMWAVEFORMAT 一样大
 | ||
| 	if (ckIn.cksize < (LONG)sizeof(PCMWAVEFORMAT))
 | ||
| 		return TraceError(L"sizeof(PCMWAVEFORMAT)");
 | ||
| 
 | ||
| 	// 将 'fmt' 块读取到 pcmWaveFormat 中
 | ||
| 	if (mmioRead(hmmio_, (HPSTR)&pcmWaveFormat,
 | ||
| 		sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat))
 | ||
| 		return TraceError(L"mmioRead");
 | ||
| 
 | ||
| 	// 分配 WAVEFORMATEX,但如果它不是 PCM 格式,再读取一个 WORD 大小
 | ||
| 	// 的数据,这个数据就是额外分配的大小
 | ||
| 	if (pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM)
 | ||
| 	{
 | ||
| 		wfx_ = (WAVEFORMATEX*) new CHAR[sizeof(WAVEFORMATEX)];
 | ||
| 
 | ||
| 		// 拷贝数据
 | ||
| 		memcpy(wfx_, &pcmWaveFormat, sizeof(pcmWaveFormat));
 | ||
| 		wfx_->cbSize = 0;
 | ||
| 	}
 | ||
| 	else
 | ||
| 	{
 | ||
| 		// 读取额外数据的大小
 | ||
| 		WORD cbExtraBytes = 0L;
 | ||
| 		if (mmioRead(hmmio_, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD))
 | ||
| 			return TraceError(L"mmioRead");
 | ||
| 
 | ||
| 		wfx_ = (WAVEFORMATEX*) new CHAR[sizeof(WAVEFORMATEX) + cbExtraBytes];
 | ||
| 
 | ||
| 		// 拷贝数据
 | ||
| 		memcpy(wfx_, &pcmWaveFormat, sizeof(pcmWaveFormat));
 | ||
| 		wfx_->cbSize = cbExtraBytes;
 | ||
| 
 | ||
| 		// 读取额外数据
 | ||
| 		if (mmioRead(hmmio_, (CHAR*)(((BYTE*)&(wfx_->cbSize)) + sizeof(WORD)),
 | ||
| 			cbExtraBytes) != cbExtraBytes)
 | ||
| 		{
 | ||
| 			SAFE_DELETE(wfx_);
 | ||
| 			return TraceError(L"mmioRead");
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	if (0 != mmioAscend(hmmio_, &ckIn, 0))
 | ||
| 	{
 | ||
| 		SAFE_DELETE(wfx_);
 | ||
| 		return TraceError(L"mmioAscend");
 | ||
| 	}
 | ||
| 
 | ||
| 	return true;
 | ||
| }
 | ||
| 
 | ||
| bool e2d::Music::ResetFile()
 | ||
| {
 | ||
| 	// Seek to the data
 | ||
| 	if (-1 == mmioSeek(hmmio_, ck_riff_.dwDataOffset + sizeof(FOURCC),
 | ||
| 		SEEK_SET))
 | ||
| 		return TraceError(L"mmioSeek");
 | ||
| 
 | ||
| 	// Search the input file for the 'data' chunk.
 | ||
| 	ck_.ckid = mmioFOURCC('d', 'a', 't', 'a');
 | ||
| 	if (0 != mmioDescend(hmmio_, &ck_, &ck_riff_, MMIO_FINDCHUNK))
 | ||
| 		return TraceError(L"mmioDescend");
 | ||
| 
 | ||
| 	return true;
 | ||
| }
 | ||
| 
 | ||
| bool e2d::Music::Read(BYTE* buffer, DWORD size_to_read)
 | ||
| {
 | ||
| 	MMIOINFO mmioinfoIn; // current status of hmmio_
 | ||
| 
 | ||
| 	if (0 != mmioGetInfo(hmmio_, &mmioinfoIn, 0))
 | ||
| 		return TraceError(L"mmioGetInfo");
 | ||
| 
 | ||
| 	UINT cbDataIn = size_to_read;
 | ||
| 	if (cbDataIn > ck_.cksize)
 | ||
| 		cbDataIn = ck_.cksize;
 | ||
| 
 | ||
| 	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(hmmio_, &mmioinfoIn, MMIO_READ))
 | ||
| 				return TraceError(L"mmioAdvance");
 | ||
| 
 | ||
| 			if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)
 | ||
| 				return TraceError(L"mmioinfoIn.pchNext");
 | ||
| 		}
 | ||
| 
 | ||
| 		// Actual copy.
 | ||
| 		*((BYTE*)buffer + cT) = *((BYTE*)mmioinfoIn.pchNext);
 | ||
| 		++mmioinfoIn.pchNext;
 | ||
| 	}
 | ||
| 
 | ||
| 	if (0 != mmioSetInfo(hmmio_, &mmioinfoIn, 0))
 | ||
| 		return TraceError(L"mmioSetInfo");
 | ||
| 
 | ||
| 	return true;
 | ||
| }
 | ||
| 
 | ||
| bool e2d::Music::FindMediaFileCch(wchar_t* dest_path, int cch_dest, const wchar_t * file_name)
 | ||
| {
 | ||
| 	bool bFound = false;
 | ||
| 
 | ||
| 	if (nullptr == file_name || nullptr == dest_path || cch_dest < 10)
 | ||
| 		return false;
 | ||
| 
 | ||
| 	// Get the exe name, and exe path
 | ||
| 	wchar_t strExePath[MAX_PATH] = { 0 };
 | ||
| 	wchar_t strExeName[MAX_PATH] = { 0 };
 | ||
| 	GetModuleFileName(HINST_THISCOMPONENT, strExePath, MAX_PATH);
 | ||
| 	strExePath[MAX_PATH - 1] = 0;
 | ||
| 
 | ||
| 	wchar_t* strLastSlash = wcsrchr(strExePath, TEXT('\\'));
 | ||
| 	if (strLastSlash)
 | ||
| 	{
 | ||
| 		wcscpy_s(strExeName, MAX_PATH, &strLastSlash[1]);
 | ||
| 
 | ||
| 		// Chop the exe name from the exe path
 | ||
| 		*strLastSlash = 0;
 | ||
| 
 | ||
| 		// Chop the .exe from the exe name
 | ||
| 		strLastSlash = wcsrchr(strExeName, TEXT('.'));
 | ||
| 		if (strLastSlash)
 | ||
| 			*strLastSlash = 0;
 | ||
| 	}
 | ||
| 
 | ||
| 	wcscpy_s(dest_path, cch_dest, file_name);
 | ||
| 	if (GetFileAttributes(dest_path) != 0xFFFFFFFF)
 | ||
| 		return true;
 | ||
| 
 | ||
| 	// Search all parent directories starting At .\ and using file_name as the leaf name
 | ||
| 	wchar_t strLeafName[MAX_PATH] = { 0 };
 | ||
| 	wcscpy_s(strLeafName, MAX_PATH, file_name);
 | ||
| 
 | ||
| 	wchar_t strFullPath[MAX_PATH] = { 0 };
 | ||
| 	wchar_t strFullFileName[MAX_PATH] = { 0 };
 | ||
| 	wchar_t strSearch[MAX_PATH] = { 0 };
 | ||
| 	wchar_t* strFilePart = nullptr;
 | ||
| 
 | ||
| 	GetFullPathName(L".", MAX_PATH, strFullPath, &strFilePart);
 | ||
| 	if (strFilePart == nullptr)
 | ||
| 		return false;
 | ||
| 
 | ||
| 	while (strFilePart != nullptr && *strFilePart != '\0')
 | ||
| 	{
 | ||
| 		swprintf_s(strFullFileName, MAX_PATH, L"%s\\%s", strFullPath, strLeafName);
 | ||
| 		if (GetFileAttributes(strFullFileName) != 0xFFFFFFFF)
 | ||
| 		{
 | ||
| 			wcscpy_s(dest_path, cch_dest, strFullFileName);
 | ||
| 			bFound = true;
 | ||
| 			break;
 | ||
| 		}
 | ||
| 
 | ||
| 		swprintf_s(strFullFileName, MAX_PATH, L"%s\\%s\\%s", strFullPath, strExeName, strLeafName);
 | ||
| 		if (GetFileAttributes(strFullFileName) != 0xFFFFFFFF)
 | ||
| 		{
 | ||
| 			wcscpy_s(dest_path, cch_dest, strFullFileName);
 | ||
| 			bFound = true;
 | ||
| 			break;
 | ||
| 		}
 | ||
| 
 | ||
| 		swprintf_s(strSearch, MAX_PATH, L"%s\\..", strFullPath);
 | ||
| 		GetFullPathName(strSearch, MAX_PATH, strFullPath, &strFilePart);
 | ||
| 	}
 | ||
| 	if (bFound)
 | ||
| 		return true;
 | ||
| 
 | ||
| 	// 失败时,将文件作为路径返回,同时也返回错误代码
 | ||
| 	wcscpy_s(dest_path, cch_dest, file_name);
 | ||
| 
 | ||
| 	return false;
 | ||
| }
 |