程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 活動桌面處理和一個例子

活動桌面處理和一個例子

編輯:關於VC++

問題:

在應用程序中如何激活活動桌面(Active Desktop)?一般情況下用戶可以在桌面單擊右鍵,選擇“活動桌面”=〉“按Web頁查看”來打開/關閉活動桌面特性。有沒有什麼函數可以程序中調用來實現對活動桌面的操作?另外,如何斷定用戶激活或取消活動桌面?

解答:

在回答這個問題之前,讓我給你一個重要警告。那就是如果你打算開關活動桌面特性,請保證經過了用戶的許可!最好使用大字體清晰地顯示:“你真的想要激活活動桌面嗎?”要是沒有這樣的提示,對用戶不免有些粗魯。有些用戶並不想要什麼程序來決定是否啟動活動桌面。如果用戶真要是喜歡Web特性而不想失去活動桌面。那他們也會容忍由此而帶來的性能下降。

好吧,這麼多嚴厲的警告。現在假設你有充足的理由打開或關閉活動桌面。也許你在寫一個新的外殼。為了激活或取消活動桌面,你需要使用IActiveDesktop,這是個活動桌面的COM接口。下面列出的是這個接口的方法列表:

//IActiveDesktop 接口方法表

方法

功能和用途

AddDesktopItem 添加一個桌面項。 AddDesktopItemWithUI 使用某個用戶界面添加一個桌面項到活動桌面。 AddUrl 添加與指定的URL關聯的桌面項。 ApplyChange 執行對活動桌面的修改。要使修改生效必須調用這個函數。用於激活或取消活動桌面。 GenerateDesktopItemHtml 產生包含給定桌面項的通用HTML頁面。 GetDesktopItem 獲得指定的桌面項。 GetDesktopItemByID 獲得與給定ID匹配的桌面項。 GetDesktopItemBySource 用源URL獲得某個桌面項。 GetDesktopItemCount 或的桌面項計數。 GetDesktopItemOptions 檢查活動桌面是否打開或關閉。SHGetSettings 性能更佳。用於激活或取消活動桌面。 GetPattern 獲取當前使用的式樣。 GetWallpaper 獲取當前使用的牆紙。僅用於活動桌面。標准模式時(桌面關閉),使用SystemParametersInfo。 GetWallpaperOptions 獲得牆紙選項。僅用於活動桌面。標准模式時(桌面關閉),使用SystemParametersInfo。 ModifyDesktopItem 修改桌面項。 RemoveDesktopItem 從桌面刪除指定的桌面項。 SetDesktopItemOptions 打開或關閉活動桌面。 SetPattern 設置活動桌面式樣。 SetWallpaper 設置活動桌面牆紙。僅用於活動桌面。標准模式時(桌面關閉),使用SystemParametersInfo。 SetWallpaperOptions 設置牆紙選項。僅用於活動桌面。標准模式時(桌面關閉),使用SystemParametersInfo。

//用IActiveDesktop可以添加和刪除桌面項(HTML頁面,圖像,URLs或者ActiveX 控件),設置和獲取牆紙(僅用於活動桌面,在標准模式時要用SystemParametersInfo函數)及其它有用功能。你可以用來打開或關閉活動桌面的函數是SetDesktopItemOptions。但首先要考慮——如何獲得IActiveDesktop接口?用通常使用COM的方法創建一個實例:

//IActiveDesktop* pAD;
HRESULT hr = ::CoCreateInstance(
   CLSID_ActiveDesktop,
   NULL, // 不支持聚合,也就是說沒有外部Unknown
   CLSCTX_INPROC_SERVER,
   IID_IActiveDesktop,
   (void**)&pAD);

//不要忘了在啟動代碼中調用CoInitialize,如MFC應用的InitInstance函數。一旦你有了ActiveDesktop指針,便可以調用它的方法。

// 激活活動桌面

COMPONENTSOPT opt;
opt.dwSize = sizeof(opt);
opt.fActiveDesktop =
   opt.fEnableComponents = TRUE;
HRESULT hr = pAD->SetDesktopItemOptions(&opt,0);

//現在活動桌面應該被激活,但真是這樣嗎?當你第一次運行時,什麼事情也沒發生。怎麼回事呢?經過檢查,我發現之所以設置沒有起作用是因為有個小細節在文檔中沒有說明——將設置應用到活動桌面:

//pAD->ApplyChanges(AD_APPLY_REFRESH);

//用完接口之後不要忘了釋放(Release)它!(當然,你不應該使用原始的接口指針,應該用ATL智能指針——希望你正在使用它們)為了檢查活動桌面是否打開或關閉,有一個對應的Get函數——GetDesktopItemOptions,它使用相同的COMPONENTSOPT結構。還有一個外殼函數做同樣的事情:

// 活動桌面打開或關閉了嗎?

SHELLFLAGSTATE shfs;

SHGetSettings(&shfs,SSF_DESKTOPHTML);

BOOL bADEnabled = shfs.fDesktopHTML;

//不需要COM,CoCreateInstance,IActiveDesktop,或任何有關COM接口的東西。只要調用這個函數。你可以用SHGetSettings來檢查一系列的外殼設置,下面列出了有關SHGetSettings使用的詳細信息。這些設置或多或少與Windows 9x的資源管理器(參見圖四)中“查看”=〉“文件夾選項”=〉“查看”標簽中的選項對應。(Windows 2000有所不同,它是在“工具”=〉“文件夾選項”=〉“查看”標簽中)可惜沒用對應的SHSetSettings函數。

//// 用SHGetSettings獲得信息

// SHGetSettings 獲得當前外殼設置

VOID SHGetSettings(
   LPSHELLFLAGSTATE lpsfs, // 下列結構的地址
   DWORD dwMask // 獲取哪個信息(參見下面內容)
);

// SHGetSettings 填充下面的位域結構. 這些標志與Explorer的“查看”=〉“文件夾選項”=〉“查看”標簽中的選項對應

typedef struct {
   BOOL fShowAllObjects : 1; // 顯示所有文件 (隱藏的或系統的)
   BOOL fShowExtensions : 1; // 顯示文件擴展名 (如 .txt)
   BOOL fNoConfirmRecycle : 1; // 刪除時不確認
   BOOL fShowSysFiles : 1; // 顯示文件的系統屬性
   BOOL fShowCompColor : 1;
   BOOL fDoubleClickInWebView : 1; // 顧名思義
   BOOL fDesktopHTML : 1; // 已打開活動桌面
   BOOL fWin95Classic : 1; // 已打開Windows 95 "傳統"視圖
   BOOL fDontPrettyPath : 1;
   BOOL fShowAttribCol : 1;
   BOOL fMapNetDrvBtn : 1; // 顯示網絡驅動器按鈕
   BOOL fShowInfoTip : 1; // 顯示彈出式描述
   BOOL fHideIcons : 1; //在活動桌面模式中隱藏圖標
   UINT fRestFlags : 3;
} SHELLFLAGSTATE;

// 這些標志被用於獲取上面的這些域;如調用時使用:

dwMask =

// (SSF_DESKTOPHTML | SSF_WIN95CLASSIC) 來獲取 fDesktopHTML 和

// fWin95Classic。

#define SSF_SHOWALLOBJECTS      0x00000001
#define SSF_SHOWEXTENSIONS      0x00000002
#define SSF_SHOWCOMPCOLOR      0x00000008
#define SSF_SHOWSYSFILES       0x00000020
#define SSF_DOUBLECLICKINWEBVIEW  0x00000080
#define SSF_SHOWATTRIBCOL      0x00000100
#define SSF_DESKTOPHTML       0x00000200
#define SSF_WIN95CLASSIC       0x00000400
#define SSF_DONTPRETTYPATH     0x00000800
#define SSF_SHOWINFOTIP       0x00002000
#define SSF_MAPNETDRVBUTTON    0x00001000
#define SSF_NOCONFIRMRECYCLE   0x00008000
#define SSF_HIDEICONS        0x00004000

圖四 “查看”/“工具” 菜單 =〉“文件夾選項”=〉“查看”

現在我們知道由兩種方法來檢查是否活動桌面是否激活——SHGetSettings 和 IActiveDesktop::GetDesktopItemOptions,哪個方法好呢?這很重要嗎?為了回答這個問題,讓我們來探討問題中的第二部分:如何斷定用戶激活或取消活動桌面,不論是從桌面菜單或者是從屬性對話框(如圖五)?

圖五 選擇活動桌面

當用戶打開或關閉活動桌面時,Windows廣播WM_SETTINGCHANGE消息給所有最上層窗口,消息值分別為:wParam = 0 和 lParam = "ShellState"。所以為了捕獲這個事件,必須處理WM_SETTINGCHANGE消息。

// 最上層框架窗口!

void CMainFrame::OnSettingChange(UINT
   uFlags, LPCTSTR pszSection)
{
   if (lpszSection &&
     _tcscmp(pszSection,_T("ShellState"))==0) {
     // do what you want
   }
   CFrameWnd::OnSettingChange(uFlags,
     pszSection);
}

//WM_SETTINGCHANGE是個Windows的常用消息,當程序修改了SystemParametersInfo設置,則Windows就會廣播此消息。但WM_SETTINGCHANGE也比較多地用在其它情形。

一般情況下,wParam/uFlags時0,lParam/pszSection是WIN.INI段名或被修改部分的注冊表鍵(只是最終的鍵,而不是整個串)。事實上,WM_SETTINGCHANGE常被叫做WM_WININICHANGE,這兩個符號在#define中的值也一樣!當IActiveDesktop廣播設置修改時,它將“ShellState”作為段名來傳遞,因為活動桌面設置被存儲在一個注冊表鍵中:

\HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\ShellState

另外,如果你要廣播自己修改的全程設置,也可以使用WM_SETTINGCHANGE。廣播是應該使用SendMessageTimeout(HWND_BROADCAST, ...)函數。

圖六 TestAD

為了整合所講的內容,我編寫了一個小程序:TestAD(如圖六)。當TestAD獲得 WM_SETTINGCHANGE時,便顯示一條消息。利用我創建的一個類(CActiveDesktop)來獲得並設置活動桌面的狀態。為了使用這個類,你只要編寫如下代碼:

//CActiveDesktop ad;

if (!ad.IsEnabled())

ad.Enable(TRUE);

//CActiveDesktop隱藏了所有與COM有關的瑣事。它使用ATL智能指針來保證接口處理的正確性和整體處理的自動化。如果你現在不使用CComQIPtr,那麼趕快學會使用它,對於它的正確使用能使你獲得健壯的,無錯的COM代碼,它非常有用。CActiveDesktop並沒用封裝所有的IActiveDesktop特性,只是封裝了我編寫TestAD所需要的功能。如果我什麼時候想要編寫一個Windows外殼時(我當然不會),再添加缺少的方法。但決定權在於你自己。CActiveDesktop非常簡單,所以有關細節就請你參考源代碼吧。

在實現CActiveDesktop和TestAD時,我遇到了一些意想不到的事情。首先是我在前面已經提到的在修改設置後要將它“應用”到(ApplyChanges)活動桌面的問題。其次是我發現了IActiveDesktop的同步bug問題。當我開始實現TestAD時,IActiveDesktop好像老是報告的錯誤狀態。也就是說活動桌面真正打開的時候,它報告的是關閉,反之亦然。我以為是我的代碼有問題,但當我細究後發現IActiveDesktop::GetDesktopItemOptions事實上在報告錯誤的狀態信息!請看下面的分析:

//TestAD 調用 CActiveDesktop::Enable(TRUE).

//CActiveDesktop 調用 IActiveDesktop::SetDesktopItemOptions, 然後將修改應用到活動桌面(ApplyChanges)。

//ApplyChanges 向最上層窗口廣播WM_SETTINGCHANGE 消息。

//CMainFrame獲得WM_SETTINGCHANGE,並調用IActiveDesktop::GetDesktopItemOptions來獲得開/關狀態——但IActiveDesktop報告的狀態仍然是關閉!

//顯然IActiveDesktop在廣播完成之前沒有更新其內部的狀態。即GetDesktopItemOptions報告的是舊的開/關狀態。碰到這種情況怎麼辦呢?我試圖自己通過消息處理來修正這個問題,也就是在主窗口處理完WM_SETTINGCHANGE消息後添加“活動桌面開/關消息”。結果在TestAD程序中開/關活動桌面倒是沒什麼問題了,但當我用桌面上下文菜單的時候,又發生同樣的問題。不用懷疑,肯定是當TestAD處理添加的消息時,Windows仍然在向下一個最上層窗口廣播WM_SETTINGCHANGE消息。

怎麼辦?難道在顯示狀態信息前等待半秒鐘?真臭。這時如果用SHGetSettings就沒問題啦。實踐證明,SHGetSettings報告的是正確的活動桌面開/關狀態,即便GetDesktopItemOptions報告的是相反的狀態——真讓人高興!很顯然,ApplyChanges更新注冊表是在廣播WM_SETTINGCHANGE消息之前及在更新其內部狀態之前——這是一件讓人哭笑不得的事情。

現在我們應該可以明確回答前面提出的問題了:用哪個方法來獲得活動桌面得開/關狀態

好呢?好像SHGetSettings最接近正確答案。

本文配套源碼

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