Dreamweaver (早期版本) 每開啟一個檔案,任務列就出現一個獨立按鈕,關閉任一視窗不影響其他視窗,彷彿每個視窗都是獨立進程。實則它們屬於同一進程,共享記憶體空間與資料 (如最近開啟路徑、設定檔)。
核心技術點:
WS_EX_APPWINDOW 或設定 ParentWindow = GetDesktopWindow()),使其各自在任務列顯示按鈕。Application->ShowMainForm = false + ShowWindow(SW_HIDE))。以下以 C++ Builder (VCL) 為例,邏輯同樣適用於 MFC/Win32 API。
Form1 (主視窗) 與 Form2 (文件視窗)。Form2 設為不自動建立 (Project Options → Forms → 移出 Auto-create list)。
// Form1.h
public:
int FormCount; // 追蹤存活的 Form2 實例數
// Form1.cpp - OnCreate
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// 取得標準視窗風格
int style = GetWindowLong(Handle, GWL_STYLE);
// 建立兩個 Form2 實例
TForm2 *F = new TForm2(NULL); // Owner=NULL 表示由應用程式管理生命週期
TForm2 *G = new TForm2(NULL);
// 關鍵:設為桌面視窗的子視窗 -> 成為頂層視窗,獨立顯示於任務列
F->ParentWindow = GetDesktopWindow();
G->ParentWindow = GetDesktopWindow();
F->Caption = "Document F";
G->Caption = "Document G";
// 套用相同視窗風格
SetWindowLong(F->Handle, GWL_STYLE, style);
SetWindowLong(G->Handle, GWL_STYLE, style);
FormCount = 2;
F->Show();
G->Show();
}
// Project1.cpp (WinMain)
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
// 關鍵兩步:隱藏主視窗與 Application 視窗
Application->ShowMainForm = false; // 不自動 Show Form1
ShowWindow(Application->Handle, SW_HIDE); // 隱藏隱形 Application 視窗
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
注意:單靠 ShowMainForm=false 不足,必須同時 ShowWindow(Application->Handle, SW_HIDE),否則 Alt+Tab 仍會出現空白應用程式圖示。
// Form2.cpp - OnClose
void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)
{
Action = caFree; // 釋放記憶體
if (Form1->FormCount > 1)
{
Form1->FormCount--; // 還有其他視窗,僅遞減計數
}
else
{
Application->Terminate(); // 最後一個視窗,結束整個應用程式
}
}
| C++ Builder / VCL | MFC / Win32 API 對應 |
|---|---|
ParentWindow = GetDesktopWindow() | CreateWindowEx(WS_EX_APPWINDOW, ..., NULL, ...) 或 SetParent(hwnd, NULL) + SetWindowLong(GWL_EXSTYLE, WS_EX_APPWINDOW) |
Application->ShowMainForm=false | m_pMainWnd->ShowWindow(SW_HIDE) 在 InitInstance 中 |
ShowWindow(Application->Handle, SW_HIDE) | MFC 無 Application 視窗概念,無需此步 |
Application->Terminate() | PostQuitMessage(0) 或 AfxGetMainWnd()->PostMessage(WM_CLOSE) |
// CMyApp::InitInstance
BOOL CMyApp::InitInstance()
{
// ... 標準初始化 ...
// 不建立主框架視窗,或建立後立即隱藏
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE;
m_pMainWnd = pMainFrame;
pMainFrame->ShowWindow(SW_HIDE); // 隱藏主視窗
pMainFrame->UpdateWindow();
// 建立第一個文件視窗
CreateNewDocumentWindow();
return TRUE;
}
// 建立新文件視窗 (對應 Form2)
void CMyApp::CreateNewDocumentWindow()
{
CChildFrame* pFrame = new CChildFrame;
if (pFrame->Create(NULL, _T("Document"), WS_OVERLAPPEDWINDOW | WS_EX_APPWINDOW, rectDefault, NULL, 0))
{
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
m_nDocWindowCount++; // 成員變數追蹤數量
}
}
// CChildFrame::OnClose
void CChildFrame::OnClose()
{
CMDIChildWnd::OnClose(); // 執行 caFree 等效邏輯
if (--((CMyApp*)AfxGetApp())->m_nDocWindowCount == 0)
{
AfxGetMainWnd()->PostMessage(WM_CLOSE); // 最後一個,關閉隱藏主視窗觸發結束
}
}
所有文件視窗屬於同一進程,共享機制天然存在:
CWinApp 派生類成員 (MFC):集中管理設定、最近檔案清單、快取資料。Dreamweaver 風格的核心在於「多頂層視窗 + 隱藏主視窗 + 參考計數生命週期」。此架構適合文件導向應用 (IDE、圖片編輯器、文書處理),提供類似多進程的使用者體驗,同時享有單進程的低資源佔用與高效資料共享。移植到現代框架 (Qt, WPF, Electron) 時,同樣遵循此核心邏輯。