65.9K
CodeProject 正在变化。 阅读更多。
Home

深入了解 Vista 侧边栏

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.70/5 (15投票s)

2008年3月8日

CPOL

10分钟阅读

viewsIcon

101256

深入了解 Vista 侧边栏。了解侧边栏的工作原理以及如何优化您的 Gadgets 开发。

引言

Vista 侧边栏 Gadget 允许您在 Vista 桌面上显示您所需的所有信息:RSS 源、天气、股票价值... 您只需少量 HTML 和 JavaScript 代码即可轻松编写自己的 Gadget。然而,由于缺乏开发环境,编写 Gadget 可能会有些令人沮丧。有时您不知道为什么您的 Gadget 无法按您希望的方式工作。在撰写本文时,我决定探索 Vista 侧边栏的内部工作原理,以便更好地理解一些调试问题。请跟随我进入 Vista 侧边栏的“黑暗面”!

什么是侧边栏?

什么是 Vista 侧边栏?一个简单的定义可能是:“Vista 侧边栏是 Vista 桌面的一个部分,您可以在其中拖放名为 Gadgets 的小型应用程序。”从某种意义上说这是真的,但侧边栏并不是真正“Vista 桌面的一个部分”。让我们试试这个:逐个最小化桌面上所有 Windows 窗口,只留下侧边栏。现在,单击菜单栏中的“显示桌面”图标。会发生什么?侧边栏被最小化了!这太不可思议了,不是吗?在我看来,这是一个 bug:当您使用此图标最小化所有 Windows 窗口时,很可能是因为您只需要桌面,所以您也希望看到侧边栏。现在有一个更奇怪的特性:重新打开您之前最小化的任何窗口:您的窗口和侧边栏都可见了!在我看来,这只是 Microsoft 开发人员为了解决“最小化侧边栏 bug”而找到的一个“hack”。

那么,什么是侧边栏?侧边栏是一个与其他任何窗口应用程序一样。这就是为什么侧边栏也会受到“最小化所有 Windows”图标的影响。此应用程序的 EXE 文件位于 %ProgramFiles%\Windows Sidebar\sidebar.exe。出于隔离目的,此进程始终启动两个实例:第一个实例托管预安装的 Gadgets(时钟、天气、日历、股票等),而第二个实例托管所有用户创建的 Gadgets。您可以使用 Mark Russinovich 的 Process Explorer 查看这两个进程。

Sidebar processes in Process Explorer - Click for full size image

尝试终止子进程:所有下载的 Gadget 都将消失;尝试终止根进程:所有侧边栏都将消失。

托管 Gadget

要编写您自己的 Gadget,您只需编写一个 HTML 页面和一些 JavaScript 源代码。为了托管 Gadget,侧边栏使用 Internet Explorer。更确切地说,侧边栏使用 ActiveX 组件 MSHTML.DLLMSHTML.DLL 控件允许您托管 Internet Explorer 实例,并对浏览器行为和外观的各个方面进行非常精细的控制。侧边栏为每个 Gadget 创建一个 ActiveX 控件实例。您可以使用 Visual Studio Tools Spy++ 检查这一点。在菜单中选择“搜索窗口”选项,然后单击 Gadget 以查看所有属性。您也可以再次使用 Process Explorer:右键单击 sidebar.exe,然后在“线程”选项卡中,您会找到 MSHTML.DLL 的多个实例。

Using Spy++ to track Gadgets hosting - Click for full size image

当您的 Gadget 向外部服务器发出 HTTP 请求时,USER_AGENT 值与 Internet Explorer 7 相同,并且 REFERER 值是当前 Gadget 页面的名称,前缀为 x-gadget:///。由于 Gadget 托管在 Internet Explorer 中,调试 Gadget 的一个常见问题涉及缓存。对于 HTML 中包含的图像或脚本中的 HTTP 请求,请不要忘记 IE 首先使用其缓存。因此,为了避免缓存问题,您需要更改 HTTP Expires 标头或在所有请求后添加时间戳。

侧边栏 API

Sidebar API 是一个 JavaScript API,您可以在 Gadget 中使用它来实现一些特定行为(例如弹出、窗口设置等)或访问一些系统信息(CPU、电源状态、网络设置)。Sidebar API 包含属性、方法和事件,所有这些都可以通过 System 前缀调用。例如,这里是调用 Sidebar API 以打印 CPU 使用率调试信息的示例

System.Debug.outputString(“CPU usage is ” + System.Machine.CPU.usagePercentage);

为了让您访问此 API,Sidebar 进程使用 MSHTML.DLL 控件的一项功能,将应用程序特定的函数公开给 JavaScript。更准确地说,Sidebar API 的所有公开属性、方法和事件都作为由 Sidebar 进程托管的 COM 接口公开。为了说明,我使用 Microsoft 的 OLE/COM Object Viewer 工具来探索 sidebar.exe 内部的类型库。启动 OLEVIEW,单击 File/View TypeLib... 菜单并打开 sidebar.exe。这是您获得的 IDL 的一部分。您可以看到前面示例中使用的两个 API。

// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: sidebar.exe

[
  uuid(AAA49BB1-378C-4206-9CAD-53C3372E9550),
  version(1.0),
  helpstring("Microsoft Sidebar API Type Library")
]
library Sidebar
{
    // TLib :     // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface ISettings;
    interface IGenericCollection;
    dispinterface DispSettings;
    interface IEnvironment;
    dispinterface DispEnvironment;
    interface ISystemMachinePowerStatus;
    interface ISystemMachinePowerStatusBatteryStatus;
    interface ISystemMachineCPU;
    interface ISystemMachine;

    ...

    [
      odl,
      uuid(B68EEF74-E7F5-4CED-B214-B8BE05C79E3F),
      dual,
      nonextensible,
      oleautomation
    ]
    interface ISystemMachineCPU : IDispatch {
        [id(0x000075f9), propget]
        HRESULT name([out, retval] BSTR* pCpuName);
        [id(0x000075fa), propget]
        HRESULT usagePercentage([out, retval] single* pUsagePercentage);
    };

    ...


    [
      odl,
      uuid(746B224A-6E2A-4A43-A5C6-D10717C910B4),
      dual,
      nonextensible,
      oleautomation
    ]
    interface ISystemDebug : IDispatch {
        [id(0x00001068)]
        HRESULT outputString([in] BSTR psz);
    };

    ...
}

下图总结了 Sidebar 的总体架构以及我们将看到下面的 Windows Live Services ActiveX 的使用

Sidebar overall architecture

Sidebar API 中一个常用功能是 System.Gadget.Settings,它包含 readreadStringwritewriteString 等方法,允许您的 Gadget 在用户打开/关闭设置窗口时加载和保存其设置。由于我总是在想这些设置存储在哪里,我使用了 Mark Russinovich 的另一个强大工具 - Process Monitor - 来寻找答案。我在更改 Gadget 设置之前启动了它。这是结果

Process Monitor tool on Sidebar - Click for full size image

事实证明,所有 Gadget 设置都存储在一个名为 %USERPROFILE%\AppData\Local\Micros oft\Windows Sidebar\Settings.ini 的唯一 INI 文件中。每个 Gadget 实例在该文件中都有自己的部分。以下代码显示了 Settings.ini 中天气 Gadget 的部分。

...
[Section 4]
PrivateSetting_GadgetName=
    "C:%5CProgram%20Files%5CWindows%20Sidebar%5CGadgets%5CWeather.Gadget"
PrivateSetting_Enabled="true"
PrivateSetting_SidebarDockedState="Docked"
PrivateSetting_GadgetTopmost="false"
WeatherLocation="Saint-Quentin-en-Yvelines,%20Yvelines"
WeatherLocationCode="wc:FRXX0280"
DisplayDegreesIn="Celsius"
PrivateSetting_GadgetDropLocationX="1051"
PrivateSetting_GadgetDropLocationY="387"
PrivateSetting_GadgetOpacity="100"
...

虽然这不是好的做法,但您可以手动更改此文件中的设置,而不是使用 Sidebar API。

Gadget 源代码

由于 Gadget 只是 HTML 和 JavaScript,每个人都可以看到它们的源代码。每个用户 Gadget 都安装在 %USERPROFILE%\AppData\Local\Microsoft\Windows Sidebar\Gadgets 的子目录中。子目录的名称是 Gadget 的名称,如果您在 Sidebar 中有多个相同 Gadget 的实例,则可选地后跟一个数字。

Vista 标准 Gadget(时钟、天气、日历、股票等)位于另一个目录 %ProgramFiles%\Windows Sidebar\Gadgets 中。这些 Gadget 中的三个——天气、股票和货币——还有另一个特殊性:它们的源代码使用一个名为 WLSRVC(Windows Live Service ActiveX 的缩写)的 ActiveX 控件,该控件嵌入在 Sidebar 目录中。同样,您可以使用 OLE/COM Object Viewer 工具查看其接口,单击 File/View TypeLib... 菜单并打开 wlsrvc.dll。以下是生成的 IDL 的一部分

// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: wlsrvc.dll

[
  uuid(4F47FCF0-E864-4D97-B309-2F5902306128),
  version(1.0),
  helpstring("Windows Live Services 1.0 Type Library")
]
library wlsrvcLib
{
    // TLib :     // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface IWLForecast;
    interface IWLWeather;
    interface IWLServiceData;
    interface IWLWeatherService;
    interface IWLStock;
    interface IWLStockService;
    interface IWLCurrency;
    interface IWLCurrencyData;
    interface IWLCurrencyService;
    interface IWLServices;

    ...
    [
      odl,
      uuid(3E46F4F0-D6FE-49E4-97E5-C731154F4E7A),
      helpstring("IWLServices Interface"),
      dual,
      nonextensible,
      oleautomation
    ]
    interface IWLServices : IDispatch {
        [id(0x00000001), propget]
        HRESULT GetService(
                        [in] BSTR bstrServiceName,
                        [out, retval] IDispatch** ppDisp);
    };
    ...
}

以下是 Vista 天气 Gadget 源代码的一部分。您可以看到 WLSRVC ActiveX 的创建和使用。

...

////////////////////////////////////////////////////////////////////////////////
//
// WeatherGadget() - main Constructor
//
////////////////////////////////////////////////////////////////////////////////
function WeatherGadget()
{
  var self = this;

  ...
  try
  {
    // Connect to Weather Service .dll
    var oMSN = new ActiveXObject("wlsrvc.WLServices");
    this.oMSN = oMSN.GetService("weather");
  }
  catch (objException)
  {
    this.isValid = false;
    this.statusMessage = getLocalizedString('ServiceNotAvailable');
    this.oMSN = new Object();
  }

  ...

我不确定微软选择这种架构是为了性能原因还是为了避免暴露处理 Windows Live 服务的源代码。

最后,%ProgramFiles%\Windows Sidebar\Shared Gadgets 目录存放由系统管理员为企业部署 Gadget 而安装的 Gadget。因此,可以限制用户只能安装企业 Gadget 或禁止用户安装新的 Gadget。

本地化文件

Gadget 最酷的功能之一是它们对本地化的支持。对于 Gadget 中打包的每个文件(*.html*.jpg*.js 等),您可以为需要支持的每种语言提供相应的文件。正确文件的选择由 Sidebar 透明地完成 — 您只需将文件放入与您的本地或区域代码对应的子目录中(与 .NET 程序集类似)。

我们可以再次使用 Process Monitor 看到此功能在运行

Process Monitor tool on Sidebar - Click for full size image

这里,我的 Gadget 调用了一个 HelloWorld.html 页面。尽管此页面位于根文件夹中,但 Sidebar 进程首先在区域文件夹(法国为“fr-FR”)中查找此文件,然后在本地文件夹(法语为“fr”)中查找。由于这两个调用都导致“路径未找到”错误,因此进程随后在根文件夹中获取文件。

URL 翻译是 MSHTML ActiveX 组件的一个功能。使用接口 IDocHostUIHandler::TranslateUrl,您可以在导航开始之前拦截托管 HTML 页面中调用的所有 URL。尽管没有证据表明这一点,但这很可能是 Sidebar 用来捕获 Gadget 中所有调用的 URL 的功能。每个相对 URL 都会在可能的情况下被翻译成本地化的等效项。请查阅 MSDN Library 以了解有关 IDocHostUIHandler 接口和 TranslateUrl 功能的更多信息。

安全

Internet Explorer 允许您根据访问的网站配置安全设置。Gadget 始终在本地计算机区域中运行,就像它们是本地 HTML 应用程序一样。Gadget 允许创建或嵌入 ActiveX 控件,但这些控件应存在于计算机上。另请注意,嵌入的 ActiveX 控件受到 Vista 用户访问控制 (UAC) 的限制。此外,Sidebar 绝不会显示 UAC 提升提示。

与传统的网页不同,Gadget 可以不受限制地访问来自多个域和多个服务器的数据。一个有趣的特性是,当 Gadget 发出 HTTP 请求时,它可以依赖 NT 身份验证,因此您可以调用需要检查用户是否属于 NT 组的企业 Web 服务。

调试 Gadget

要调试 Gadget,您需要使用 Visual Studio 并将其附加到 sidebar.exe(使用“调试/附加到进程...”菜单项)。此附加允许您捕获 JavaScript 错误并在源代码中设置断点。不幸的是,根据我自 Vista RC1 以来作为 Gadget 开发人员的经验,调试 Gadget 的效果是随机的。附加到 Visual Studio 也允许您使用“旧式”调试:“打印”!同样,您可以使用 System.Debug.outputString 在 Visual Studio 的输出窗口中显示调试信息。

Gadget 开发环境

从事 Gadget 开发数月,我有一个梦想:微软能为我们提供一个出色的 Gadget 开发环境。我希望在这个 IDE 中,您可以轻松地在 Visual Studio 中托管您的 Gadget,挂钩 API 调用,并模拟设置、浮出和未停靠视图等功能。有了这个 IDE,我希望您始终可以调试代码、捕获错误并动态添加 JavaScript 代码。当然,我希望您也可以使用这个 IDE 调试非 Gadget AJAX 应用程序。

这个梦想没有在 Visual Studio 2008 中实现,但 Visual Studio 2008 正在朝着正确的方向前进。Visual Studio 2008 最有用的功能之一是 JavaScript Intellisense,一种自动自动完成技术,通过减少您必须编写的 JavaScript 代码量来加速开发。请参阅下图,了解 Intellisense 在 Visual Studio 2008 中的工作情况。

Intellisense in Visual Studio 2008 - Click for full size image

由于 JavaScript 编程可能很困难,JavaScript IntelliSense 在编写标准 JavaScript 代码和 ASP.NET AJAX 客户端代码时最有帮助。JavaScript 没有显式类型声明,因为它是一种动态语言。另一方面,Visual Studio 使用“类型推断”来检测任何变量的当前类型并在您当前上下文中给出正确的建议。由于开发(例如)Vista 侧边栏 Gadget 会产生大量 JavaScript 代码,因此 IntelliSense 是一个非常有用的功能。此外,Visual Studio 2008 允许您使用 C# 注释模板 (///) 提供 JavaScript 代码的帮助信息。因此,IntelliSense 的工具提示可以显示函数和参数的描述。

一个拥有 Intellisense 的好地方是 SideBar API。不幸的是,由于 SideBar API 是一个 COM 接口,Visual Studio 类型推断无法检测到它。为了避免这个缺点,我编写了一个与 SideBar API 接口匹配的虚拟 JavaScript 文件。我只在设计时包含它,以便在 SideBar API 上获得 Intellisense。下面的清单显示了我在第一个示例中使用的函数。

///////////////////////////////////////////////////////////////////////////
// Dummy SideBar API
// Match System.* interface to provide help information on Intellisense
///////////////////////////////////////////////////////////////////////////

var System = new SystemClass();

function outputString(strOutputString) {
/// <summary>Writes a string to the console.</summary>
/// <param name="strOutputString">Required. String to write to the console.</param>
    alert(msg);
}

function DebugClass() {
/// <summary>Defines the method available for debugging Windows Sidebar gadget script
    </summary>
    this.outputString = outputString;
}

function MachineClass() {
/// <summary>Gets the percentage of a microprocessor's capacity being used.</summary>
    this.usagePercentage = 100;
}

function SystemClass() {
    this.Debug = new DebugClass();
    this.Machine = new MachineClass();
}

开发 Gadget 的一个好习惯是首先在一个 HTML 页面中开发所有源代码,然后在 Internet Explorer 中运行您的页面,这样您就可以轻松地在 Visual Studio IDE 中调试 sidebar.exe 之外的代码。当您的所有功能在 HTML 中正常工作时,您将它们与图形和 Gadget 特定代码混合到一个打包的 Gadget 中。另一种选择是使用仅限 JavaScript 的 SideBar 框架,就像之前展示的那样。通过使用这种技巧,您可以想象编写类似 SideBar 模拟器的东西,以便在 Visual Studio 中完全测试您的 Gadget。我希望你们中的一些人会尝试进行这次冒险。

结论

在等待神奇 IDE 的出现之前,Gadget 开发是一项复杂的工作。Visual Studio 2008 为我们提供了一些不错的功能,但缺少 Gadget 模板项目和模拟器模式。我希望我在这里展示的内部技巧和提示能帮助您提高生产力,并更好地了解 Vista SideBar 的工作方式。

致谢

感谢 Smaïl Aïssaoui 对本文的仔细阅读。

历史

  • 2008 年 3 月 8 日:文章发布

© . All rights reserved.