荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: DuMiYue (TH@Figo), 信区: Visual
标  题: 活动桌面处理和一个例子
发信站: 荔园晨风BBS站 (Tue Mar 19 15:00:08 2002), 转信

活动桌面处理和一个例子
赵湘宁
下载本文例子代码
问题:
    在应用程序中如何激活活动桌面(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 设置活动桌面墙纸。仅用于活动桌面。标准模式时(桌面关闭),使用S
ystemParametersInfo。
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来检查一系列的外壳设置,下面列出了有关SHG
etSettings使用的详细信息。这些设置或多或少与Windows 9x的资源管理器(参见图四
)中“查看”=〉“文件夹选项”=〉“查看”标签中的选项对应。(Windows 2000有所
不同,它是在“工具”=〉“文件夹选项”=〉“查看”标签中)可惜没用对应的SHSetS
ettings函数。
//
// 用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 和 IAc
tiveDesktop::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_WIN
INICHANGE,这两个符号在#define中的值也一样!当IActiveDesktop广播设置修改时,
它将“ShellState”作为段名来传递,因为活动桌面设置被存储在一个注册表键中:
\HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\ShellState
另外,如果你要广播自己修改的全程设置,也可以使用WM_SETTINGCHANGE。广播是应该
使用SendMessageTimeout(HWND_BROADCAST, ...)函数。
图六 TestAD
    为了整合所讲的内容,我编写了一个小程序:TestAD(如图六)。当TestAD获得 W
M_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::GetDesktopItemOp
tions事实上在报告错误的状态信息!请看下面的分析:
//
TestAD 调用 CActiveDesktop::Enable(TRUE).
//
CActiveDesktop 调用 IActiveDesktop::SetDesktopItemOptions, 然后将修改应
用到活动桌面(ApplyChanges)。
//
ApplyChanges 向最上层窗口广播WM_SETTINGCHANGE 消息。
//
CMainFrame获得WM_SETTINGCHANGE,并调用IActiveDesktop::GetDesktopItemOptions
来获得开/关状态——但IActiveDesktop报告的状态仍然是关闭!
//
    显然IActiveDesktop在广播完成之前没有更新其内部的状态。即GetDesktopItemOp
tions报告的是旧的开/关状态。碰到这种情况怎么办呢?我试图自己通过消息处理来修
正这个问题,也就是在主窗口处理完WM_SETTINGCHANGE消息后添加“活动桌面开/关消息
”。结果在TestAD程序中开/关活动桌面倒是没什么问题了,但当我用桌面上下文菜单的
时候,又发生同样的问题。不用怀疑,肯定是当TestAD处理添加的消息时,Windows仍然
在向下一个最上层窗口广播WM_SETTINGCHANGE消息。
    怎么办?难道在显示状态信息前等待半秒钟?真臭。这时如果用SHGetSettings就没
问题啦。实践证明,SHGetSettings报告的是正确的活动桌面开/关状态,即便GetDeskt
opItemOptions报告的是相反的状态——真让人高兴!很显然,ApplyChanges更新注册表
是在广播WM_SETTINGCHANGE消息之前及在更新其内部状态之前——这是一件让人哭笑不
得的事情。
     现在我们应该可以明确回答前面提出的问题了:用哪个方法来获得活动桌面得开/
关状态
好呢?好像SHGetSettings最接近正确答案。
最后祝弟兄们编程愉快!

--
他能够不知不觉令到我倾心
    也能够一句说话而留下痛恨
        感情最光辉一刹热暖我一生
            爱人远走了以后仍然是爱人

※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 218.30.30.9]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店