應用啟動時播放視頻作為 Splash Screen,需支持:
難點:MCIWnd 窗口內部封裝了鼠標消息處理,宿主無法直接捕獲其客戶區點擊事件。
使用 SetWindowLong(GWL_WNDPROC) 替換 MCIWnd 窗口過程,攔截 WM_LBUTTONDOWN 設置全局標志,其餘消息調用原窗口過程。
// App 類
class CMyApp : public CWinApp {
public:
bool m_CanClose = false; // 是否允許關閉視頻窗口
};
// View 類成員
HWND m_VideoWnd = NULL; // 視頻窗口句柄
long m_VideoLength = 0; // 視頻總長度 (毫秒)
WNDPROC OldProc = NULL; // 原窗口過程地址
CMyView::CMyView() {
CString file = _T("D:\\Video\\Cover.avi");
m_VideoWnd = MCIWndCreate(GetSafeHwnd(), AfxGetInstanceHandle(),
WS_POPUP | WS_VISIBLE | MCIWNDF_NOPLAYBAR | MCIWNDF_NOMENU,
file);
// 居中顯示
RECT rc; GetWindowRect(m_VideoWnd, &rc);
int sx = (GetSystemMetrics(SM_CXSCREEN) - (rc.right - rc.left)) / 2;
int sy = (GetSystemMetrics(SM_CYSCREEN) - (rc.bottom - rc.top)) / 2;
SetWindowPos(m_VideoWnd, HWND_TOPMOST, sx, sy, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE);
}
void CMyView::OnInitialUpdate() {
CView::OnInitialUpdate();
OldProc = (WNDPROC)GetWindowLong(m_VideoWnd, GWL_WNDPROC);
SetWindowLong(m_VideoWnd, GWL_WNDPROC, (LONG)NewProc); // 替換窗口過程
m_VideoLength = MCIWndGetLength(m_VideoWnd);
MCIWndPlay(m_VideoWnd);
SetTimer(1, 20, NULL); // 20ms 輪詢
}
void CMyView::OnTimer(UINT nIDEvent) {
CMyApp* pApp = (CMyApp*)AfxGetApp();
if (MCIWndGetPosition(m_VideoWnd) >= m_VideoLength || pApp->m_CanClose) {
KillTimer(1);
MCIWndDestroy(m_VideoWnd);
AfxGetMainWnd()->ShowWindow(SW_SHOWMAXIMIZED); // 顯示主窗口
return;
}
CView::OnTimer(nIDEvent);
}
LRESULT CALLBACK NewProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
if (msg == WM_LBUTTONDOWN) {
CMyApp* pApp = (CMyApp*)AfxGetApp();
pApp->m_CanClose = true; // 設置標志,定時器將關閉視頻
}
return CallWindowProc(OldProc, hWnd, msg, wParam, lParam); // 其餘轉發
}
MCIWNDF_NOPLAYBAR 隱藏控制欄,MCIWNDF_NOMENU 隱藏右鍵菜單,實現全屏沉浸體驗。MCIWndCreate 之後、MCIWndPlay 之前完成,確保攔截生效。m_CanClose 僅在 UI 線程讀寫,無需鎖;若跨線程需用 InterlockedExchange 或原子操作。MCIWndDestroy 釋放視頻窗口及 MCI 設備,避免句柄泄漏。