Windows 7程式開發系列之一(工作列篇)

來源:互聯網
上載者:User

Windows 7 引入了很多新特性,其中最直觀的莫過於使用者介面上的變化。很多人也因為不能適應這種變化而回到了XP。但是在我看來這些新的特性卻是一種進步,使用了一段時間之後,也萌生了要做一點開發的衝動。於是把以前一個電源管理的小軟體重寫了一次(點此下載 ),利用了Windows 7 的工作列特性和JumpList。

  關於Windows 7開發的中文資料比較少。微軟官方的教程還比較豐富,但都是英文的,可能某些英文不太好的朋友學起來比較吃力。我把工作列和JumpList這兩個方面做一個簡短的教程,希望對後來者有所協助。

  工作列方面的東西比JumpList稍微簡單一點,就先從工作列說起。Windows7的工作列包含了幾個新的特性:Progress Bar(進度條)、Overlay Icon(覆蓋表徵圖)、 Thumbnail(縮圖)、Thumbnail Toolbar(位於縮圖下方的工具列)、Tooltip(滑鼠指向時的提示資訊)、Aero Peek Preview(當滑鼠停放在縮圖上時顯示視窗預覽)。下面是一張foobar2000運行時的:

  

  中foobar2000使用Progress Bar顯示當前歌曲的播放進度,並且在右下角有一個白色的小三角形(Overlay Icon)顯示當前是播放還是暫停,使用Thumbnail顯示唱片封面,Thumbnail Toolbar有三個按鈕分別是上一曲、暫停、下一曲,上方的Tooltip提示當前播放曲目,當滑鼠放在縮圖上時,Aero Peek功能會隱藏所有視窗,只顯示當前視窗的預覽圖。上面的Progress Bar和Overlay Icon不太清晰,下面的比較清晰:

  

  這篇文章中,將會講解這些功能的開發。

  MSDN上很容易找到SDK的,我就不貼了。SDK有1.44G,下載需要一點時間。安裝過程也沒什麼可講,就是安裝完後在開始菜單中找到Microsoft Windows SDK v7.0->Visual Studio Registration->Windows SDK Configuration Tool,將v7.0設定為目前的版本,這樣VS中的Windows SDK將使用v7.0版。 與工作列按鈕相關的功能都在這個介面中,Progress Bar, Overlay Icon等。首先建立一個Win32項目,並建立一個簡單的視窗,具體不再贅述,文章末尾會給出代碼。在WinMain函數的開頭,註冊一個"TaskbarButtonCreated"的訊息,

  //註冊使用者訊息

  WM_TASKBARBUTTONCREATED=::RegisterWindowMessage (TEXT ("TaskbarButtonCreated" ));

  這樣我們在WndProc中就可以收到我們註冊的 WM_TASKBARBUTTONCREATED訊息了。在這個訊息中,建立ITaskbarList4 介面對象,並調用初始化方法。至於COM相關的內容,已經超出了本教程的範圍,有興趣的可以參考相關資料。

  //建立介面對象 ITaskbarList

  if (msg==WM_TASKBARBUTTONCREATED )

  {

  ::CoCreateInstance (CLSID_TaskbarList,NULL,CLSCTX_INPROC_SERVER,IID_PPV_ARGS (&g_pTaskbar ));

  g_pTaskbar->HrInit ();

  } SetProgressState 方法設定進度條的狀態,實際反映出來就是進度條的顏色,它接受一個視窗控制代碼和一個狀態標記為參數。其中 TBPF_NOPROGRESS 為取消進度條,TBPF_INDETERMINATE 是一個不斷滾動的,無確定值的進度條。

  SetProgressValue 方法設定進度條的值,也就是當前進度。它的參數是視窗控制代碼、當前值和總值。如果用這個方法設定了進度條,之前設定的TBPF_INDETERMINATE 將無效。

  SetOverlayIcon 設定覆蓋表徵圖,他的參數是視窗控制代碼、表徵圖控制代碼和描述。其中表徵圖要求16X16大小。如果表徵圖控制代碼為NULL,則取消覆蓋表徵圖。

  在g_pTaskbar對象初始化後,加入下面代碼:

  g_pTaskbar->SetProgressState (g_hWnd,TBPF_ERROR );

  g_pTaskbar->SetProgressValue (g_hWnd,50,100 );

  g_pTaskbar->SetOverlayIcon (g_hWnd,g_hRed,TEXT ("Error" ));

  這段代碼將進度條狀態設定為ERROR(紅色),進度為50%,並在右下角顯示一個紅色表徵圖(表徵圖已在WinMain函數中載入)。運行後效果如下:

  

  Toolbar稍微麻煩一點,需要設定一個結構THUMBBUTTON 。其中參數dwMask 是一些標記的組合,這裡我們設定為THB_ICON | THB_TOOLTIP ,即我們會使用icon和tooltip這兩個欄位(注意此處的tooltip是按鈕的tooltip,不要與上面的tooltip混淆了)。iId 關係到事件的響應,可按順序編號。hIcon 是該按鈕使用的表徵圖控制代碼。szTip 中設定提示資訊。使用下面的代碼增加3個按鈕:

  THUMBBUTTONMASKdwMask=THB_ICON | THB_TOOLTIP ;

  THUMBBUTTONthbButtons[3];

  thbButtons[0].dwMask=dwMask ;

  thbButtons[0].iId=0 ;

  thbButtons[0].hIcon=g_hRed ;

  StringCbCopy (thbButtons[0].szTip,sizeof (thbButtons[0].szTip),TEXT ("Red" ));

  thbButtons[1].dwMask=dwMask ;

  thbButtons[1].iId=1 ;

  thbButtons[1].hIcon=g_hGreen ;

  StringCbCopy (thbButtons[1].szTip,sizeof (thbButtons[1].szTip),TEXT ("Green" ));

  thbButtons[2].dwMask=dwMask ;

  thbButtons[2].iId=2 ;

  thbButtons[2].hIcon=g_hBlue ;

  StringCbCopy (thbButtons[2].szTip,sizeof (thbButtons[2].szTip),TEXT ("Blue" ));

  g_pTaskbar->ThumbBarAddButtons (g_hWnd,ARRAYSIZE (thbButtons),thbButtons );

  效果如下:

  

  按鈕有了,該響應這些按鈕的訊息了。點擊這些按鈕後,windows將會發送一個WM_COMMAND 訊息,其中wParam 的高位為THBN_CLICKED ,低位是按鈕的id(由參數iId設定 )。

  caseWM_COMMAND:

  if (HIWORD (wParam ) ==THBN_CLICKED )

  {

  OnThumbnailButtonClicked (LOWORD (wParam ));

  }

break ;

  voidOnThumbnailButtonClicked ( intid )

  {

  switch (id )

  {

  case0:

  SetForegroundWindow (g_hWnd );

  MessageBox (g_hWnd,TEXT ("Red button clicked!"),NULL,MB_OK );

  break ;

  case1:

  SetForegroundWindow (g_hWnd );

  MessageBox (g_hWnd,TEXT ("Green button clicked!"),NULL,MB_OK );

  break ;

  case2:

  SetForegroundWindow (g_hWnd );

  MessageBox (g_hWnd,TEXT ("Blue button clicked!"),NULL,MB_OK );

  break ;

  }

  }

  現在點擊工具列上的按鈕將會有顯示對應的訊息框。

  Tooltip比較簡單。SetThumbnailTooltip 傳入視窗控制代碼和描述的文字就可以了。在 WM_TASKBARBUTTONCREATED 訊息處理中加入下面代碼:

  g_pTaskbar->SetThumbnailTooltip (g_hWnd,TEXT ("Some information" ));

  資訊提示顯示在預覽窗之上:

  

  到目前為止,這個程式的縮圖和Aero Peek預覽仍然是由系統來控制。對於大多數程式來說,系統自動產生的縮圖和預覽幾經足夠。但有時候我們想要自己控制,比如foobar2000需要在縮圖中顯示唱片封面(見第一張圖)。

  要實現手動控制,需要在視窗建立以後設定一下視窗的屬性,告訴Windows該視窗的縮圖和預覽圖需要由程式提供。在視窗建立之後加入下面代碼:

  //設定視窗屬性,開啟自訂縮 略圖和AeroPeek預覽

  BOOLtruth=TRUE ;

  DwmSetWindowAttribute (g_hWnd,DWMWA_HAS_ICONIC_BITMAP,&truth,sizeof (truth ));

  DwmSetWindowAttribute (g_hWnd,DWMWA_FORCE_ICONIC_REPRESENTATION,&truth,sizeof (truth ));

  通過這樣的設定以後,在WndProc中就可以收到WM_DWMSENDICONICTHUMBNAIL 和WM_DWMSENDICONICLIVEPREVIEWBITMAP 這兩個訊息,它們分別是Windows向程式請求Thumbnail和Aero Peek Preview時發送的訊息。對於第一個訊息,參數wParam的高位和低位分別表示所請求的縮圖的最大寬度和高度。在訊息處理中,通過DwmSetIconicThumbnail 將一個位元影像控制代碼(HBITMAP)發送給系統。這裡系統要求的位元影像大小不能超過參數傳入的最大寬度和高度,而且必須是32位的位元影像,但是一般的軟體只能儲存24位的位元影像,這樣的位元影像不符合要求,是無法顯示的。於是我利用GDI+寫了兩個輔助函數,用於將PNG格式的資源載入為HBITMAP,以及縮放大小。

  第一個函數是載入PNG格式的資源,參數分別是:模組控制代碼(如果是本模組可使用GetModuleHandle(NULL)取得控制代碼),資源id,資源類型。這段代碼所做的就是將資源載入到記憶體中,並由這塊記憶體建立一個Bitmap對象。

  //將PNG等格式的資源載入為 GdiPlus::Bitmap

  Gdiplus::Bitmap*LoadBitmapFromResource ( HMODULEhModule,UINTresID,LPCTSTRresType )

  {

  //分配一塊記憶體,把 載入的資源拷貝到記憶體中

  //取得資源的控制代碼

  HRSRChRsrc=http://blog.soso.com/qz.q/FindResource (hModule,MAKEINTRESOURCE (resID),resType );

  if (hRsrc=http://blog.soso.com/qz.q/=NULL )

  {

  returnNULL ;

  }

  //資源的大小

  intsize=SizeofResource (GetModuleHandle (NULL),hRsrc );

  //載入資源

  HGLOBALhGlobal=LoadResource (GetModuleHandle (NULL),hRsrc );

  if (hGlobal==NULL )

  {

  returnNULL ;

  }

  //分配相同大小的一塊記憶體

  HGLOBALhGlobal2=GlobalAlloc (GMEM_MOVEABLE,size );

  if (hGlobal2==NULL )

  {

  FreeResource (hGlobal );

  returnNULL ;

  }

  //鎖資源

  LPVOIDlpVoid=LockResource (hGlobal );

  if (lpVoid==NULL )

  {

  FreeResource (hGlobal );

  GlobalFree (hGlobal2 );

  returnNULL ;

  }

  //鎖記憶體

  LPVOIDlpVoid2=GlobalLock (hGlobal2 );

  if (lpVoid2==NULL )

  {

  UnlockResource (hGlobal );

  FreeResource (hGlobal );

  GlobalFree (hGlobal2 );

  returnNULL ;

  }

  //將資源拷貝到記憶體

  memcpy (lpVoid2,lpVoid,size );

  //解鎖

  GlobalUnlock (hGlobal2 );

  UnlockResource (hGlobal );

  //建立流

  LPSTREAMlpStream ;

  HRESULThr=CreateStreamOnHGlobal (hGlobal2,TRUE,&lpStream );

  if (hr!=S_OK )

  {

  FreeResource (hGlobal );

  GlobalFree (hGlobal2 );

  returnNULL ;

  }

  //基於流建立GdiPlus::Bitmap

  Gdiplus::Bitmap*bmp=Gdiplus::Bitmap::FromStream (lpStream );

//釋放資源和記憶體

  FreeResource (hGlobal );

  GlobalFree (hGlobal2 );

  returnbmp ;

  }

  第二個函數是縮放Bitmap對象,最後一個參數可以選擇是否要保持長寬比

  //縮放 GdiPlua::Bitmap

  Gdiplus::Bitmap*ResizeBitmap ( Gdiplus::Bitmap*bmpSrc, intdestWidth,intdestHeight,boolkeepAspect=true )

  {

  //取得源圖片寬度和高度

  intsrcWidth=bmpSrc->GetWidth ();

  intsrcHeight=bmpSrc->GetHeight ();

  //計算橫向和縱向的縮放比率

  floatscaleH= (float )destWidth/srcWidth ;

  floatscaleV= (float )destHeight/srcHeight ;

  //如果需要保持長寬比,則橫向和縱向統一採用較小的縮放比率

  if (keepAspect )

  {

  if (scaleH>scaleV )

  {

  scaleH=scaleV ;

  }

  else

  {

  scaleV=scaleH ;

  }

  }

  //計算目標寬高

  destWidth = (int )(srcWidth*scaleH );

  destHeight= (int )(srcHeight*scaleV );

  //建立目標Bitmap

  Gdiplus::Bitmap*bmpDest=newGdiplus::Bitmap (destWidth,destHeight,PixelFormat32bppARGB );

  Gdiplus::Graphicsgraphic (bmpDest );

  //設定插值演算法

  graphic.SetInterpolationMode (Gdiplus::InterpolationModeHighQualityBicubic );

  //將源映像繪製到目 標映像上

  graphic.DrawImage (bmpSrc,Gdiplus::Rect (0,0,destWidth,destHeight),

  0,0,srcWidth,srcHeight,Gdiplus::UnitPixel );

  graphic.Flush ();

  returnbmpDest ;

  }

  在調用這兩個函數之前,還有一點工作要做。那就是要初始化GDI+。在WinMain函數的開頭,加入下面的初始化代碼:

  //初始化GDI+

  ULONG_PTRtoken ;

  Gdiplus::GdiplusStartupInputinput ;

  Gdiplus::GdiplusStartup (&token,&input,NULL );

  在WinMain函數結束的時候別忘了關閉GDI+:

  //關閉GDI+

  Gdiplus::GdiplusShutdown (token );

  準備工作做好以後,可以正式來處理縮圖了。訊息處理函數中,加入下面的訊息處理代碼:

  caseWM_DWMSENDICONICTHUMBNAIL:

  OnSendThumbnail (hWnd,HIWORD (lParam),LOWORD (lParam ));

  break ;

  在這裡我們得到了縮圖需要的寬和高,轉到OnSendThumbnail函數中處理。

  voidOnSendThumbnail ( HWNDhWnd,intwidth,intheight )

  {

  //將資源載入為Bitmap

  Gdiplus::Bitmap*bmp=LoadBitmapFromResource (GetModuleHandle (NULL),IDB_THUMB,TEXT ("PNG" ));

  //縮放Bitmap

  Gdiplus::Bitmap*bmpResized=ResizeBitmap (bmp,width,height,false );

  //取得位元影像控制代碼

  HBITMAPhbmp ;

  bmpResized->GetHBITMAP (UINT (Gdiplus::Color::AlphaMask),&hbmp );

  //發送位元影像控制代碼

  DwmSetIconicThumbnail (hWnd,hbmp,0 );

  //釋放對象

  DeleteObject (hbmp );

  deletebmpResized ;

  deletebmp ;

  }

  因為沒有保持長寬比,所以得到下面的效果:

  

  如果保持長寬比,將得到下面的效果:

  

  接下來處理Aero Peek預覽圖,對應的Windows訊息是WM_DWMSENDICONICLIVEPREVIEWBITMAP , 但是在該訊息的參數中,沒有提供寬和高。而如果我們提供的預覽圖的寬和高超過了視窗的客戶區大小,同樣是不能顯示的。所以我們要手動擷取客戶區的大小,然後將映像縮放到客戶區的尺寸。加入如下訊息處理:

  caseWM_DWMSENDICONICLIVEPREVIEWBITMAP:

  OnSendPreview (hWnd );

  break ;

  voidOnSendPreview ( HWNDhWnd )

  {

  //取得視窗用戶端區域

  RECTrcClient ;

  GetClientRect (hWnd,&rcClient );

  //將資源載入為 Bitmap

  Gdiplus::Bitmap*bmp=LoadBitmapFromResource (GetModuleHandle (NULL),IDB_PREVIEW,TEXT ("PNG" ));

  Gdiplus::Bitmap*bmpResized=ResizeBitmap (bmp,rcClient.right-rcClient.left,

  rcClient.bottom-rcClient.top,false );

  //取得位元影像控制代碼

  HBITMAPhbmp ;

  bmpResized->GetHBITMAP (UINT (Gdiplus::Color::AlphaMask),&hbmp );

  //發送位元影像控制代碼

  DwmSetIconicLivePreviewBitmap (hWnd,hbmp,NULL,0 );

  //釋放對象

  DeleteObject (hbmp );
deletebmpResized ;

  deletebmp ;

  }

  得到下面的效果 ,這張變形金剛的圖片是我們發送給Windows的預覽圖,實際程式的介面上並沒有這張圖 。

  

  有一點要提醒一下,Windows會將縮圖緩衝,並不會每次都向程式請求縮圖,所以如果要更新縮圖的話需要調用API函數DwmInvalidateIconicBitmaps ,這樣當Windows再次需要顯示縮圖時,就會發送WM_DWMSENDICONICTHUMBNAIL訊息,嚮應用程式請求縮圖。

  至此,上面提到的所有功能都已實現。有幾個標頭檔需要包含到程式中:

  #include <shobjidl.h>

  #include <dwmapi.h>

  #include <tchar.h>

  #include <strsafe.h>

  #include <GdiPlus.h>

  編譯時間需要連結的庫有:dwmapi.lib gdiplus.lib

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.