每周源代码33-Google浏览器中的微软开放源


[原文发表地址]  The Weekly Source Code 33 - Microsoft Open Source inside Google Chrome

[原文发表时间]  2008-09-11 11:56 AM

“首先,我要提醒大家,请大家看看我正在进行的最新的请求阅读源代码以成为更杰出的开发人员博客。亲爱的读者,我现在向你们展示"每周源代码."一系列帖子中的第33篇。

里面提到过,微软代码怎样处理Google最新的浏览器——Google Chrome?请大家看看Google 代码上的"Chromium"的条款和条件。Chrome的运行涉及到24个不同位制的第三方软件,其中之一就是2004年发布的开放源WTL,即Windows模板库。

Chrome使用开放源Windows模板库

WTL在 MS-PL(即微软公众授权协议)下发布。这是个非常轻松的协议,基本上说,“非常有趣,如果有麻烦就不会调用。”在开放源的王国里,这样的协议铁定能取悦大家。

WTL是 一套开发Win32的C++库,类似于MFC(微软基础类),但它较之更轻便。它最初是由Nenad Stefanovic在微软内部发布的一个框架,但却未获得微软的官方支持。直到现在,Nenad也一直位于旧的源代码开发项目的名单之列。

WTL接受ATL(活动模板库),ATL是一系列C++模板类,它使COM的开发变得更简单。它多少都效仿了STL(标准模板库)。 您可能还记得, Chris Sells是开发ATL的核心。Chris和Chris Tavares一致力于ATL Internals 这本书

早在2002年,WTL就深受大家喜爱。下面是Simon Steele在当年就WTL的乐趣写的一篇帖子

Simon写道:

WTL是基于MFC的加强版模板库-经过瘦身提速的成功阶段,WTL为我们提供了一种在不采取庞大的MFC或复杂的纯粹Win32API 编程的情况下编写合适GUI应用程序的用户界面框架。许多MFC中的“must-have”类也可作为WTL的实用工具类。热烈欢迎你的老朋友CPoint, CSize, Crect以及最终要的CString归队!WTL创造了无需MFC淤泥新内阁库的可执行小文本-事实上,只要你不使用Visual C++运行库函数(strcpy和朋友),你也可以省去msvcrt.dll-这样就能得到运行速度更快的小程序。

4年前,Windows模板库作为开放源码发布,正如28年的互联网一样。2004年的5月似乎特别漫长。我以为微软不会花很多精力开发开放源的,但看起来他们真的这样做了。实际上,2003年4月,Pranish Kumar和Visual C++开发团队都相继在WTL Yahoo Group上发表讲话,都声称要将WTL永久地放到社区上。

历史:WTL是怎样变成开放源的?

我今天和Pranish Kumar在即时聊天软件上谈到了WTL是怎样成为2004微软开放源项目的。建议你可以去查看MSFT上的共享源页面开放源页面, 和最重要的端口25.

下面是我和Pranish 关于WTL发布的聊天内容:

WTL就算不是微软的第一个OSS开源软件,最起码也是其中之一。和Boss们开会的时候也展示了3款有潜力的OSS软件。我估计WTL是首个参与到联合MS/社区的“真正意义上的OSS”,跟以往仅仅在外部发布信息相反。WTL是唯一通过的项目。

我:启动“共享源代码计划吗?

是的,从更广泛的意义上说,我们把Win CE的协议/进程的基础和一些其他的微软可提供(以某种形式)的组件作为共享源码。他们还把目光投向了BSD和其他的协议。

对于各方面的原因来说,这都是一次非常不错的体验。其中之一是看到微软高层对整个开放源码/共享源码的回应。OSS = Linux的问题和是否我们从事的项目有商业价值也广受关注。

我们立场/态度的改变是非常惊人的。WTL之所以通过的原因是我们说服了管理层。他们有着热情洋溢的社区基础,能真正帮助我们打好基础。

我时不时地在社区上检查(不定期),它的强大性总让我印象深刻。

我之所以愿意为ScottGu工作的一个原因是微软对于发布源码的态度越来越好。微软是个大公司,有时候发展是缓慢了点,但跟以前相比,更多的人都“得到”了机会。

发掘

Chrome使用抽象库来提取非Windows平台上的GUI。但现在,ChromeView的下面是WTL。也也也有doa 也有道理。为什么不通过本地库获得本地速度呢? 他们使用WTL8.0构建7161

Chromium有一大堆的代码。如果你想尝试自己用VS2005编译,源程序打包文件会超过400兆。让我们看看几个有趣的微位。你也可以查看它们的"Build Bot" 。观察Linux 和Mac Versions每天的进程。

在某些地方,Chrome使用WTL的小程序如宏指令。例如,在Chrome的AeroTooltipManager中,GET_X_LPARAM就是一个宏指令:

   1: ...snip... 
   2:  if (u_msg == WM_MOUSEMOVE || u_msg == WM_NCMOUSEMOVE) {
   3:  int x = GET_X_LPARAM(l_param);
   4:  int y = GET_Y_LPARAM(l_param);
   5:  if (last_mouse_x_ != x || last_mouse_y_ != y) {
   6:    last_mouse_x_ = x;
   7:    last_mouse_y_ = y;
   8:    HideKeyboardTooltip();
   9:    UpdateTooltip(x, y);
  10:  }
  11: ...snip...

在其他地方,它们的依赖性更强,如在包括atlcrack.h 的text_field.cc中。请不要忘记,These are not drugs, mind you, but rather "message crackers" to help get at, and react to, the information inside Window Messages.这些不是药品,而是“消息破解程序”来帮助获得,应对窗口消息内部的信息。这些都用于帮助创建所有你感兴趣的 “消息映射”。这些宏指令都扩展到了一大堆代码中,极其方便。

   1: // CWindowImpl
   2: BEGIN_MSG_MAP(Edit)
   3:   MSG_WM_CHAR(OnChar)
   4:   MSG_WM_CONTEXTMENU(OnContextMenu)
   5:   MSG_WM_COPY(OnCopy)
   6:   MSG_WM_CUT(OnCut)
   7:   MESSAGE_HANDLER_EX(WM_IME_COMPOSITION, OnImeComposition)
   8:   MSG_WM_KEYDOWN(OnKeyDown)
   9:   MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk)
  10:   MSG_WM_LBUTTONDOWN(OnLButtonDown)
  11:   MSG_WM_LBUTTONUP(OnLButtonUp)
  12:   MSG_WM_MBUTTONDOWN(OnNonLButtonDown)
  13:   MSG_WM_MOUSEMOVE(OnMouseMove)
  14:   MSG_WM_MOUSELEAVE(OnMouseLeave)
  15:   MSG_WM_NCCALCSIZE(OnNCCalcSize)
  16:   MSG_WM_NCPAINT(OnNCPaint)
  17:   MSG_WM_RBUTTONDOWN(OnNonLButtonDown)
  18:   MSG_WM_PASTE(OnPaste)
  19:   MSG_WM_SYSCHAR(OnSysChar)  // WM_SYSxxx == WM_xxx with ALT down
  20:   MSG_WM_SYSKEYDOWN(OnKeyDown)
  21: END_MSG_MAP()

他们还利用一些围绕在窗口结构的C++类。例如,Windows POINT结构是WTL中的一个被称作CPoint的类。该类其实是从结构中派生而来。这儿有很多有趣的东西,WTL帮助和保持整洁却处于比较低的水平。

现在,让我们进入较有吸引力的地方。因为没有记录,可能需要,也可能不需要完成一些拆解。

Chrome 数据执行保护的基数

这部分没有详细介绍开放源码的使用。但它的确非常有趣。这是Chrome's WinMain()的一部分。虽然很长,你可以查看有趣的位。首先,以if/else为开端。然后看命令行并决定它们(EXE)是否为三者之中的某个,是否为输出程序,或插件(主机)进程,或浏览器进程。请注意查看输出程序和主要浏览器是否开启DEP(数据执行保护)。必须确保ATL7的运行,因为还存在许多通过旧式方法构建的奇怪的插件。它们最终会调用SetProcessDEPPolicy并传递来启用DEP,并启用ATL7编译过程。来自MSDN的帮助:

“禁用DEP - ATL thunk模拟目前的进程,以防止系统拦截源自活动模板库(ATL)的thunk层的NX故障。”

这些新的API被添加在了Vista SP1, Windows XP SP3 和 WIndows 2008中。为什么ATL被特殊设定了呢?下面是Michael Howard的解释:

“旧版本的ATL之所以旧是因为它使用的是pre-Visual C++ 2005,在小型孤立的情况下使用动态生成代码。显然,如果没有合适的API,在启用DEP的电脑上会出现问题的。因为你不能执行这些数据。这些代码就被称为“thunk”和VC++ 2005中的ATL版本。然后,正确地处理DEP。”

在Chrome sandboxed进程中运行的一些插件可以通过这种方式编译,那样进程有一个与其他不同的安全DEP设置。

   1: int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prev_instance,
   2:                       wchar_t* command_line, int show_command) {
   3:   // The exit manager is in charge of calling the dtors of singletons.
   4:   base::AtExitManager exit_manager;
   5:  
   6:   // Note that std::wstring and CommandLine got linked anyway because of
   7:   // breakpad.
   8:   CommandLine parsed_command_line;
   9:   std::wstring process_type = 
  10:    parsed_command_line.GetSwitchValue(switches::kProcessType);
  11:  
  12:    const wchar_t* dll_name = L"chrome.dll";
  13:   if (process_type == switches::kPluginProcess) {
  14:     // Plugin process.
  15:     // For plugins, we enable ATL7 thunking support because we saw old activex
  16:     // built with VC2002 in the wild still being used.
  17:     sandbox::SetCurrentProcessDEP(sandbox::DEP_ENABLED_ATL7_COMPAT);
  18:   } else if (process_type == switches::kRendererProcess) {
  19:     // Renderer process.
  20:     // For the processes we control, we enforce strong DEP support.
  21:     sandbox::SetCurrentProcessDEP(sandbox::DEP_ENABLED);
  22:   } else {
  23:     // Browser process.
  24:     // For the processes we control, we enforce strong DEP support.
  25:     sandbox::SetCurrentProcessDEP(sandbox::DEP_ENABLED);
  26:   }
  27: ...snip...
  28: }

当你试图深入发掘DEP的用处时,请注意这个有趣的评论。因为他们试图让DEP在Windows XP SP2 和Windows Server 2003 SP1上运行。他们从2005年一直使用这篇文章中概述到的完全未获支持的技术来尝试打开DEP。如果你试图在Vista上调用它,则会返回“不支持数据”的指令。当然,还有一个官方的Vista API,这就是 SetProcessDEPPolicy。

作为一方面,有趣的是,对于那些试图在Linux下但在VM外部仿效Windows的人来说,这个没有文档说明的API上周已作为补丁被添加到了WINE(Windows仿真)上

请注意此方法中最有趣的评论:

“//Microsoft完全没有文档说明。你可以通过使用你最喜欢的反汇编程序对Vista's SP1 kernel32.dll进行反汇编来查找这些信息。

enum PROCESS_INFORMATION_CLASS {

ProcessExecuteFlags = 0x22,

}"

Chromium的作者似乎对Windows 核心部件进行了反汇编,以在Windows XP SP2下实现安全性功能。他们很清楚地知道自己做的是益事,而非恶事,因为他们的意图(通过阅读他们的代码)是使他们的浏览器在XP SP2上更安全,并且防止不必要的代码执行。

这款内部和完全不获支持的API位于Microsoft Windows Internals的第四版,第六章,在download.microsoft.com(PDF)上。在 Microsoft Research PowerPoint (PPTX)中也提到过。Windows内核团队的建构师也在帖子上指出,这只限于内部使用:

作为一个免责声明,我想强调,NtSetInformationProcess,class ProcessAccessToken是一个无文档记录和未获支持的界面。它仅保留给系统组件使用,遵从操作系统发布之间的变更。

你可以看到下面Chrome的杰作,或从源网址查看。他们通过使用GetProcAddress四处寻找能满足他们要求的方法。

   1: namespace sandbox {
   2:  
   3: namespace {
   4:  
   5: // These values are in the Windows 2008 SDK but not in the previous ones. Define
   6: // the values here until we're sure everyone updated their SDK.
   7: #ifndef PROCESS_DEP_ENABLE
   8: #define PROCESS_DEP_ENABLE                          0x00000001
   9: #endif
  10: #ifndef PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION
  11: #define PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION     0x00000002
  12: #endif
  13:  
  14: // SetProcessDEPPolicy is declared in the Windows 2008 SDK.
  15: typedef BOOL (WINAPI *FnSetProcessDEPPolicy)(DWORD dwFlags);
  16:  
  17: // Completely undocumented from Microsoft. You can find this information by
  18: // disassembling Vista's SP1 kernel32.dll with your favorite disassembler.
  19: enum PROCESS_INFORMATION_CLASS {
  20:   ProcessExecuteFlags = 0x22,
  21: };
  22:  
  23: // Flags named as per their usage.
  24: const int MEM_EXECUTE_OPTION_ENABLE = 1;
  25: const int MEM_EXECUTE_OPTION_DISABLE = 2;
  26: const int MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION = 4;
  27: const int MEM_EXECUTE_OPTION_PERMANENT = 8;
  28:  
  29: // Not exactly the right signature but that will suffice.
  30: typedef HRESULT (WINAPI *FnNtSetInformationProcess)(
  31:     HANDLE ProcessHandle,
  32:     PROCESS_INFORMATION_CLASS ProcessInformationClass,
  33:     PVOID ProcessInformation,
  34:     ULONG ProcessInformationLength);
  35:  
  36: }  // namespace
  37:  
  38: bool SetCurrentProcessDEP(DepEnforcement enforcement) {
  39: #ifdef _WIN64
  40:   // DEP is always on in x64.
  41:   return enforcement != DEP_DISABLED;
  42: #endif
  43:  
  44:   // Try documented ways first.
  45:   // Only available on Vista SP1 and Windows 2008.
  46:   // http://msdn.microsoft.com/en-us/library/bb736299.aspx
  47:   FnSetProcessDEPPolicy SetProcDEP =
  48:       reinterpret_cast<FnSetProcessDEPPolicy>(
  49:           GetProcAddress(GetModuleHandle(L"kernel32.dll"),
  50:                                          "SetProcessDEPPolicy"));
  51:  
  52:   if (SetProcDEP) {
  53:     ULONG dep_flags;
  54:     switch (enforcement) {
  55:       case DEP_DISABLED:
  56:         dep_flags = 0;
  57:         break;
  58:       case DEP_ENABLED:
  59:         dep_flags = PROCESS_DEP_ENABLE |
  60:                     PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION;
  61:         break;
  62:       case DEP_ENABLED_ATL7_COMPAT:
  63:         dep_flags = PROCESS_DEP_ENABLE;
  64:         break;
  65:       default:
  66:         NOTREACHED();
  67:         return false;
  68:     }
  69:     return 0 != SetProcDEP(dep_flags);
  70:   }
  71:  
  72:   // Go in darker areas.
  73:   // Only available on Windows XP SP2 and Windows Server 2003 SP1.
  74:   // http://www.uninformed.org/?v=2&a=4
  75:   FnNtSetInformationProcess NtSetInformationProc =
  76:       reinterpret_cast<FnNtSetInformationProcess>(
  77:           GetProcAddress(GetModuleHandle(L"ntdll.dll"),
  78:                                          "NtSetInformationProcess"));
  79:  
  80:   if (!NtSetInformationProc)
  81:     return false;
  82:  
  83:   // Flags being used as per SetProcessDEPPolicy on Vista SP1.
  84:   ULONG dep_flags;
  85:   switch (enforcement) {
  86:     case DEP_DISABLED:
  87:       // 2
  88:       dep_flags = MEM_EXECUTE_OPTION_DISABLE;
  89:       break;
  90:     case DEP_ENABLED:
  91:       // 9
  92:       dep_flags = MEM_EXECUTE_OPTION_PERMANENT | MEM_EXECUTE_OPTION_ENABLE;
  93:       break;
  94:     case DEP_ENABLED_ATL7_COMPAT:
  95:       // 0xD
  96:       dep_flags = MEM_EXECUTE_OPTION_PERMANENT | MEM_EXECUTE_OPTION_ENABLE |
  97:                   MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION;
  98:       break;
  99:     default:
 100:       NOTREACHED();
 101:       return false;
 102:   }
 103:  
 104:   HRESULT status = NtSetInformationProc(GetCurrentProcess(),
 105:                                         ProcessExecuteFlags,
 106:                                         &dep_flags,
 107:                                         sizeof(dep_flags));
 108:   return SUCCEEDED(status);
 109: }
 110:  
 111: }  // namespace sandbox

 

在留言板中也有很多东西读起来非常有趣,比如TODOs, HACKs,。你期望的所有东西都会有大规模的应用。有趣的是,我深入研究C++至少有5年之久。而我所做的这一切-〉全职所赚的钱。

更多的信息请查看在Vista SP1 上UAC被禁用,复选框被锁住了。以及线程是很有趣的,因为它们有一个连接到宏端口和Linux的线程类。最后请查看 Pickle.cc。他们通过“pickling them”将对象序列化。Pickle为Python序列化,这看起来就像是它们在C++和Python之间进行序列化。这是Pickle的一个C++实现。

再回到WTL,如果你感兴趣,可以在 Microsoft Downloads下载WTL 7.1的最终微软版本。然而, 2007年六月发布了最有趣的8.0版本。这是社区最新的发布。WTL 8 全面支持Vista!

微软在共享源码,引用源码或我最喜欢的开放源码发布着越来越多的代码。这简直太棒了!事实上Google已经使用开放源码了(尽管只有一小部分),这确实说明了开放源码的精神。

Comments (1)

  1. DevDivChina says:

    Visual Studio 11 测试版和.NET Framework 4.5 测试版将于下周,2月29日(美国时间)正式发布,详细信息可访问 blogs.msdn.com/.../vs-11.aspx

Skip to main content