程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 利用VC++編寫Windows95的CPL組件

利用VC++編寫Windows95的CPL組件

編輯:關於VC++

控制面板是Windows 95 的控制中心,通過它可以完成添加新硬件設備、改變桌面設置、配置網絡協議等多項工作。在Windows 95 中,控制面板通常有20 多個組件,我們只要用鼠標雙擊任一組件的圖標,就會彈出一個對話框,對話框包含有設置一些系統參數的選項,這些參數的絕大多數都存放在Windows 95 的注冊表中。

---- 控制面板的主程序是CONTROL.EXE,它在啟動時自動Winndows\System 目錄下查找並調入文件擴展名為CPL 的控制面板組件。控制面板組件是可以擴充的,一些軟件在安裝過程中會自動加入新的控制面板組件。按照Windows 用戶界面設計原則的規定,凡是影響到系統的整體行為和界面風格的各項參數都應該通過控制面板來設置,因此掌握控制面板組件的編程方法是很有必要的。

控制面板組件的工作原理---- 控制面板的各個組件都是一些特殊的動態鏈接庫,只不過它們的擴展名不是DLL,而是CPL,即Control Panel 的縮寫。

---- CONTROL.EXE 啟動後會依次調入在系統目錄下查找到的CPL 庫。一般來說,一個CPL 庫只負責管理某一方面的設置,對應著控制面板中的一個組件( 即一個圖標),但也有少數CPL 庫支持多個組件。

---- 每個CPL 庫必須輸出一個叫CPlApplet() 的函數供CONTROL.EXE 調用,CPlApplet() 具有以下原型:

typedef LONG (APIENTRY *APPLET_PROC)(HWND hwndCpl, UINT msg,LONG lParam1,LONG lParam2);

---- 容易看出,CPlApplet() 與普通窗口處理函數的形式很相似,事實上,控制面板正是以發送消息的方式與CPL 庫進行通信。參數hwndCpl 為控制面板的窗口句柄,msg 為消息標識,lParam1 和lParam2 為附加的兩個參數,具體的意義視msg 的值而定。

---- 控制面板用LoadLibrary() 函數把CPL 庫調入內存以後,立刻向CPlApplet() 發送一條CPL_INIT 消息,指示CPL 庫作初始化工作。

---- 因為這是唯一允許返回失敗信息的消息,所以CPL 庫此時應該分配運行過程中需要的所有內存和資源,如果因為內存不夠或者其它原因不能繼續,就返回零值,控制面板將不再處理這個CPL 庫,並自動卸下它。

---- 所有CPL 庫初始化完畢後,控制面板再向每個CPL 庫的CPlApplet() 函數發送一條CPL_GETCOUNT 消息,此時CPL 庫返回它所支持的組件數。接下來,控制面板再針對每一個組件向CPlApplet() 函數發送多條CPL_NEWINQUIRE 消息,目的是取得每個組件對應的圖標、名稱和提示信息,CPL 庫可以在處理這條消息時依次初始化各個組件的對話框。在Windows 3.x 中,控制面板發送的是CPL_INQUIRE 消息,考慮到兼容性的問題,這條消息在Windows 95 中被保留下來了,但是基於WIN 32 的CPL 庫只需處理新的CPL_NEWINQUIRE 消息。

---- 進行到這一步後,控制面板顯示出所有組件的圖標,並開始接受用戶的選擇。當用戶雙擊某個組件的圖標時,控制面板向該組件所在的CPL 庫發送一條CPL_DBLCLK 消息,並指明用戶選擇的是該CPL 庫中的第幾個組件,CPL 庫在接到這條消息後從INI 文件或Windows 95 的注冊表中讀出要處理的系統參數的原始值,並啟動相應的對話框,允許用戶改變設置。當用戶在修改過程中按下應用(Apply) 按鈕後,CPL 庫保存新的參數並返回到控制面板中;如果用戶取消了所作修改,CPL 庫只需返回即可。

---- 控制面板在被關閉時會對每個組件發送一條CPL_STOP 消息,接著對每個CPL 庫發送一條CPL_EXIT 消息,此時CPL 庫釋放在CPL_INIT 消息中分配的內存和資源。最後控制面板依次卸下各個CPL 庫並退出。

---- 上面敘述的就是控制面板組件的工作原理,其中各條消息的具體參數定義請參考WIN 32 SDK。

利用VC++ 編寫控制面板組件---- 編寫控制面板組件實際上是編寫DLL,利用Visual C++ 這個強大的可視化編程工具可以很方便地完成這項工作。MFC 基本類庫為我們封裝了DLL 的基本框架,我們只需編寫處理消息的CPlApplet() 函數和各個組件的對話框即可。遺憾的是,MFC 類庫中沒有現成的關於控制面板組件的類,為了充分利用C++ 語言可繼承性的優點,本文後面的程序給出了一個控制面板組件的基類CControlPanel,它的成員函數提供了處理各種CPL 消息的缺省代碼,我們只要從這個基類派生出新的子類,並為需要處理的消息重載相應的代碼,就可以迅速建立一個控制面板組件。

---- 利用Visual C++ 中編寫控制面板組件的步驟如下:

調用AppWizard 建立一個新的項目,將應用程序類型為設使用MFC 的DLL,並把MFC 類庫作為靜態庫連接,按下Finish 按鈕,讓AppWizard 自動生成框架文件。

把本文後面的CtrlPan.CPP 加入到項目中,把CPlApplet 添加到DEF 文件的輸出名表中,然後選擇Build 菜單的Settings,修改輸出文件的擴展名為CPL。

從CControlPanel 中派生出新的子類,並重載部分消息代碼。多數情況下只需要重載處理CPL_NEWINQUIRE 和CPL_DBLCLK 消息的函數就行了,如下所示:

#include "ctrlpan.h"
class CNewCPL : public CControlPanel
{
public:
virtual LONG OnInquire(UINT uAppNum, NEWCPLINFO* pInfo);
virtual LONG OnDblClk(HWND hwndCPl, UINT uAppNum, LONG lData);
};

---- 如果要在一個CPL 庫中支持多個組件,那麼至少還要重載OnGetCount() 函數。

編寫消息處理代碼,OnInquire() 函數負責返回組件的各種信息,可參考基類中該函數的實現代碼,OnDblClk() 函數負責讀取和保存各個參數,並調用對話框讓用戶選擇。

設計對話框,用ClassWizard 生成對話框的處理代碼,並修改這些代碼使之符合要求。

源代碼編寫完畢後,編譯連接,把生成的CPL 文件拷則到SYSTEM 目錄下,運行控制面板進行調試。

調試正確後,重新建立CPL 庫的Release 版。

// CtrlPan.h:類CControlPanel 的聲明

#ifndef _CTRLPAN_H_
#define _CTRLPAN_H_
#include   //VC 提供的頭文件
class CControlPanel
{
public:
  CControlPanel();
  virtual ~CControlPanel();
  // 可重載的消息處理函數
  virtual LONG OnDblClk(HWND hwndCPl, UINT uAppNum, LONG lData);
  virtual LONG OnExit();
  virtual LONG OnGetCount();
  virtual LONG OnInit();
  virtual LONG OnInquire(UINT uAppNum, NEWCPLINFO* pInfo);
  virtual LONG OnSelect(UINT uAppNum, LONG lData);
  virtual LONG OnStop(UINT uAppNum, LONG lData);
          virtual LONG OnExit();
  // CPL 庫的輸出函數
  static LONG APIENTRY CPlApplet(HWND hwndCPl, UINT uMsg,LONG lParam1, LONG lParam2);
   static CControlPanel* m_pThis;
};
#endif // _CTRLPAN_H_
// CtrlPan.cpp, 定義了類CControlPanel 的缺省處理函數
#include "stdafx.h"
#include "ctrlpan.h"
CControlPanel* CControlPanel::m_pThis = NULL;
CControlPanel::CControlPanel()
{          m_pThis = this; }
CControlPanel::~CControlPanel()
{ }
// CPL 庫的輸出函數
LONG APIENTRY CControlPanel::CPlApplet(HWND hwndCPl, UINT uMsg,LONG lParam1, LONG lParam2)
{
  CControlPanel* pCtrl = m_pThis;
  ASSERT(pCtrl); // 檢查pCtrl 的有效性
  switch (uMsg) {
  case CPL_DBLCLK:
          return pCtrl->OnDblClk(hwndCPl, lParam1, lParam2);
          case CPL_EXIT:
    return pCtrl->OnExit();
          case CPL_GETCOUNT:
    return pCtrl->OnGetCount();
  case CPL_INIT:
    return pCtrl->OnInit();
  case CPL_NEWINQUIRE:
    return pCtrl->OnInquire(lParam1, (NEWCPLINFO*)lParam2);
  case CPL_INQUIRE:
    return 0; // 基於WIN32 的CPL 庫不處理這條消息
  case CPL_SELECT:
    return pCtrl->OnSelect(lParam1, lParam2);
  case CPL_STOP:
    return pCtrl->OnStop(lParam1, lParam2);
          case CPL_EXIT:
            retrun pCtrl->OnExit();
  default: break;
  }
  return 1;
}
// 缺省的消息處理函數
LONG CControlPanel::OnDblClk(HWND hwndCPl, UINT uAppNum, LONG lData)
{ return 0; }
LONG CControlPanel::OnExit()
{ return 0; }
LONG CControlPanel::OnGetCount()
{ return 1; } // 缺省為一個組件
LONG CControlPanel::OnInit()
{ return 1; }
LONG CControlPanel::OnInquire(UINT uAppNum, NEWCPLINFO* pInfo)
{
  // 填充NEWCPLINFO 結構,結構的定義請參考VC 的聯機幫助
  pInfo->dwSize = sizeof(NEWCPLINFO);
  pInfo->dwFlags = 0;
  pInfo->dwHelpContext = 0;
  pInfo->lData = 0;
  pInfo->hIcon = ::LoadIcon(AfxGetResourceHandle(), MAKEINTRESOURCE(1));
  strcpy(pInfo->szName, "Applet");
  strcpy(pInfo->szInfo, "Default Control Panel Applet");
  strcpy(pInfo->szHelpFile, "");
  return 0;
}
LONG CControlPanel::OnSelect(UINT uAppNum, LONG lData)
{ return 1; }
LONG CControlPanel::OnStop(UINT uAppNum, LONG lData)
{ return 1; }
LONG CControlPanel::OnExit()
{ return 1; }

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved