仿製金山毒霸專殺工具界面

金山毒霸2002自推出以來,廣受用戶的喜愛。今天在這裏我要談的不是金山毒霸的功能,而是其別具一格的界面。本人早就對金山毒霸的界面垂涎三尺,如果自己的程序裏能用上金山毒霸那樣的界面,那該多爽!

說幹就幹,本着由淺入深的原則,我選擇了金山毒霸的一個附帶工具:Duba_GOP.exe來開刀。該工具在金山毒霸的網站上可以下載到,其

界面如圖所示:

好,開始準備工具:

提取資源工具:freeRes

編程工具:VC++

還有一個Vc自帶的小工具:spy++

之所以不用大家都熟悉的eXeScope,是因爲Duba_GOP.exe已經被壓縮過了,有些資源提取工具無法再用。

用freeRes打開Duba_GOP.exe,可看到其帶有20個bmp位圖,有整個窗體的背景圖,右上角兩個小按鈕“主頁”和“關閉”的三態位圖,以及兩個大按鈕“瀏覽文件夾”和“開始掃描”的三態位圖。看到這些圖片,即可猜到該軟件是沒有標題欄的,而顯示出來的標題欄只是背景,而且連同窗體下部的金山毒霸的標誌都是屬於同一幅背景圖片!那麼“標題欄”上的“主頁”,“關閉”是怎麼回事?用spy++來試一下,發現這兩個是Button,而不是Bitmap。同樣“瀏覽文件夾”和“開始掃描”也是Button,隨即想到這四個Button可以用VC中的CBitmap類來實現。而窗體下部的超鏈接僅僅是Static靜態框。把這些難點分析出來了,下面就可以開始我們的編程之旅了,不過記得把有用的圖片保存下來。 

 一.窗體背景問題

打開vc,新建一個對話框工程,名爲Interface。去掉缺省的一個Static和兩個Button,在窗體上點右鍵,在出現的屬性對話框中,去掉標題欄,再將用freeRes提取的所有圖片Import進工程的資源中。修改CInterfaceDlg::OnPaint()如下:

01.void CInterfaceDlg::OnPaint()
02.{
03.if (IsIconic())
04.{
05.CPaintDC dc(this); // device context for painting
06.SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
07.// Center icon in client rectangle
08.int cxIcon = GetSystemMetrics(SM_CXICON);
09.int cyIcon = GetSystemMetrics(SM_CYICON);
10.CRect rect;
11.GetClientRect(&rect);
12.int x = (rect.Width() - cxIcon + 1) / 2;
13.int y = (rect.Height() - cyIcon + 1) / 2;
14.// Draw the icon
15.dc.DrawIcon(x, y, m_hIcon);
16.}
17.else
18.{
19.//      CDialog::OnPaint();//將這一句註銷掉
20.*********************************************************
21.CPaintDC dc(this);
22.CRect rect;
23.GetClientRect(&rect);//得到窗體的大小
24.CDC dcMem;
25.dcMem.CreateCompatibleDC(&dc);
26.CBitmap bmpBackground;
27.bmpBackground.LoadBitmap(IDB_BITMAPBACKGROUND);//加載背景圖片
28.BITMAP bitMap;
29.bmpBackground.GetBitmap(&bitMap);
30.CBitmap *pbmpOld=dcMem.SelectObject(&bmpBackground);
31.dc.StretchBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0,bitMap.bmWidth,bitMap.bmHeight,SRCCOPY);//畫窗體
32.********************************************************
33.}
34.}

其中兩個“*”行之間的部分爲所添加部分。

這段代碼的作用是將那幅背景圖片畫在窗體上, 其中我使用了StretchBlt來畫出窗體,StretchBlt比BitBlt要慢許多,如果你想得到更好的速度,可以考慮建立與背景位圖相同尺寸的窗體,然後用BitBlt來畫出。CBitmap bmpBackground;最好作爲CInterfaceDlg的成員變量並在CInterfaceDlg::OnInitDialog中提前加載位圖,感興趣的朋友可以試試。

編譯,運行。可看到修改後的效果,由於該圖片是平鋪在窗體上的,如果圖片和窗體尺寸不一致就會導致圖片發生扭曲。因此可將窗體調整到與圖片相同或相近的尺寸大小,那麼運行後看起來就順眼多了。但是窗體邊框還是不大對勁,於是再進vc的資源編輯器,將窗體的Border由“Dialog Frame”該爲“Thin”,重新編譯,運行,搞定!

但是沒過一秒鐘就覺得這話說早了,標題欄沒了,窗體怎麼移動啊?

別急,車到山前必有路,我們不妨騙騙Windows。當我們的鼠標在窗體任意位置上拖動鼠標時,讓Windows以爲鼠標在標題欄上拖動,不就可以實現任意點擊窗體的什麼地方都可以移動窗體了嗎?於是在CInterfaceDlg::OnLButtonDown(UINT nFlags, CPoint point)中添加下面一句:
1.PostMessage(WM_NCLBUTTONDOWN,HTCAPTION,MAKELPARAM(point.x, point.y));

該語句的作用是向系統發送HTCAPTION消息,讓系統以爲鼠標點在標題欄上。

現在編譯,運行,真的搞定了!

二.按鈕問題

Duba_GOP.exe中包括兩類按鈕,第一類就是上面所說的四個圖片按鈕,可以CBitmap類來實現:

在窗體上添加四個Button,設爲Bitmap和OwnerDraw風格。爲窗體添加4個CBitmap的成員變量,比如:

1.CBitmapButton   m_StartBtn;

並與這四個Button關聯上,再在CInterfaceDlg::OnInitDialog()中添加:

1.m_StartBtn.LoadBitmaps(IDB_BITMAPONNORMAL,IDB_BITMAPONDOWN);//讓窗體初始化時就加載按鈕位圖

其中IDB_BITMAPONNORMA是一般狀態下的按鈕的圖片ID,IDB_BITMAPONDOWN是鼠標按下時的按鈕的圖片ID。 合理擺放這四個按鈕的位置,使其與Duba_GOP.exe界面上的位置一致,現在運行一下看看,界面增色不少吧。只是有一點:“開始掃描”這個按鈕比較特殊,當鼠標點擊時,會變成停止的位圖,當再次點擊時,又變成開始的位圖,因此這個按鈕擁有兩套,4個位圖。爲此,再爲CInterfaceDlg添加一個成員變量:

1.BOOL m_bEnable;

並在CInterfaceDlg::OnInitDialog()中設其初值爲True。

然後在點擊“開始掃描”按鈕的相應事件CInterfaceDlg::OnStartbtn()中添加:

01.if(m_bEnable)
02.{
03.m_StartBtn.LoadBitmaps(IDB_BITMAPOFFNORMAL,IDB_BITMAPOFFDOWN);//加載位圖
04.m_bEnable=false;
05.m_StartBtn.RedrawWindow();
06.}
07.else
08.{
09.m_StartBtn.LoadBitmaps(IDB_BITMAPONNORMAL,IDB_BITMAPONDOWN);//加載另一套位圖
10.m_bEnable=true;
11.m_StartBtn.RedrawWindow();
12.}

以上代碼的作用是控制兩套位圖的切換。

好了,現在輪到Duba_GOP.exe界面中上部的那三個CheckBox風格的Button了。首先添加三個這樣的按鈕,但看上去與Duba_GOP.exe的按鈕不太一樣,於是將我的三個按鈕加上“平坦”的風格,OK,行了。

編譯,運行,頓時大吃一驚,原來新添加的三個CheckBox的背景還是缺省的灰色,與白色的窗體背景極不協調,非常難看。看來需要改變該類Button的背景顏色了。於是,從CButton類派生出一個CColorButton類,其主要代碼如下:

01.// ColorButton.h : header file
02.class CColorButton : public CButton
03.{
04.……//省略了無關代碼
05.public:
06.CBrush m_brush;
07.void SetBackColor(COLORREF BackColor);
08.protected:
09.COLORREF m_BackColor;
10.//{{AFX_MSG(CColorButton)
11.afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
12.//}}AFX_MSG
13.DECLARE_MESSAGE_MAP()
14.……//省略了無關代碼
15.}
16. 
17.// ColorButton.cpp
18. 
19.BEGIN_MESSAGE_MAP(CColorButton, CButton)
20.//{{AFX_MSG_MAP(CColorButton)
21.ON_WM_CTLCOLOR_REFLECT()
22.//}}AFX_MSG_MAP
23.END_MESSAGE_MAP()
24. 
25.void CColorButton::SetBackColor(COLORREF BackColor)
26.{
27.m_BackColor=BackColor; //設置背景顏色
28.m_brush.CreateSolidBrush(m_BackColor); //創建畫刷
29.}
30. 
31. 
32.HBRUSH CColorButton::CtlColor(CDC* pDC, UINT nCtlColor)
33.{
34.pDC->SetBkMode(TRANSPARENT);//將背景設爲透明
35.return (HBRUSH)m_brush;//返回自定義畫刷
36.//  return NULL;//將缺省的處理註銷掉
37.}  

好了,新類構造完成了。如何使用呢?將這三個CheckBox按鈕聲明爲CColorButton類的三個成員變量,比如:

1.CColorButton m_ColorBtn1;

不過要記得在CInterfaceDlg::OnInitDialog()中添加:

1.m_ColorBtn1.SubclassDlgItem(IDC_CHECK1,this);// IDC_CHECK1是其中一個CheckBox的ID
2.m_ColorBtn1.SetBackColor(RGB(222,223,222));// RGB(222,223,222)就是窗體背景顏色

現在編譯一下,可以看到這三個按鈕已經融入窗體背景之中,按鈕問題已經全部解決了。

三.Static問題

這個界面上Static不少,中上部有一個Static有一種凹下去的效果,其實只需要一般的Static設置了“下沉”的風格即可。其餘幾個Static可算作一類,存在的問題和前面的一樣,也就是背景顏色問題,爲此又從CStatic中派生出CcolorStatic類,主要代碼如下:

01.// ColorStatic.h : header file
02.class CColorStatic : public CStatic
03.{
04.……//省略了無關代碼
05.public:
06.void SetTextColor(COLORREF TextColor);
07.COLORREF m_TextColor;
08.void SetBackColor(COLORREF BackColor);
09.void SetCaption(CString strCaption);
10.void Create(CString strCaption,COLORREF BackColor);
11.COLORREF m_BackColor;
12.CString m_strCaption;
13.protected:
14.//{{AFX_MSG(CColorStatic)
15.afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
16.afx_msg void OnPaint();
17.//}}AFX_MSG
18.DECLARE_MESSAGE_MAP()
19.}
20. 
21. 
22.// ColorStatic.cpp
23.BEGIN_MESSAGE_MAP(CColorStatic, CStatic)
24.//{{AFX_MSG_MAP(CColorStatic)
25.ON_WM_CREATE()
26.ON_WM_PAINT()
27.//}}AFX_MSG_MAP
28.END_MESSAGE_MAP()
29. 
30.void CColorStatic::OnPaint() //重畫Static
31.{
32.CPaintDC dc(this); // device context for painting
33.CRect rect;
34.GetClientRect(&rect);
35.dc.SetBkColor(m_BackColor);
36.dc.SetBkMode(TRANSPARENT);
37.CFont *pFont=GetParent()->GetFont();//得到父窗體的字體
38.CFont *pOldFont;
39.pOldFont=dc.SelectObject(pFont);//選用父窗體的字體
40.dc.SetTextColor(m_TextColor);//設置文本顏色
41.dc.DrawText(m_strCaption,&rect,DT_CENTER);//將文本畫在Static的中央
42.dc.SelectObject(pOldFont);
43. 
44.// Do not call CStatic::OnPaint() for painting messages
45.}
46. 
47.void CColorStatic::SetCaption(CString strCaption)
48.{
49.m_strCaption=strCaption;//設置Static文本
50.}
51. 
52.void CColorStatic::SetBackColor(COLORREF BackColor)
53.{
54.m_BackColor=BackColor;//設置背景顏色
55.}
56. 
57.void CColorStatic::SetTextColor(COLORREF TextColor)
58.{
59.m_TextColor=TextColor;//設置文字顏色
60.}

OK,這也是個很簡單的類,主要功能就是可以設置該Static的文字顏色和背景顏色,但是已經基本滿足我們的需要了。有一點缺陷就是下面三個Static應該還具備超鏈接的功能,不過你可以通過修改這個類來實現。或者直接用一個超鏈接類來替代也可以。

總之,這個問題也解決了,現在我們的程序運行起來已經非常像金山毒霸的Duba_GOP.exe了,做到這一步仿製任務已經基本完成,但是低頭看看系統的任務欄你會發現自己的程序還不夠完美,因爲在任務欄上屬於自己程序的那個小方塊上空空如也,太不專業了。

請注意Windows是將程序的標題欄上的Caption和圖標顯示在任務欄上,而我的這個程序沒有標題欄,故而什麼也顯示不出來,怎麼辦呢?辦法在這裏:

1.恢復標題在對話框的OnInitDialog()中添加:

SetWindowText("金山毒霸專殺工具");//設置對話框的標題爲金山毒霸專殺工具

2.恢復圖標

在對話框上打開屬性對話框,重新選上“System Menu”和“Title Bar”風格。然後在對話框的OnInitDialog()中添加:

ModifyStyle(WS_CAPTION,WS_MINIMIZEBOX,SWP_DRAWFRAME);

現在運行起來看看,瞧!系統任務欄上熟悉的圖標和標題又回來了。但是這樣又帶來一個問題:窗體最下面的那個Static在運行後與背景圖片上金山毒霸2002幾個字重疊在一起,很不好看。而且由於對話框的尺寸有限制,這個Static也移不到合適的位置,於是咱們只好在程序裏動動腦筋了。比如在OnInitDialog()里加上幾句:

1.CRect rect1;
2.m_Link3.GetWindowRect(&rect1);
3.rect1.top+=17;
4.rect1.bottom+=17;
5.m_Link3.MoveWindow(rect1.left,rect1.top,rect1.Width(),rect1.Height());//往下移17

其中的m_Link3就是代表那個Static的成員變量。至於那個ListCtrl嘛,只不過在它的風格里去掉了邊框而已。自此,大功告成!

該程序在VC6+Win98/2k/xp下調試通過。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章