Visual C++ 6.0 編 程 經 驗 (轉)

worldblog發表於2007-12-29
Visual C++ 6.0 編 程 經 驗 (轉)[@more@]

另 一 種 改 變 窗 口 標 題 的 方 法 ---- 在1997 年9 月1 日 出 版 的《 計 算 機 世 界》 上 有 一 篇 名 為《Visual C++ 4.0 編 程 經 驗 談》 的 文 章, 其 中 提 到 過 一 種 改 變 窗 口 標 題 的 方 法, 即 在 應 用 程 序 的 框 架 類CMainFrame 類( 注: 它 是CFrameWnd 類 的 派 生 類) 的 成 員 函 數PreCreateWindow() 中 修 改CFrameWnd 類 的 成 員 變 量m_strTitle 的 值。 這 種 方 法 的 不 足 之 處 就 是 只 能 一 次 性 的 設 置 窗 口 標 題 的 內 容, 不 能 在 程 序 運 行 過 程 中 隨 時 修 改 它。 比 如 當 需 要 把 鼠 標 的 當 前 坐 標 在 窗 口 標 題 上 顯 示 時, 我 們 就 要 使 用 到Visual C++ 提 供 的 全 程 函 數 BOOL SetWindowText( HWND hWnd, LPCTSTR lpString )。 這 個 函 數 實 際 上 是 一 個 函 數。 它 的 第 一 個 參 數 要 求 是 一 個 窗 口 框 架 的 句 柄, 而 第 二 個 參 數 要 求 是 一 個 指 向 一 個 常 量 字 符 串 的32 位 指 針, 即LPCTSTR 類 型 的 變 量。 通 常 情 況 下, 我 們 往 往 會 在 應 用 程 序 的 文 檔 類 或 視 類 中 動 態 改 變 窗 口 標 題 的 內 容, 從 而 沒 有 現 成 的 指 向 窗 口 框 架 的 句 柄 供 我 們 使 用, 因 此 我 們 還 需 要 在 使 用SetWindowText() 函 數 前 先 調 用 另 一 個Win32 函 數AfxGetMainWnd(), 來 獲 得 一 個 指 向 應 用 程 序 的 框 架 類 的 指 針, 例 如 使 用 語 句

---- CWnd* m_pCWnd= AfxGetMainWnd()

---- 然 後, 再 以 如 下 形 式 調 用SetWindowText() 函 數:

---- SetWindowText(*m_pCWnd, (LPCTSTR) m_WindowText);// m_WindowText 可 以 是 一 個CString 類 的 變 量

---- 如 何 把 多 於256 色 的 位 圖 作 為 資 源 加 入 到 應 用 程 序 中

---- 曾 經 使 用 過Visual C++ 進 行 應 用 程 序 開 發 的 同 行 們 都 知 道,Visual C++ 5.0 以 前 版 本 中 自 帶 的 位 圖 編 輯 器 不 能 瀏 覽 和 編 輯256 色 以 上 的 位 圖, 並 且 資 源 文 件 中 也 不 允 許 嵌 入(Import)256 色 以 上 的 位 圖 作 為 資 源( 否 則, 在 應 用 程 序 運 行 時 會 報 錯)。 這 一 特 性 使 得 我 們 用Visual C++ 開 發 應 用 程 序 時 不 得 不 使 用 其 他 方 法 來 增 強 界 面 圖 畫 的 美 觀 性, 於 是 就 有 使 用Visual C++ 開 發 應 用 程 序 內 核, 用 開 發 界 面 部 分 的 組 合 方 法。 好 在Visual C++ 5.0 中 這 個 問 題 有 了 改 善。 首 先, 位 圖 編 輯 器 可 以 創 建 並 編 輯256 色 的 位 圖 了。 另 外,Visual C++ 5.0 允 許 程 序 員 把256 色 以 上 的 位 圖 嵌 入 到 資 源 中, 盡 管 仍 然 無 法 在Visual C++ 的 位 圖 編 輯 器 中 瀏 覽, 並 且 還 要 求 必 須 選 擇Win32 Release 作 為 編 譯 方 式 生 成 可 執 行 的 應 用 程 序。 另 外 一 個 限 制 條 件 是 作 為 資 源 的256 色 以 上 的 位 圖 不 能 由 應 用 程 序 內 核 自 動 打 開 和 關 閉。 比 如 說, 在 上 述 那 篇 名 為《Visual C++ 4.0 編 程 經 驗 談》 的 文 章 中 曾 經 提 到 過 一 種 為 對 話 框 加 入 位 圖 式 按 鈕 的 方 法, 即 由 程 序 開 發 者 為 每 個 按 鈕 創 建 四 幅 位 圖, 分 別 用 於 表 示 按 鈕 的 彈 起 狀 態(UP)、 按 下 狀 態(DOWN)、 輸 入 焦 點 狀 態(FOCUS) 和 禁 止 狀 態(DISABLE), 並 且 必 須 以 該 按 鈕 的 標 題 名 與 上 述 四 種 狀 態 之 一 的 組 合 作 為 位 圖 的 標 識, 以 便 應 用 程 序 在 繪 制 位 圖 按 鈕 時, 可 以 自 動 地 找 到 相 應 的 資 源( 即 位 圖)。 然 而 這 一 自 動 映 射 只 限 制 於Visual C++ 位 圖 編 輯 器 能 夠 打 開 的 位 圖。 因 此 如 果 選 擇256 色 以 上 的 位 圖 作 為 位 圖 按 鈕 的 資 源, 並 也 希 望 達 到 上 述 四 狀 態 的 相 互 切 換 的 話, 就 必 須 用 到 下 述 的 函 數 和 程 序 設 計 參 考 模 型。

  • 設 置 四 個 常 量,BUTTON_UP,BUTTON_DOWN,BUTTON_FOCUS,BUTTON_DISABLE, 分 別 用 於 標 識 各 按 鈕 的 當 前 狀 態。
  • 在 應 用 程 序 的 相 應 對 話 框 類 中 為 每 個 位 圖 按 鈕( 為 下 面 敘 述 方 便, 不 妨 假 設 為 兩 個) 設 置 一 個CRect 類 的 對 象,m_rect1 和m_rect2, 來 記 載 各 按 鈕 在 對 話 框 中 所 佔 據 的 坐 標 矩 形。 再 在 該 對 話 框 類 中 為 每 個 按 鈕 設 置 一 個 整 型 變 量,Buton1_Status,Button2_Status, 記 錄 各 按 鈕 的 當 前 狀 態。 然 後 在 對 話 框 的 構 造 函 數 中 初 始 化 這 些CRect 類 的 對 象 和 整 型 變 量。
  • 在 該 對 話 框 類 中 創 建 分 別 響 應 鼠 標 各 種 狀 態 的 消 息 處 理 函 數, 如OnMouseMove(),OnLButtonUp(),OnLButtonDown() 等。
  • 同 時 按 下Ctrl 和W 鍵 或 直 接 單 擊 工 具 條 上 的ClassWizard 按 鈕, 打 開ClassWizard 對 話 框。 在 類 名(Class name) 列 表 框 中 選 擇 對 話 框 類, 在 IDs 列 表 框 中 選 擇 該 類 的 類 名 後, 在 消 息(Messages) 列 表 框 中 選 擇WM_PAINT 消 息 並 雙 擊 它, 這 時ClassWizard 就 會 在 該 對 話 框 類 中 加 入 一 個OnPaint() 函 數。 該 函 數 將 在 我 們 的 程 序 中 起 重 要 作 用。 之 後, 再 在Object IDs 列 表 框 中 選 擇 新 加 入 的 工 具 條 按 鈕 的 標 識 名, 雙 擊 消 息 列 表 框 中 的COMMAND 消 息,ClassWizard 又 會 在 對 話 框 類 中 加 入 相 應 的 消 息 處 理 函 數。 關 閉ClassWizard 對 話 框。

---- 下 面 給 出 部 分 程 序 代 碼, 僅 供 參 考。

void CTESTDlg::OnPaint() { CWnd* pWnd; CDC* pDC; CDC* pDisplayMemDC; CBitmap* pBitmap; pWnd=GetDlgItem(IDC_IMAGE1);//得到指向第一個點陣圖按鈕的指標 pDC=pWnd->GetDC();//獲得一個視窗裝置用於畫圖 pWnd->Invalidate();//使視窗無效,從而它 pWnd->UpdateWindow(); pDisplayMemDC=new CDC; pBitmap=new CBitmap; pDisplayMemDC->CreateCompatibleDC(pDC); if (Change1) {//說明第一個按鈕的狀態發生了變化 switch (Button1_Status){ case BUTTON_DISABLE: pBitmap->Loaitmap(IMAGE1_DISABLE);//裝入點陣圖 pDisplayMemDC->Object(pBitmap); pDC->BitBlt(0,0,140,30,pDisplayMemDC,0,0,SRCCOPY); //把點陣圖複製到指定區域 break; case BUTTON_UP: pBitmap->LoadBitmap(IMAGE1_UP); pDisplayMemDC->SelectObject(pBitmap); pDC->BitBlt(0,0,140,30,pDisplayMemDC, 0,0,SRCCOPY); break; case BUTTON_FOCUS: pBitmap->LoadBitmap(IMAGE1_FOCUS); pDisplayMemDC->SelectObject(pBitmap); pDC->BitBlt(0,0,140,30,pDisplayMemDC,0,0,SRCCOPY); break; case BUTTON_DOWN: pBitmap->LoadBitmap(IMAGE1_DOWN); pDisplayMemDC->SelectObject(pBitmap); pDC->BitBlt(0,0,140,30,pDisplayMemDC,0,0,SRCCOPY); break; } } delete pDisplayMemDC; delete pBitmap; pWnd=GetDlgItem(IDC_IMAGE2); pDC=pWnd->GetDC(); pWnd->Invalidate(); pWnd->UpdateWindow(); pDisplayMemDC=new CDC; pBitmap=new CBitmap; pDisplayMemDC->CreateCompatibleDC(pDC); if (Change2) {//說明第二個按鈕的狀態發生了變化 switch (Button2_Status){ case BUTTON_DISABLE: pBitmap->LoadBitmap(IMAGE2_DISABLE); pDisplayMemDC->SelectObject(pBitmap); pDC->BitBlt(0,0,140,30,pDisplayMemDC,0,0,SRCCOPY); break; case BUTTON_UP: pBitmap->LoadBitmap(IMAGE2_UP); pDisplayMemDC->SelectObject(pBitmap); pDC->BitBlt(0,0,140,30,pDisplayMemDC,0,0,SRCCOPY); break; case BUTTON_FOCUS: pBitmap->LoadBitmap(IMAGE2_FOCUS); pDisplayMemDC->SelectObject(pBitmap); pDC->BitBlt(0,0,140,30,pDisplayMemDC,0,0,SRCCOPY); break; case BUTTON_DOWN: pBitmap->LoadBitmap(IMAGE2_DOWN); pDisplayMemDC->SelectObject(pBitmap); pDC->BitBlt(0,0,140,30,pDisplayMemDC,0,0,SRCCOPY); break; } } delete pDisplayMemDC; delete pBitmap; CDialog::OnPaint(); } void CTESTDlg::OnMouseMove(UINT nFlags, CPoint point) { CRect rect=CRect(0,0,1,1); CRgn rgn1,rgn2;//記錄各點陣圖按鈕所佔據的矩形區域 rgn1.CreateRectRgnIndirect(m_rect1); //rgn1記錄第一個點陣圖按鈕所佔據的矩形區域 if (rgn1.PtInRegion(point)) {//滑鼠當前是否已落入第一個點陣圖按鈕所佔據的矩形區域 if ( (Button1_Status!=BUTTON_FOCUS) && (Button1_Status!=BUTTON_DISABLE) ) { //如果點陣圖按鈕的當前狀態不是輸入焦點狀態並且也不是禁止狀態 Button1_Status= BUTTON_FOCUS; Change1=true; InvalidateRect(rect,FALSE); } if ( (Button2_Status!=BUTTON_UP) && (Button2_Status!=BUTTON_DISABLE) ) { Button2_Status= BUTTON_UP; Change2=true; InvalidateRect(rect,FALSE); } } else{ rgn2.CreateRectRgnIndirect(m_rect2); if (rgn2.PtInRegion(point)){ if ( (Button2_Status!=BUTTON_FOCUS) && (Button2_Status!=BUTTON_DISABLE) ) { Button2_Status= BUTTON_FOCUS; Change2=true; InvalidateRect(rect,FALSE); } if ( (Button1_Status!=BUTTON_UP) && (Button1_Status!=BUTTON_DISABLE) ) { Button1_Status= BUTTON_UP; Change1=true; InvalidateRect(rect,FALSE); } } } CDialog::OnMouseMove(nFlags, point); } void CTESTDlg::OnLButtonUp(UINT nFlags, CPoint point) { CRect rect=CRect(0,0,1,1); CRgn rgn1,rgn2; rgn1.CreateRectRgnIndirect(m_rect1); if (rgn1.PtInRegion(point)){ if ( (Button1_Status!=BUTTON_UP) && (Button1_Status!=BUTTON_DISABLE) ) { Button1_Status=BUTTON_UP; Change1=true; InvalidateRect(rect,FALSE); } } else{ rgn2.CreateRectRgnIndirect(m_rect2); if (rgn2.PtInRegion(point)){ if ( (Button2_Status!=BUTTON_UP) && (Button2_Status!=BUTTON_DISABLE) ) { Button2_Status=BUTTON_UP; Change2=true; InvalidateRect(rect,FALSE); } } } CDialog::OnLButtonUp(nFlags, point); } void CTESTDlg::OnLButtonDown(UINT nFlags, CPoint point) { CRect rect=CRect(0,0,1,1); CRgn rgn1,rgn2; rgn1.CreateRectRgnIndirect(m_rect1); if (rgn1.PtInRegion(point)){ if ( (Button1_Status!=BUTTON_DOWN) && (Button1_Status!=BUTTON_DISABLE) ) { Button1_Status=BUTTON_DOWN; Change1=true; InvalidateRect(rect,FALSE); } if ( (Button2_Status!=BUTTON_UP) && (Button2_Status!=BUTTON_DISABLE) ) { Button2_Status=BUTTON_UP; Change2=true; InvalidateRect(rect,FALSE); } } else{ rgn2.CreateRectRgnIndirect(m_rect2); if (rgn2.PtInRegion(point)){ if ( (Button2_Status!=BUTTON_DOWN) && (Button2_Status!=BUTTON_DISABLE) ) { Button2_Status=BUTTON_DOWN; Change2=true; InvalidateRect(rect,FALSE); } if ( (Button1_Status!=BUTTON_UP) && (Button1_Status!=BUTTON_DISABLE) ) { Button1_Status=BUTTON_UP; Change1=true; InvalidateRect(rect,FALSE); } } } CDialog::OnLButtonDown(nFlags, point); }


如 何 在 應 用 程 序 中 不 加 載 菜 單、 工 具 條 和 狀 態 條 ---- 在 通 過AppWizard 生 成 的 具 有 窗 口 框 架 結 構 的 應 用 程 序(SDI 和MDI) 中,MFC 類 庫 已 為 我 們 加 載 上 了 菜 單( 包 括 一 個 系 統 菜 單)、 工 具 條 和 狀 態 條。 但 有 時 由 於 特 殊 需 要, 我 們 可 能 希 望 在 自 己 的 應 用 程 序 中 事 先 不 加 載 菜 單、 工 具 條 和 狀 態 條。 這 時 就 需 要 我 們 手 動 地 刪 除 和 修 改 一 些 類 中 的 語 句。

  • 打 開MainFrm.cpp 文 件, 使 用 工 具 條 上 的 函 數 下 拉 列 表 框 找 到OnCreate() 函 數。 按 照 下 述 程 序 注 釋 掉 創 建 工 具 條 和 狀 態 條 部 分 的 語 句。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; //在此處開始加註釋符號 /* if (!m_wndToolBar.Create(this) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0("Failed to create toolbarn"); return -1; // fail to create } if (!m_wndStatar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status barn"); return -1; // fail to create } // TODO: Remove this if you don't want tool or a resizeable toolbar m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); // TODO: Delete these three lines if you don't want the toolbar to be dockable m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); 在處結束註釋*/ return 0; }


  • 在MainFrm.cpp 文 件 中, 使 用 工 具 條 上 的 函 數 下 拉 列 表 框 找 到PreCreateWindow() 函 數。 參 照 第 三 部 分 中 的 程 序, 加 入 特 定 的 窗 口 框 架 屬 性, 不 妨 把cs.style 設 置 成 如 下 形 式, 即 不 加 載 系 統 菜 單。
    // Create a window without min/max buttons,system menu, or sizable border
    cs.style =WS_OVERLAPPED | WS_BORDER;
  • 在 主 應 用 程 序, 即 含 有 定 義theApp 全 程 變 量 的.cpp 文 件 中, 使 用 工 具 條 上 的 函 數 下 拉 列 表 框 找 到InitInstance() 函 數。 在“pDocTemplate = new CSingleDocTemplate” 一 句 中, 用NULL 替 換IDR_MAINFRAME。 如 下 段 程 序 所 示。

BOOL CYourMainApp::InitInstance() { //.....此處略去一部分無關語句 CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( NULL, //IDR_MAINFRAME, //用NULL替換IDR_MAINFRAME RUNTIME_CLASS(CNoBarDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CNoBarView)); AddDocTemplate(pDocTemplate); //.....此處略去一部分無關語句 }


  • 找 到Visual C++ 編 輯 器 的 工 具 條 上 的 編 譯 方 式 下 拉 列 表 框, 選 擇Win32 Release, 生 成Release 版 本 的 應 用 程 序。

---- 至 此, 我 們 就 得 到 了 不 含 菜 單、 工 具 條 和 狀 態 條 結 構 的 應 用 程 序。

 

實 現 操 作 過 程 提 示 對 話 框 的 一 種 方 法 ---- 在 使 用95 進 行 文 件 拷 貝 或 者 刪 除 操 作 時, 您 一 定 見 到 過 那 種 具 有 飛 文 件 動 畫 的 操 作 過 程 提 示 對 話 框。 這 一 功 能 的 加 入 不 僅 使 我 們 能 夠 在 操 作 過 程 當 中 隨 時 取 消 操 作, 而 且 也 使 文 件 拷 貝 或 者 刪 除 操 作 變 得 生 動 活 潑。 其 實, 在 使 用Visual C++ 進 行 應 用 程 序 設 計 時, 我 們 也 可 以 使 用 下 述 方 法 在 適 當 位 置 加 入 自 己 的 操 作 過 程 提 示 對 話 框。

  • 為 每 一 個 操 作 過 程 提 示 對 話 框 創 建 一 個 對 話 框 類。 為 了 下 面 敘 述 方 便, 我 們 只 假 設 應 用 程 序 需 要 一 個 操 作 過 程 提 示 對 話 框 並 以“CModel” 作 為 對 應 的 對 話 框 類 的 名 字。
  • 使 用Visual C++ 提 供 的 資 源 編 輯 器 編 輯 提 示 對 話 框, 比 如 加 入 一 些 文 字 說 明 和 動 畫 等。
  • 在CModel 類 的 頭 文 件(Model.h) 中, 加 入 兩 個 成 員 變 量,
    CWnd* m_pParent; // 指 向 調 用 該 提 示 對 話 框 的 框 架 類( 或 對 話 框 類), 即 它 的“ 父 類” int m_nID;// 記 錄 該 提 示 對 話 框 的ID 號
    以 及 下 面 兩 個 成 員 函 數:
    CModel(CWnd* pParent = NULL); // 舍 棄 原 有 的 構 造 函 數, 或 者 把 原 函 數 修 改 成 這 種 無 模 式 對 話 框 的 構 造 函 數
    BOOL Create(); // 該 函 數 將 調 用 創 建 基 類 的Create() 函 數 創 建 對 話 框
  • 在Model.cpp 文 件 中, 加 入 相 應 函 數 的 實 現 部 分。

CModel::CModel(CWnd* pParent /*=NULL*/) : CDialog(CModel::IDD, pParent) { m_pParent=pParent; m_nID=CModel::IDD; //{{AFX_DATA_INIT(CModel) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT } BOOL CModel::Create() { return CDialog::Create(m_nID,m_pParent); }


  • 同 時 按 下Ctrl 和W 鍵 或 直 接 單 擊 工 具 條 上 的ClassWizard 按 鈕, 打 開ClassWizard 對 話 框。 在 類 名(Class name) 列 表 框 中 選 擇 該 提 示 對 話 框 類, 在Object IDs 列 表 框 中 選 擇 該 類 的 類 名 後, 在 消 息(Messages) 列 表 框 中 選 擇PostNcDestroy 消 息 並 雙 擊 它, 這 時ClassWizard 就 會 在 該 對 話 框 類 中 加 入 一 個PostNcDestroy() 函 數。 該 函 數 將 會 在 對 話 框 窗 口 消 失 後, 由OnNcDestroy() 函 數 調 用。 因 此, 可 以 在 該 函 數 中 加 入 一 些 掃 尾 工 作, 例 如 數 據 傳 送, 釋 放 指 針 空 間 等。

void CModel::PostNcDestroy() { // TODO: Add your specialized code here and/or call the base class delete this; CDialog::PostNcDestroy(); }


  • 在 要 調 用 提 示 對 話 框 類 的 類 的 頭 文 件 中, 先 包 含(#include)CModel 類 的 頭 文 件, 再 聲 明 一 個 指 向CModel 類 的 對 象 的 指 針, 如m_Dlg, 並 在 該 類 的 構 造 函 數 中, 加 入“m_Dlg = NULL;” 一 句。 然 後, 在 打 開 和 關 閉 提 示 對 話 框 的 函 數 中 加 入 如 下 一 段 程 序:

if (m_Dlg==NULL) {//如果當前沒用提示對話方塊在活動,就建立一個 m_Dlg = new CModel(this); m_Dlg->Create(); GetDlgItem(IDC_EXPORT)->EnableWindow(FALSE); } else//否則就啟用它 m_Dlg->SetActiveWindow(); 另外,再在要關閉提示對話方塊的地方,加入如下語句: m_Dlg->DestroyWindow(); m_Dlg=NULL;


---- 至 此, 您 已 經 擁 有 了 自 己 的 過 程 操 作 提 示 對 話 框。 不 過, 它 還 不 具 有 動 畫 和 隨 時 取 消 操 作 的 功 能。 您 不 妨 嘗 試 著 加 入 這 些 功 能。 另 外, 筆 者 也 曾 嘗 試 過 用 下 面 介 紹 的 方 法 實 現 過 程 操 作 提 示 對 話 框。 兩 種 方 法 比 較, 可 謂 各 有 千 秋。 如 果 您 希 望 上 面 設 計 的 過 程 提 示 對 話 框 能 夠 被 多 個 應 用 程 序 共 享, 那 麼 最 好 把 提 示 對 話 框 作 為 獨 立 的 進 程 來 調 用。 但 是, 當 您 還 希 望 在 提 示 對 話 框 與 調 用 者 之 間 傳 輸 數 據 的 話, 似 乎 這 一 部 分 介 紹 的 實 現 方 法 更 簡 潔 且 更 有 效。

應 用 進 程 實 現 對 其 他 應 用 程 序 的 調 用 ---- 在 我 們 設 計 的 應 用 程 序 中, 很 可 能 會 用 到 其 他 應 用 程 序 來 完 成 某 一 特 定 功 能。 例 如, 當 我 們 為 了 便 於 數 據 的 傳 輸 而 對 諸 多 文 件 進 行 壓 縮 和 解 壓 縮 時, 一 種 作 法 是 我 們 自 己 設 計 一 個 這 樣 的 壓 縮/ 解 壓 縮 程 序, 然 後 以 動 態 鏈 接 庫(DLL) 或 者 函 數 庫 的 形 式 由 主 應 用 程 序 調 用。 但 更 方 便 而 且 高 效 的 作 法 是 利 用 現 有 的 這 方 面 的 優 秀 軟 件, 比 如ARJ.EXE, 並 以 進 程 的 形 式 調 用 它, 再 在 適 當 時 候 關 閉 它。 下 面 將 以 上 面 所 述 為 例, 具 體 介 紹 後 一 種 方 法 的 實 現 過 程。

  • 在 需 要 調 用ARJ.EXE 進 行 壓 縮/ 解 壓 縮 的 類 中, 創 建 一 個 成 員 函 數, 不 妨 稱 作CreateBat(), 其 作 用 是 生 成 一 個 批 處 理 文 件。 由 該 批 處 理 文 件 調 用ARJ.EXE, 並 給 出 具 體 壓 縮/ 解 壓 縮 參 數。 之 後, 再 利 用MS-D的DIR 命 令 生 成 一 個 臨 時 文 件, 以 作 為 壓 縮/ 解 壓 縮 工 作 完 成 的 標 志。

void CMyCompress:: CreateBat(CString BatPath,CString ArjPath, CString BatName,CString ArjFileName, CString TempPath,CString ExitFlag,BOOL out) { LPTSTR lpBuffer; UINT uSize; HANDLE hHeap; uSize=(GetCurrentDirectory(0,NULL))*sizeof(TCHAR); hHeap=GetProcessHeap(); lpBuffer=(LPSTR)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,uSize); GetCurrentDirectory(uSize,lpBuffer); //得知當前目錄資訊,以便根據需要變換目錄 if (lpBuffer!=BatPath) //diferent dir SetCurrentDirectory(BatPath); CStdioFile f; CFileException e; if (!f.Open( BatName, CFile::modeCreate|CFile::modeWrite, &e)) //以BatName的內容建立一個批處理 { AfxMessageBox("不能建立檔案"+BatName); return ; } char density[6]; sprintf(density,"%d",mTotalBytes);


---- //mTotalBytes 是 由 其 他 函 數 設 定 的 變 量, 用 於 記 錄 用 於 拷 入 或 拷 出 文 件 的 磁 盤 所 具 有 的 最 大 可 用 空 間

CString Density=density; CString string; if (out)//說明是生成做工作的批處理檔案 string="arj a -v"+Density; else //說明是生成做解壓縮工作的批處理檔案 string="arj e -v"+Density; string+=" .."+ArjPath+""+ArjFileName+" "; if (out) string=string+".."+TempPath+"*.* -y -jmn"; else string=string+".."+TempPath+" -y -jmn"; f.WriteString(string); string="dir >"+ExitFlag+"n"; f.WriteString(string); f.Close(); SetCurrentDirectory(lpBuffer);//回覆到原來的目錄下 }


---- 該 函 數 執 行 後, 將 生 成 一 個 批 處 理 文 件, 內 容 大 致 是:
---- ARJ A -V1440 壓 縮 後 文 件 的 路 徑 名+ 文 件 名 被 壓 縮 文 件 的 路 徑 名+ 文 件 名 -Y -JM
---- DIR > 臨 時 文 件 名
---- 或 者 是:
---- ARJ E -V1440 被 解 壓 縮 文 件 的 路 徑 名+ 文 件 名 解 壓 縮 後 文 件 的 路 徑 名+ 文 件 名 -Y -JM
---- DIR > 臨 時 文 件 名

  • 在 需 要 調 用ARJ.EXE 進 行 壓 縮/ 解 壓 縮 的 類 中, 再 創 建 一 個 成 員 函 數, 不 妨 稱 作RunBat(), 其 作 用 是 創 建 和 執 行 進 程 來 運 行 上 述 所 生 成 的 批 處 理 文 件, 並 在 適 當 時 候 撤 消 進 程。

void CMyCompress::RunBat(CString BatPath,CString fileName,CString ExitFlag) { CString lpApplicationName=BatPath+""+fileName; // 進 程 執 行 的 應 用 程 序 的 完 全 路 徑 名 STARTUPINFO StartupInfo;// 創 建 進 程 所 需 的 信 息 結 構 變 量 GetStartupInfo(&StartupInfo); StartupInfo.lpReserved=NULL; StartupInfo.lpDesktop=NULL; StartupInfo.lpTitle=NULL; StartupInfo.dwX=0; StartupInfo.dwY=0; StartupInfo.dwXSize=200; StartupInfo.dwYSize=300; StartupInfo.dwXCountChars=500; StartupInfo.dwYCountChars=500; StartupInfo.dwFlags=STARTF_USESHOWWINDOW; StartupInfo.wShowWindow=SW_H; // 說 明 進 程 將 以 隱 藏 的 方 式 在 後 臺 執 行 StartupInfo.cbReserved2=0; StartupInfo.lpReserved2=NULL; StartupInfo.hStdInput=stdin; StartupInfo.hStdOutput=stdout; StartupInfo.hStdError=stderr; LPTSTR lpBuffer; UINT uSize; HANDLE hHeap; uSize=(GetCurrentDirectory(0,NULL))*sizeof(TCHAR); hHeap=GetProcessHeap(); lpBuffer=(LPSTR)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,uSize); GetCurrentDirectory(uSize,lpBuffer); // 得 知 當 前 目 錄 信 息, 以 便 根 據 需 要 變 換 目 錄 if (lpBuffer!=BatPath) //diferent dir SetCurrentDirectory(BatPath); // 創 建 進 程 if (CreateProcess(lpApplicationName,NULL,NULL, NULL,FALSE,CREATE_DEFAULT_ERROR_MODE, NULL,NULL,&StartupInfo,&pro_info)) { MSG Message; DeleteFile(ExitFlag); SetTimer(1,100,NULL);// 設 置 計 時 器 Search=TRUE; while(Search) { if (::PeekMessage(&Message,NULL,0,0,PM_REMOVE)) { ::TranslateMessage(&Message); ::DispatchMessage(&Message); } } // 進 程 結 束 前 後 的 處 理 工 作 DExitCode; if (!GetExitCodeProcess(pro_info.hProcess,&ExitCode)) AfxMessageBox("GetExitCodeProcess is Failed!"); if (!TenateProcess(pro_info.hProcess,(UINT)ExitCode)) // 終 止 進 程 AfxMessageBox("TerminateProcess is Failed!"); if (!CloseHandle(pro_info.hProcess)) // 釋 放 被 終 止 進 程 的 句 柄 AfxMessageBox("CloseHandle is Failed!"); KillTimer(1);// 撤 消 計 時 器 } else AfxMessageBox("Process Is Not Created!"); SetCurrentDirectory(lpBuffer);// 回 復 到 原 來 的 目 錄 下 }


  • 同 時 按 下Ctrl 和W 鍵 或 直 接 單 擊 工 具 條 上 的ClassWizard 按 鈕, 打 開ClassWizard 對 話 框。 在 類 名(Class name) 列 表 框 中 選 擇 需 要 調 用ARJ.EXE 進 行 壓 縮/ 解 壓 縮 的 類, 在Object IDs 列 表 框 中 選 擇 該 類 的 類 名 後, 在 消 息(Messages) 列 表 框 中 選 擇WM_TIMER 消 息 並 雙 擊 它, 這 時ClassWizard 就 會 在 該 類 中 加 入 一 個OnTimer() 函 數。 該 函 數 將 以 一 定 的 時 間 間 隔 檢 查 壓 縮/ 解 壓 縮 程 序 是 否 已 經 執 行 完 畢, 即 檢 查 作 為 標 志 的 臨 時 文 件 是 否 已 經 存 在, 並 及 時 修 改 狀 態 變 量“Search”, 以 便 通 知RunBat() 函 數 結 束 進 程。

void CMyCompress::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default CFile file; CFileException Error; if (file.Open(ExitFlag,CFile::modeRead,&Error)) { Search=FALSE; file.Close(); } }


自 編 刪 除 目 錄 及 其 下 屬 文 件 的 函 數 ---- 高 版 本 的MS-DOS 和Windows 95 都 提 供 了 一 個 可 以 刪 除 一 個 或 多 個 目 錄 及 其 下 屬 文 件 和 目 錄 的 命 令, 即DeleteTree 命 令。 然 而, 無 論 在MFC 類 庫 還 是 在Win32 函 數 庫 中, 都 沒 有 相 應 的 函 數 與 之 對 應。 這 樣, 當 我 們 在 自 己 設 計 的 應 用 程 序 中 需 要 用 到DeleteTree 的 功 能 時, 自 然 想 到 的 方 法 是 通 過 進 程 調 用 或 者 系 統 調 用 的 方 式( 正 如 上 面 部 分 所 述 的 那 樣) 調 用MD-DOS 或Windows 95 下 的DeleteTree 命 令。 然 而,Win32 函 數 庫 已 經 為 我 們 提 供 了 多 種 用 於 文 件 和 目 錄 操 作 的 函 數, 利 用 它 們 不 難 設 計 出 自 己 的DeleteTree() 函 數。

---- 讀 者 讀 到 這 裡, 也 許 會 感 到 有 些 疑 惑, 為 什 麼 第 六 部 分 強 調 進 程 調 用 優 於 自 我 設 計 的 函 數, 而 這 一 部 分 又 反 了 過 來 ? 是 的, 在 通 常 情 況 下, 調 用 應 用 程 序 內 部 的 函 數 比 使 用 進 程 或 者 調 用 外 部 函 數 更 靈 活 並 且 可 以 提 高 執 行 效 率, 也 便 於 修 改。 所 以, 象DeleteTree() 這 樣 的 功 能, 利 用 現 有 的 函 數 並 不 難 實 現, 自 然 就 最 好 通 過 內 部 函 數 的 方 式 來 完 成。 然 而, 象 設 計 一 個 壓 縮/ 解 壓 縮 這 樣 的 函 數 的 工 作 量, 並 不 比 通 過 進 程 調 用 來 使 用 現 成 品 的 開 銷 更 合 算, 因 為 它 至 少 需 要 我 們 了 解 壓 縮/ 解 壓 縮 的 復 雜 算 法, 而 且 調 試 和 維 護 它 也 需 要 一 定 代 價。 於 是, 這 個 時 候, 還 是 採 用“ 拿 來 主 義” 為 好。

---- 下 面, 給 出 我 自 己 設 計 的DeleteTree() 函 數, 僅 供 參 考。

BOOL DeleteTree(CString DirName) { //成功:返回TRUE;否則,返回FALSE BOOL Result; Result=PreRemoveDirectory(DirName) && RemoveDirectory(DirName); return Result; } BOOL PreRemoveDirectory(CString DirName) {//成功:返回TRUE;否則,返回FALSE LPTSTR lpBuffer; UINT uSize; CString fileName; HANDLE hHeap; BOOL result; HANDLE hFindFile; WIN32_FIND_DATA FindFileData; uSize=(GetCurrentDirectory(0,NULL))*sizeof(TCHAR); hHeap=GetProcessHeap(); lpBuffer=(LPSTR)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,uSize); GetCurrentDirectory(uSize,lpBuffer); if (lpBuffer!=DirName) {//調整當前目錄 SetCurrentDirectory(DirName); } hFindFile=FindFirstFile("*.*",&FindFileData); CString tFile; if (hFindFile!=INVALID_HANDLE_VALUE) { do { tFile=FindFileData.cFileName; if ((tFile==".")||(tFile=="..")) continue; if (FindFileData.dwFileAttributes== FILE_ATTRIBUTE_DIRECTORY){ if (DirName[DirName.GetLength()-1]!='') PreRemoveDirectory(DirName+''+tFile); else PreRemoveDirectory(DirName+tFile); if (!RemoveDirectory(tFile)) result=FALSE; else result=TRUE; } else if (!DeleteFile(tFile)) result=FALSE; else result=TRUE; } while (FindNextFile(hFindFile,&FindFileData)); FindClose(hFindFile); } else { SetCurrentDirectory(lpBuffer); return FALSE; } SetCurrentDirectory(lpBuffer); //回覆到原來的目錄下 return result; }


如 何 得 到 並 修 改 各 驅 動 器 的 信 息 ---- 在 設 計 和 文 件 輸 入/ 輸 出 有 關 的 應 用 程 序 時, 我 們 很 可 能 在 輸 入/ 輸 出 文 件 前, 需 要 了 解 一 下 源 驅 動 器 或 者 目 標 驅 動 器 的 各 項 信 息, 比 如 是 否 有 磁 盤 在 軟 驅 中, 它 是 否 已 打 開 寫 保 護, 以 及 現 有 磁 盤 的 容 量 等。 遺 憾 的 是,MFC 類 庫 中 沒 有 提 供 支 持 這 些 功 能 的 類, 所 以 我 們 只 能 通 過Win32 提 供 的 函 數 來 完 成 我 們 的 要 求。 下 面, 我 根 據 自 己 的 編 程 實 踐, 通 過 幾 段 程 序, 來 說 明 如 何 利 用Win32 提 供 的 函 數 實 現 對 驅 動 器 的 操 作。 讀 者 可 以 根 據 自 己 的 需 要, 把 介 紹 的 函 數 稍 加 修 改 後, 即 可 插 入 到 自 己 設 計 的 應 用 程 序 中 去。

  • 下 面 程 序 的 功 能 是 搜 索 計 算 機 中 所 有 驅 動 器, 選 擇 出 其 中 軟 盤 驅 動 器 的 驅 動 器 號, 依 次 加 入 到 一 個 下 拉 列 表 框 中。

void FindInfo() { CComboBox* Driver=(CComboBox*)GetDlgItem(IDC_DRIVER); DWORD dwNumByteorDriveStrings; HANDLE hHeap; LPSTR lp; CString strLogdrive; int nNumDrives=0, nDriveNum; dwNumBytesForDriveStrings=GetLogicalDriveStrings(0,NULL) *sizeof(TCHAR);//實際器號的字串長度 if (dwNumBytesForDriveStrings!=0) { hHeap=GetProcessHeap(); lp=(LPSTR)HeapAlloc(hHeap,HEAP_ZERO_MEMORY, dwNumBytesForDriveStrings);// GetLogicalDriveStrings(HeapSize(hHeap,0,lp),lp); StringBox.SetSize(dwNumBytesForDriveStrings/sizeof(TCHAR)+1); while (*lp!=0) { if (GetDriveType(lp)==DRIVE_REMOVABLE){ Driver->AddString(lp); StringBox[nNumDrives]=lp; nNumDrives++; } lp=_tcschr(lp,0)+1; } } else AfxMessageBox("Can't Use The Function GetLogicalDriveStrings!"); }


  • 下 面 介 紹 的EmptyDiskSpace() 函 數 主 要 負 責 清 空 指 定 驅 動 器 中 的 磁 盤, 同 時 它 還 負 責 記 錄 指 定 驅 動 器 中 磁 盤 的 容 量, 並 得 到 該 磁 盤 的 序 列 號。 在 該 函 數 中, 還 將 調 用 第 七 部 分 提 到 的PreRemoveDirectory() 函 數, 來 完 成 清 空 工 作。

BOOL EmptyDiskSpace(CString Driver) { BOOL result=TRUE; DWORDSectorsPerCluster; // address of sectors per cluster DWORDBytesPerSector; // address of bytes per sector DWORDNumberOfFreeClusters; // address of number of free clusters DWORDTotalNumberOfClusters; DWORDTotalBytes; DWORDFreeBytes; int bContinue=1; char DiskVolumeSerialNumber[30]; //儲存驅動器內當前的序列號 LPCTSTRlpPathName; // address of root directory of the file system LPTSTRlpVolumeNameBuffer=new char[12]; // address of name of the volume DWORDnVolumeNameSize=12; // length of lpVolumeNameBuffer DWORD VolumeSerialNumber; // address of volume serial number DWORD MaximumComponentLength; // address of system's maximum filename length DWORD FileSystemFlags; // address of file system flags LPTSTRlpFileSystemNameBuffer=new char[10]; // address of name of file system DWORDnFileSystemNameSize=10; // length of lpFileSystemNameBuffer lpRootPathName=Driver; while (1){ if (GetDiskFreeSpace(Driver, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters)) {//驅動器中有磁碟 TotalBytes=SectorsPerCluster*BytesPerSector *TotalNumberOfClusters;//磁碟總容量 FreeBytes=SectorsPerCluster*BytesPerSector *NumberOfFreeClusters;//磁碟空閒空間容量 GetVolumeInformation(lpRootPathName, lpVolumeNameBuffer, nVolumeNameSize, &VolumeSerialNumber, &MaximumComponentLength, &FileSystemFlags, lpFileSystemNameBuffer, nFileSystemNameSize); sprintf(DiskVolumeSerialNumber,"%X",VolumeSerialNumber); //得到驅動器內當前磁碟的序列號 SetmTotalBytes(TotalBytes/1024);//儲存指定驅動器中磁碟的容量 if (TotalBytes!=FreeBytes){//當磁碟總容量不等於空閒空間容量時, 應該清空操作 while (bContinue) { if ((bContinue==2)||(MessageBox ("在驅動器 "+m_Driver+"中的磁碟尚存有資料. n您願意讓為您刪除它們嗎?", "提問",MB_YESNO|MB_ICONQUESTION)==IDYES)) if (!PreRemoveDirectory(Driver))//無法執行清空操作 if (MessageBox("因某種原因系統無法刪除 在驅動器 "+m_Driver+"中的磁碟上的資料. n請檢查磁碟是否沒有關閉防寫. n您願意再試一次嗎?", "問題",MB_YESNO|MB_ICONERROR)==IDYES) { bContinue=2; continue; } else { bContinue=0; result=FALSE; } else { MessageBox("成功刪除磁碟上的資料!", "提示資訊",MB_OK|MB_ICONINFORMATION); bContinue=0; result=TRUE; } else {//THE FIRST IF'S ELSE bContinue=0; result=FALSE; } } } else result=TRUE; break; } else { if (MessageBox("沒有磁碟在驅動器 "+m_Driver+"中. n您願意插入一張磁碟再來一次嗎?", "問題",MB_YESNO|MB_ICONASTERISK)==IDYES) continue; else break; } }//END OF WHILE return result; }


  • 在MS-DOS 和Windows95 中, 磁 盤 卷 標 最 多 由11 個 字 符 組 成, 並 且 字 母 的 大 小 寫 不 加 區 分。 當 需 要 設 定 指 定 驅 動 器 中 磁 盤 的 卷 標 時, 只 要 調 用Win32 的SetVolumeLabel() 函 數 即 可, 並 在 第 一 個 參 數 中 指 明 磁 盤 所 在 的 驅 動 器 號, 在 第 二 個 參 數 中 指 明 新 的 卷 標 號。 例 如,SetVolumeLabel(DriverNum, NewVolumeLabel)。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-996098/,如需轉載,請註明出處,否則將追究法律責任。

相關文章