將聲音檔 (如提示音、警報音) 直接編譯進 EXE 內部,避免部署時遺漏外部檔案,是專業軟體開發的常見需求。本文詳細說明如何在 VC++ 專案的 .rc 資源檔中加入 WAVE 資源,並透過獨立執行緒載入播放,附完整代碼範例。
.rc 檔。資源ID 類型 檔案路徑
IDW_WAVE WAVE "c:\sounds\notify.wav"
IDW_WAVE:自訂資源識別碼 (符號常數)。WAVE:資源類型,必須大寫。.rc 檔,關閉編輯器。Visual Studio 會自動偵測變更。播放聲音可能阻塞 UI,建議用 AfxBeginThread 建立獨立執行緒。
AfxBeginThread((AFX_THREADPROC)PlaySoundThread, NULL, THREAD_PRIORITY_NORMAL);
UINT PlaySoundThread(LPVOID pParam) {
// 1. 取得模組實例控制代碼
HINSTANCE hInst = AfxGetInstanceHandle();
// 2. 尋找資源
// 參數:模組句柄、資源名稱 (MAKEINTRESOURCE 或字串)、資源類型
HRSRC hRes = FindResource(hInst, _T("IDW_WAVE"), _T("WAVE"));
if (!hRes) return 0;
// 3. 載入資源到全域記憶體
HGLOBAL hGlobal = LoadResource(hInst, hRes);
if (!hGlobal) return 0;
// 4. 鎖定記憶體取得指標
LPVOID pSoundData = LockResource(hGlobal);
if (!pSoundData) {
FreeResource(hGlobal); // 雖然 LockResource 通常不需 Unlock,但載入失敗釋放是好習慣
return 0;
}
// 5. 播放:SND_MEMORY 表示記憶體資料,SND_SYNC 同步播放 (播放完畢才返回)
sndPlaySound((LPCSTR)pSoundData, SND_MEMORY | SND_SYNC);
// 6. 釋放資源 (實際上 LoadResource 在模組卸載時自動釋放,此處為規範流程)
FreeResource(hGlobal);
return 0;
}
| 步驟 | API | 用途 |
|---|---|---|
| 1 | AfxGetInstanceHandle() | 取得當前 EXE/DLL 的模組句柄 |
| 2 | FindResource(hMod, name, type) | 定位資源目錄項,返回 HRSRC |
| 3 | LoadResource(hMod, hRes) | 將資源資料載入全域堆疊,返回 HGLOBAL |
| 4 | LockResource(hGlobal) | 鎖定記憶體區塊,返回資料起始指標 LPVOID |
| 5 | sndPlaySound(ptr, SND_MEMORY|SND_SYNC) | 從記憶體播放波形聲音 |
| 6 | FreeResource(hGlobal) | 釋放載入的資源記憶體 |
.rc 中定義為字串 "IDW_WAVE",FindResource 參數需對應字串 _T("IDW_WAVE"),或使用 MAKEINTRESOURCE(IDW_WAVE) (需在 resource.h 定義符號值)。SND_SYNC 會阻塞執行緒直到播放結束;若需非同步播放,改用 SND_ASYNC,但需自行管理記憶體生命週期 (不可立即 FreeResource)。此法適用於 VC 6.0 至 Visual Studio 現代版本,是嵌入式聲音播放的標準做法。