為 VC++ 應用程序添加啟動動畫封面(播放 AVI 視頻),最簡便方法是使用 Video for Windows (VfW) SDK 提供的 MCIWnd 窗口類。相比底層 AVIFile API,MCIWnd 封裝了打開、播放、關閉等完整流程,幾行代碼即可實現專業級啟動動畫效果。
| 函數 | 用途 |
|---|---|
MCIWndRegisterClass | 註冊 MCIWnd 窗口類(進程僅需調用一次) |
MCIWndCreate | 創建 MCIWnd 窗口,返回窗口句柄 |
MCIWndOpen | 打開媒體文件(AVI/MPEG/WAV 等) |
MCIWndPlay | 開始播放 |
MCIWndGetLength | 獲取媒體總時長(毫秒) |
MCIWndDestroy | 關閉媒體並銷毀窗口 |
MCIWndUseTime | 設置時間格式為毫秒 |
// Stdafx.h
#include
#include
#include // Video for Windows 標頭
#pragma comment(lib, "vfw32.lib") // 自動鏈接庫
BOOL CMyApp::InitInstance()
{
// 1. 註冊 MCIWnd 窗口類
if (!MCIWndRegisterClass())
{
AfxMessageBox(_T("無法註冊 MCIWnd 類,請確認 vfw32.dll 存在"));
return FALSE;
}
// 2. 創建播放窗口(無播放控制欄、可見、彈出式)
HWND hMCIWnd = MCIWndCreate(
NULL, // 父窗口:無(桌面級)
AfxGetInstanceHandle(), // 實例句柄
MCIWNDF_NOPLAYBAR | WS_VISIBLE | WS_POPUP, // 風格
NULL // 初始不打開文件
);
if (!hMCIWnd)
{
AfxMessageBox(_T("創建 MCI 窗口失敗"));
return FALSE;
}
// 3. 打開 AVI 文件
CString strAviPath = _T("D:\\Media\\Splash.avi"); // 實際項目中應從資源/配置讀取
if (strAviPath.GetLength() > 0)
{
if (MCIWndOpen(hMCIWnd, strAviPath, 0) != 0)
{
AfxMessageBox(_T("無法打開視頻文件:") + strAviPath);
MCIWndDestroy(hMCIWnd);
}
else
{
// 4. 設置時間格式為毫秒
MCIWndUseTime(hMCIWnd);
// 5. 獲取播放時長
long lPlayTime = MCIWndGetLength(hMCIWnd); // 毫秒
// 6. 開始播放
MCIWndPlay(hMCIWnd);
// 7. 關鍵:主線程休眠等待播放完成
// 注意:Sleep 會阻塞消息泵,MCIWnd 內部有獨立消息泵可處理 WM_PAINT 等
// 若視頻較長(>5秒),建議改用模態對話框+定時器方案
::Sleep(lPlayTime);
// 8. 銷毀播放窗口
MCIWndDestroy(hMCIWnd);
}
}
// 9. 繼續標準 MFC 初始化流程
#ifdef _AFXDLL
Enable3dControls();
#else
Enable3dControlsStatic();
#endif
// ... 後續文檔模板創建、主框架創建等標準代碼
return TRUE;
}
MCIWnd 播放是異步的。若不等待直接進入主框架創建,動畫窗口會被主窗口遮蓋,或主窗口初始化過程導致動畫卡頓。Sleep(lPlayTime) 讓主線程阻塞,但 MCIWnd 窗口擁有獨立消息泵,仍能正常繪制視頻幀。
| 風格 | 效果 |
|---|---|
MCIWNDF_NOPLAYBAR | 隱藏播放控制條(進度條、音量等),純全屏播放 |
MCIWNDF_NOMENU | 禁用右鍵菜單 |
WS_POPUP | 無邊框彈出窗口,適合全屏封面 |
WS_VISIBLE | 創建即顯示 |
Windows 多任務調度可能導致 Sleep 實際休眠時間超過預期。更穩健方案:
// 方案:使用等待定時器或事件,而非 Sleep
// MCIWnd 支持 MCI_NOTIFY 通知,但需設置回調窗口
// 簡單替代:循環檢查播放位置
long lLength = MCIWndGetLength(hMCIWnd);
long lPos = 0;
while (lPos < lLength)
{
::Sleep(100); // 短間隔輪詢
lPos = MCIWndGetPosition(hMCIWnd);
// 處理消息防止假死
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| MCIWnd (VfW) | 代碼極簡、系統內置、無額外依賴 | 僅支持舊格式(AVI/MPEG-1/WAVE)、無硬件加速、高 DPI 模糊 | 維護老項目、簡單啟動動畫 |
| DirectShow | 支持所有編解碼器、硬件加速、流媒體 | COM 復雜、代碼量大、需分發 quartz.dll | 專業播放器、複雜媒體處理 |
| Media Foundation | 現代架構、高性能、硬件加速、DRM | Vista+、學習曲線陡峭 | Win7+ 新項目、高清視頻 |
| WebView2 / HTML5 Video | 格式豐富、易於定製 UI、跨平台思維 | 體積大(~100MB 運行時)、啟動慢 | 現代 UI、需 Web 技術棧 |
| GDI+ / Direct2D 逐幀繪製 | 完全控制、無外部依賴 | 需自行解碼、CPU 佔用高 | 特殊效果、極小文件 |
將 AVI 作為自定義資源嵌入 EXE,避免外部文件依賴:
AVI(自定義類型) → ID 如 IDR_AVI_SPLASH
CString ExtractAviResource(UINT nResID)
{
HRSRC hRes = FindResource(AfxGetInstanceHandle(), MAKEINTRESOURCE(nResID), _T("AVI"));
if (!hRes) return _T("");
HGLOBAL hGlob = LoadResource(AfxGetInstanceHandle(), hRes);
DWORD dwSize = SizeofResource(AfxGetInstanceHandle(), hRes);
LPVOID pData = LockResource(hGlob);
CString strTemp = GetTempFileName(_T("avi"));
CFile file(strTemp, CFile::modeCreate | CFile::modeWrite);
file.Write(pData, dwSize);
file.Close();
return strTemp;
}
// InitInstance 中使用
CString strAvi = ExtractAviResource(IDR_AVI_SPLASH);
if (!strAvi.IsEmpty())
{
MCIWndOpen(hMCIWnd, strAvi, 0);
// ... 播放 ...
DeleteFile(strAvi); // 播放後刪除臨時文件
}
MCIWndCreate 窗口尺寸默認 0,需 SetWindowPos 設置全屏尺寸,或使用 MCIWNDF_NOPLAYBAR 配合 MCIWndSetZoomWS_POPUP 窗口默認主顯示器,需 SetWindowPos 移動到目標顯示器使用 VfW MCIWnd 實現啟動動畫封面是經典 VC++ 技術,代碼量極小、零外部依賴、部署簡單。雖然格式支持有限,但對於內部工具、老項目維護、簡單品牌展示動畫仍是極具性價比的選擇。現代新項目建議評估 Media Foundation 或 WebView2 等方案,獲得更好格式支持與高 DPI 適配。