随笔 - 55, 评论 - 292, 引用 - 17

导航

关于

我是新人,: )

每月存档

最新留言

  • re:Last day at Microsoft
    写的真好,看了觉得很受到启发,谢谢,
    by Zheying Zheng(匿名) on 2010/2/4 10:35:20
  • re:REST API的身份验证(Authentication)
    <p>顶</p>
    by kekesoft(注册) on 2010/1/27 20:27:43
  • re:Last day at Microsoft
    祝你好运啊
    by Eric v(匿名) on 2010/1/24 1:53:34
  • re:Last day at Microsoft
    鄙人正在打算内部调动,跨过欢德福卡海峡去西雅图呢, 以后互通有无,常联系.
    by Charlie 木匠(匿名) on 2010/1/11 4:23:23
  • re:Last day at Microsoft
    Good luck, buddy.
    by Huimiao Liu(匿名) on 2010/1/10 20:18:24
  • re:Last day at Microsoft
    &lt;p&gt;Oops,you are right. 改正了。&lt;/p&gt;
    by demonfox(匿名) on 2010/1/10 18:22:13
  • re:Last day at Microsoft
    2006年8月14日起至2009年1月8日止 是2010年吧
    by q(匿名) on 2010/1/10 14:59:53
  • re:Last day at Microsoft
    &quot;永远选择你最感兴趣的项目而不是升职空间等所谓的职业发展前景&quot; -- 说的很好!Good luck!
    by CoderZh(匿名) on 2010/1/8 20:49:26
  • re:Chrome OS和Android的背后
    Google当然不是在传统的操作系统上去跟微软计算,手机操作系统,Windows CE是公认的烂,Google在这里竞争没有什么不可以。Chrome OS更多的是Google云计算战略的一部分,人家根...
    by 啊(匿名) on 2009/12/22 13:14:12
  • re:利用Powershell做简单的单元测试
    <p>@ grapef: 调用所测试的类的函数的时候当然是通过reflection的,不过这里问题的关键是我无法将assembly加载到一个新的AppDomain中(以便之后可以卸载),如...
    by demonfox(注册) on 2009/10/12 4:55:22
  • re:利用Powershell做简单的单元测试
    我不确定这个是不是PowerShell的问题。但是,你的跨域操作的实现似乎有点问题。 一般情况下,如果需要将Assembly加载到另一个AppDomain中,并调用其中的代码。我都需要有一个两个域都...
    by Colin Han(匿名) on 2009/10/10 23:23:34
  • re:利用Powershell做简单的单元测试
    用powershell每次创建测试代码执行程序的进程,通过解析进程的output来验证成功或失败否。 这样可以避免程序及加载到powershell本身程序域的问题。
    by geniusleft(匿名) on 2009/10/10 12:28:14
  • re:利用Powershell做简单的单元测试
    用reflection调用可以么?
    by grapef(匿名) on 2009/10/10 11:16:34
  • re:为什么要goto? 为什么不要goto?
    呵呵,忘了留下邮件地址了,hiverdarcytutu@sina.com,谢谢啦~ 这个名字的出处正是&lt;傲慢与偏见&gt;呢,那是我最喜欢的一本小说。
    by Darcy(匿名) on 2009/9/14 10:02:28
  • re:为什么要goto? 为什么不要goto?
    <p>@ Darcy: 呵呵,那你也得留给我email呀,: ) 我可以试着找找,也许还能找到。另:我喜欢《傲慢与偏见》,不知你的nickname是否与之有关,: )</p>
    by demonfox(注册) on 2009/9/9 17:04:18

广告

【第1页/共4页,55条】
首页
前页
1
2010年01月27日

在Amazon就职两周半了,很有意思,和微软截然不同的感觉。不过这个以后再说,今天想聊聊刚才在InfoQ上看到的一篇文章:RESTful API Authentication Schemes,当然InfoQ上的其实只是转载,原文是George Reese写的Principles for Standardized REST Authentication

我在微软做的第一个Project就是一个基于REST协议API集已经一个Authentication引擎,所以此后对这两个话题一直很有兴趣,即使手边做的是其他类型的项目。这里我就结合当年自己做的那个项目来聊聊George Reese文章中所提出的3个观点。

George Reese在文中概括了他认为REST API Authentication所应该遵循的3条原则:

1. All REST API calls must take place over HTTPS with a certificate signed by a trusted CA. All clients must validate the certificate before interacting with the server.

译:所有REST API请求都必须通过加密的HTTPS协议来传递,加密所用的证书应由一个trusted CA (certificate authority)签发。所有客户端必须验证服务器端的身份证书,然后才开始进行会话。

关于这点我想应该是毫无疑义的。所有客户端和服务器端的通信内容应该都要通过加密通道(HTTPS)传输,明文的HTTP通道将会是man-in-the-middle及其各种变种攻击的温床。所谓man-in-the-middle攻击简单讲就是指恶意的黑客可以在客户端和服务器端的明文通信通道上做手脚,黑客可以监听通信内容,偷取机密信息,甚至可以篡改通信内容,而通过注入RSA公匙加密后的通信内容理论上是无法被破译的(除非对应的private key被偷了…)。George很幽默地说道:如果你没有对你的API调用请求加密,你甚至没有在假装很安全。

不过另外有一点经常被忽略的就是:即使使用了加密的HTTPS协议,客户端也必须对服务器端的身份进行验证。每当说到身份验证(Authentication),人们总是先想到服务器端对客户端进行的身份验证(以确定你是哪个用户),但其实反向的客户端对服务器端的验证也是极为重要的。在这个飞贼横行,假冒遍地的年代,你怎么知道为你提供服务的对象真的是你所指定的服务器呢?一个简单的spoof,或者被恶意篡改过的hosts文件,都可能让你在不知不觉中把各种机密信息心甘情愿地交给躲在暗处的偷儿。但请注意我这里并不是说我们用的authentication模式一定是传统意义上基于电子证书的Mutual SSL Authenatication,因为客户端不一定会拥有或使用独立电子证书,事实了,基于经济成本的考虑,大多数end-user并没有由一个trusted CA发布的电子证书(这玩意儿可不是太便宜,至少大多数情况下是这样,嗯,当然有些例外,比如GoDaddy这种的,记得那时10美元就可以搞一个,汗…)

 
2. All REST API calls should occur through dedicated API keys consisting of an identifying component and a shared, private secret. Systems must allow a given customer to have multiple active API keys and de-activate individual keys easily.

译:所有REST API必须由专门的API密匙来验证。API密匙(注意:这里的密匙是广义的,不一定是指public key authentication中的key)必须有一个身份确认段,以及一个客户端/服务器端共享的私有秘密。服务器端的系统必须允许每个用户拥有一个或多个API密匙,并且可以方便地禁用某些密匙(使之不再有效)。

这段话可能是文中写的最模糊的内容,我还是结合我们当年设计的方案来解释一下吧:

我们当年提供的客户端验证手段主要是基于mutual ssl(如果客户拥有自己的电子证书的话)和我们自己设计的一种authentication token。客户可以随时向我们申请任意个authentication token,我们会通过一个HTTPS通道将authentication token发送给客户,客户拿到token以后应该妥善保存,不应该和任何人分享(因为这是他们的身份证明,如果别人知道了,就可以冒充他们了)。之后,在客户向我们递交请求时(同样还是在一个HTTPS通道上),他必须同时递交一个authentication token来证明他的身份,我们在服务器端会进行验证。

Authentication token的设计大致是这样的:

AuthenticationToken = RealmString “:” TimeStamp “:”Domain “:”SignedPackage

而其中最关键的SignedPackage是这样定义的:Base64(S(Gpri,  RealmString + “:” + TimeStamp + “:” + DNS + OwnerPUID)),其中,Gpri是我们的private key,S代表签名操作,而Base64就是Base64编码。也就是说,SignedPackage是这样生成的:先取得这个字符串:RealmString + “:” + TimeStamp + “:” + DNS + OwnerPUID,然后用我们的private key对该字符串进行签名加密,最后用Base64编码一下(这个只是为了HTTP协议传输的需要,和安全性无关)。

对照George Reese的文章,就可以看出,他所谓的identifying component在我们的设计中就是DNS部分,而shared, private secret就是整个SignedPackage部分。请注意AuthenticationToken中的TimeStamp部分,也就是说,客户在不同时刻申请的token都会是不同的,所以这使得用户可以拥有多个token。而我们在服务器端也用很简单的机制可以revoke任何已经发出的token。

最后可能有人要问这里的OwnerPUID是干什么的,这个其实是我们服务中的一个特殊的设计,和我们所提供的服务细节有关,这里就先不讨论了,也许下次有机会。


3. All REST queries must be authenticated by signing the query parameters sorted in lower-case, alphabetical order using the private credential as the signing token. Signing should occur before URL encoding the query string.

所有REST的查询请求都必须经过验证,验证的方式是客户端需对所有查询参数以小写字母顺序排序后用上文所提到的shared, private secret进行签名加密。签名加密的步骤应该在对查询字符串进行URL编码之前执行

Ok, 这点是我不敢苟同的。对于查询字串加密会造成很多问题:比如所有的代理服务器的功能都无效了,比如服务器端缓存就无法实现了,最搞笑的是用户如果想把某个REST查询在Facebook上分享给朋友都不可能了。

其实最最重要的是我觉得要求在REST查询的URL上夹带authentication信息是对REST原则的一种违反。我不敢说我完全理解Roy Fielding大师论文的真义,但我觉得REST协议的一个基本原则就是低耦合,也就是专项专用。URL就是用来决定resource的位置的,而不应该也不必要再夹带其他功能,比如身份验证。身份验证的信息完全可以通过其他标准的HTTP协议的组件来实现(比如最简单的Authorization请求报头 – request header)。

 

George Reese的文章总体上还是很有意思的,但我唯独对第三点有所异议,不过当然这只是一些我个人的看法。什么是REST,REST应该怎样实现,REST的根本在哪里,这些内容似乎是blogsphere上吵不完的话题,我就不参与了,呵呵。

posted on 2010-01-27 19:06:07 by demonfox  评论(1) 阅读(3311)

 
2010年01月08日

明天(北美太平洋时间)是我在微软的最后一天。从2006年8月14日起至2010年1月8日止,我的微软生涯要告一段落了。

不过幸好我下一站的停靠是去Amazon,所以这个blog的抬头:Sleepless in Seattle,倒还是不用修改。我将会加入Amazon的Elastic Compute Cloud项目组,也就是俗称“云计算”的那个地方。

我从没有想过会一辈子都在微软工作,但也没有想过会这么快的离开。原来的打算是想待个5年再考虑其他的,现在只是3年多一些就决定要离开了,其中的种种变化和曲折暂时还不足为人道,我想我需要相当一段时间去消化和体会职场生存的种种明波暗流。总之在微软的3年总体而言还是收获颇丰的,前2年的确是一路都顺风顺水轻舟而过,但很大程度上也助长了自己自以为是等一些的坏习惯,而近9个月的徘徊也让自己有机会重新审视很多自身存在的缺陷和过去错误的决定。好在最终还是能及早脱身去一个自己真正感兴趣的项目重新开始,已经觉得实在是很幸运。

我不敢妄论自己在微软的3年能有什么感悟,至多,也可能不过是一些朦胧的经验教训。我很不知深浅地写出来,也许会对其他什么人偶尔有个启发:

1. 永远选择你最感兴趣的项目而不是升职空间等所谓的职业发展前景。真正的职场提升,是指在技术上的日积月累潜修默练。若是你对所做的东西不敢兴趣,你是做不到静下心来去钻研的。不要为了一点点待遇上的提高或一些所谓的上升空间的拓展绞尽脑汁。中国有句老话,叫机关算尽反误了卿卿性命,颇得其中三味。

2. 职场行走,先学做人。而做人最好的途径,我觉得,就是 1) 换位思考,助人为乐, 2) 制造双赢,互相帮助,3) 谦虚大度,平易近人。

3. 找一个好老板。找不到,想办法改善与现在老板的关系。改善不了,还是另谋职位吧。

4. 大部分合格的manager一般都喜欢这样的手下:1. 有大局观,以团队为重。2. 有能力,有执行力。3. 永远和他/她同进同退。

5. 交流的最终目的是Build Trust,不是show off,也不是制造紧张或矛盾。所以下一次你想让人家帮你什么忙时,可以首先想想能让对方从结果中收获点什么,因为没有比输送利益更好的Build Trust的方法了。

6. 不好听的话要多听。不好听的话要少说。

 

好了,去Amazon看看吧。

posted on 2010-01-08 16:49:51 by demonfox  评论(7) 阅读(6298)

 
2009年10月09日

上周为测试组写了一个用来对测试环境进行配置的小工具,在编写的时候要不停地调试看看功能是否运行正确。我们平时写项目是有unit test framework的,但写个小工具我就难得去加什么正式的unit test了。

不过没unit test,写程序的时候又浑身不舒服,所以就临时想在Powershell里写几个小scrip来做些简单的测试。因为这个工具是用C#写的,所以用Powershell直接调用assembly里的那些函数其实很方便。

在Powershell里要调用一个assembly里的类以及其方法,首先要做的是把这个assembly加载到当前的AppDomain里来。Powershell启动的时候是有一个默认的AppDomain的,其中也已经默认加载了一些.NET的基础assembly,可以用如下命令查看:

image

然后我写了一个简单的脚本来加载需要测试的assembly:

//load.ps1
 
if ($args.Length -ne 1 -or $args[0] -eq "/?" -or $args[0] -eq "-?")
{
  Write-Host "Usage:"
  Write-Host "  load.ps1 <Path to .NET assembly>"
  return
}
 
if (!(Test-Path $args[0]))
{
  Write-Host "ERROR: Specified .NET assembly does not exist"
  return
}
 
$assemblyPath = resolve-path $args[0]
Write-Host "$assemblyPath"
[System.Reflection.Assembly]::LoadFile($assemblyPath)

这样调用:

PS C:\me\Projects\Powershell> .\load.ps1 MathLib.dll

之后你可以再运行一下:[AppDomain]::CurrentDomain.GetAssemblies()来看看结果如何。

Assembly加载进来后就可以调用其中的类型以及函数了,比如你有如下的C#类:

namespace MathLib
{
    public class Math
    {
        public static double Add(double n1, double n2)
        {
            return n1 + n2;
        }
        public static double Subtract(double  n1, double n2)
        {
            return n1 - n2;
        }
        public double Multiply(double n1, double n2)
        {
            return n1 * n2;
        }
        public double Divide(double n1, double n2)
        {
            return n1 / n2;
        }
    }
}

你就可以在Powershell下直接这样调用:

PS C:\me\Projects\Powershell> [MathLib.Math]::Add(1, 2)

如果需要调用的不是static的成员函数也很简单,只要用“New-Object”先生成一个对象就可以调用了:

PS C:\me\Projects\Powershell> $obj = New-Object MathLib.Math
PS C:\me\Projects\Powershell> $obj.Multiply(3, 8)

下面这个小脚本可以快速看一下某个类所提供的成员函数:

//list-methods.ps1
 
$obj = New-Object $args[0]
$methods = $obj.GetType().GetMethods()
foreach ($m in $methods)
{
  Write-Host " - " $m.ReturnParameter.Member
}

 

这之后我其实发现这个方法有一个很大的问题,那就是加载到Powershell默认AppDomain中的assembly是无法动态卸载的,唯一的方法是关闭当前的Powershell窗口。这样的话,如果我们对代码进行了一些改动,但重新编译后却无法覆盖先前的assembly(因为它还是处于被加载的状态,想copy的话系统会报Access Denied)!这确实是很不方便(每次重新编译后,都要关掉Powershell窗口,copy新的assembly,然后再打开一个Powershell)。为了解决这个问题我看了很多资料,试用了N多的方法,不过最终没有找到一个好的解答。

首先要明确的是.NET中没有提供动态卸载单个Assembly的函数(动态加载是可以的,如上文所示)。所以只有通过卸载Assembly所在的那整个AppDomain才可以。不过我们显然不可能卸载Powershell自己所在的当前默认AppDomain,那么自然的答案就是先建立一个新的AppDomain,然后在新的AppDomain里加载所需的Assembly,使用完毕后再卸载整个AppDomain。

这个思路似乎是可行的,但在实际试验中我没有发现任何一种行得通的做法,我试过AppDomain::Load, AppDomain::CreateInstance, AppDomain::CreateInstanceAndUnwrap, AppDomain::CreateInstanceFrom, AppDomain::CreateInstanceFromAndUnwrap等各种方法,都没有成功。其中CreateInstance这一系列函数确实可以生成运行时的一个对象,但因为这些方法返回的结果是一个Object,而在没有将Assembly加载进来前,我又没有相关的类型定义可以用来做typecast,所以空有一个对象却无法调用所需的函数(如果你看完这段还能理解的话,我很佩服;不懂的话也没关系,自己动手试一下很快就明白我的意思了)。

我也查到有些资料说关键是要将你的Aseembly编译为强命名的(strong-named)并且将其加入GAC(过后再从GAC里清理出去即可),比如这篇文章:

http://www.eggheadcafe.com/software/aspnet/30766269/how-to-load--unload-dll.aspx

但我过后发现实际情况并非如此。事实上,你不用把你的assembly加入GAC(strong-named只是允许将assembly加入GAC前的先决条件)也有办法将它加载到一个新的AppDomain中来,下面是我写的代码:

// load-in-new-appdomain.ps1
 
$appDomain = [System.AppDomain]::CreateDomain("TempDomain")
Write-Host "Assemblies in the temp AppDomain"
$appDomain.GetAssemblies()
 
Write-Host "Assemblies in the default AppDomain"
[AppDomain]::CurrentDomain.GetAssemblies()
 
$appDomain.GetAssemblies()[0].GetType()::LoadFile("C:\me\Projects\Powershell\MathLib.dll")
Write-Host "Assemblies in the temp AppDomain after loading"
$appDomain.GetAssemblies()
 
Write-Host "Assemblies in the default AppDomain after loading"
[AppDomain]::CurrentDomain.GetAssemblies()
 
[AppDomain]::Unload($appDomain)
 
Write-Host "Assemblies in the temp AppDomain after unloading"
$appDomain.GetAssemblies()
 
Write-Host "Assemblies in the default AppDomain after unloading"
[AppDomain]::CurrentDomain.GetAssemblies()

看上去,我确实是对新建的$appDomain在调用LoadFile,但结果呢:

PS C:\me\Projects\Powershell> .\load-in-new-appdomain.ps1

Assemblies in the temp AppDomain

GAC    Version        Location
---    -------        --------
True   v2.0.50727     C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll

...

Assemblies in the temp AppDomain after loading
True   v2.0.50727     C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll

Assemblies in the default AppDomain after loading
True   v2.0.50727     C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll
True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.ConsoleHost\1.0.0.0__31bf3856ad364e35\Micros...
...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c561934e089\System.Xml.dll
False  v2.0.50727     C:\me\Projects\Powershell\MathLib.dll

Assemblies in the temp AppDomain after unloading
Exception calling "GetAssemblies" with "0" argument(s): "Attempted to access an unloaded AppDomain."
At C:\me\Projects\Powershell\load-in-new-appdomain.ps1:18 char:25
+ $appDomain.GetAssemblies( <<<< )

Assemblies in the default AppDomain after unloading
True   v2.0.50727     C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll
True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.ConsoleHost\1.0.0.0__31bf3856ad364e35\Micros...
...
True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c561934e089\System.Xml.dll
False  v2.0.50727     C:\me\Projects\Powershell\MathLib.dll

我们的Assembly还是被加载在了默认的AppDomain里!所以我们又回到了square 1,就是我们是无法卸载默认的这个AppDomain的,除非我们关掉Powershell的窗口。

所以最终我没能很好地解决每次重新编译都要关掉Powershell才能copy新的assembly的问题。如果你找到了一个很好的解决方法的话,请不吝赐教。

=========================================================================================================================

后记:写完这篇文章后,我才偶尔看到以下的一篇blog,原来说的是和我同样的意思,呵呵:

Why AppDomains are not a Magic Bullet

posted on 2009-10-09 17:40:21 by demonfox  评论(4) 阅读(5852)

 
2009年09月09日

这次做的一个project需要对一个XML文件进行解析,为了保证文件的格式是正确的,于是在进行解析(parsing)之前需要先用schema验证一下,大致的代码如下(C++):

   1: try
   2: {
   3:     HRESULT hr;
   4:     
   5:     hr = pProductListXml.CreateInstance(__uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER);
   6:     if (FAILED(hr))
   7:         return hr;
   8:     
   9:     MSXML2::IXMLDOMDocument2Ptr pSchemaDoc;
  10:     hr = pSchemaDoc.CreateInstance(__uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER);
  11:     if (FAILED(hr))
  12:         return hr;
  13:     
  14:     // TODO: initialize the schema doc
  15:  
  16:     MSXML2::IXMLDOMSchemaCollection2Ptr pSchemaColl;
  17:     hr = pSchemaColl.CreateInstance(__uuidof(MSXML2::XMLSchemaCache60), NULL, CLSCTX_INPROC_SERVER);
  18:     if (FAILED(hr))
  19:         return hr;
  20:  
  21:     hr = pSchemaColl->add(L"", pSchemaDoc.GetInterfacePtr());
  22:     if (FAILED(hr))
  23:         return hr;
  24:  
  25:     pProductListXml->schemas = pSchemaColl.GetInterfacePtr();
  26:     pProductListXml->async = VARIANT_FALSE;
  27:     pProductListXml->validateOnParse = VARIANT_FALSE;
  28:     pProductListXml->resolveExternals = VARIANT_FALSE;
  29:  
  30:     if (VARIANT_TRUE != pProductListXml->load(productListFile))
  31:         return HRESULT_FROM_WIN32(ERROR_XML_PARSE_ERROR);
  32:  
  33:     MSXML2::IXMLDOMParseErrorPtr pError = pProductListXml->validate();
  34:  
  35:     if (S_OK != pError->errorCode)
  36:         return HRESULT_FROM_WIN32(ERROR_XML_PARSE_ERROR);
  37: }
  38: catch (_com_error e)
  39: {
  40:     return e.Error();
  41: }

看起来有点繁复,主要是因为是用C++写的关系。进行schema验证的也就是:

MSXML2::IXMLDOMParseErrorPtr pError = pProductListXml->validate();

这一句。

在处理如何初始化schema文件的时候,遇到了点问题。因为schema文件本质上也是一个XML文件,所以我打算将schema文件当做一个二进制资源加入到工程中,然后在运行时将其作为一个字符串直接从内存中加载进来,然后调用pSchemaDoc->loadXML就可以了。所以上面的

  14:     // TODO: initialize the schema doc

 

展开实现后就是这样的

   1: _bstr_t schemaXml = GetProductsSchemaXml();
   2:  
   3: if (_bstr_t(L"") == schemaXml || VARIANT_TRUE != pSchemaDoc->loadXML(schemaXml))
   4: {
   5:     return HRESULT_FROM_WIN32(ERROR_XML_PARSE_ERROR);
   6: }

没问题吧?

最后就是如何实现GetProductsSchemaXml()了,这个帮助函数的代码如下:

   1: _bstr_t GetProductsSchemaXml()
   2: {
   3:     HMODULE hDll = ::GetModuleHandle(NAME_OF_THIS_DLL);
   4:     if (NULL == hDll)
   5:         return _bstr_t(L"");
   6:  
   7:     HRSRC  hResource = ::FindResource(hDll, MAKEINTRESOURCE(IDR_PRODUCTS_SCHEMA), L"BIN");
   8:     if (NULL == hResource)
   9:         return _bstr_t(L"");
  10:  
  11:     HGLOBAL hSchema = ::LoadResource(hDll, hResource);
  12:     if (NULL == hSchema)
  13:         return _bstr_t(L"");
  14:  
  15:     WCHAR* pSchemaXml = (WCHAR*)::LockResource(hSchema);
  16:     if (NULL == pSchemaXml)
  17:         return _bstr_t(L"");
  18:  
  19:     DWORD size = ::SizeofResource(hDll, hResource);
  20:  
  21:     return _bstr_t(CStringW(pSchemaXml, size));
  22: }

嗯,思路是很简单的,取得带有该二进制资源的DLL的句柄,然后从DLL里加载资源,最后将该资源作为一个字符串返回。

XML schema就是一个xsd文件,我们可以通过资源编辑器来将它嵌入到编译成的DLL中:

image

请注意我把资源的类型定名为"BIN”,这个其实是无所谓的,你爱叫什么都可以,只是记得这里的类型要和FindResource的第三个参数一致(参看上面的代码:::FindResource(.., .., L”BIN”) )。

另外请注意因为我的代码全部是Unicode的(仔细的朋友应该注意到了诸如:L”…” 以及 "WCHAR*” 这类明显的Unicode代码的特征),所以products.xsd这个文件也是以Unicode格式存盘的:

image

好了,到此为止工作基本上就完成了。

------------------------------------------------------------------------ 自以为工作已经结束的分割线 ---------------------------------------------------------------------------

但是,在实际测试的时候,pSchemaDoc->loadXML却一直返回VARIANT_FALSE这个失败的结果。怎么回事呢。

开动debugger来检视pSchemaXml(或者hResource,或者hSchema,这三者在这里其实都指向同一个内存地址,当然从语义上它们是不同的)指向的内存区,觉得内容看起来是对的呀:

 

image

调试了很久,一直到打开内存观察器(从菜单的Debug->Windows->Memory那里打开)我才意识到问题所在:

image

大家注意到红色框中的内容了么?也还记得刚才我说因为程序是Unicode的,所以我把products.xsd也以Unicode编码存盘的么?这就是问题所在了。因为UTF-16编码(UTF-16是Unicode的一种)文件的开头都有这个两字节的文件头(2-byte header),主要是用来区分UTF-16和UTF-8的,但就是这个“自动”多加出来的两个字节,使得pSchemaDoc->loadXML无法正确理解字符串(因为完全无法理解开头的FFFE这两个字节),所以当然就失败了。

另外顺便说一句:FFFE这个2 byte header也叫Byte Order Mark,也就是用来标记文件是low-endian的还是big-endian的。FFFE指的是low-endian,而FEFF指的是big-endian,有兴趣的同学可以试试在notepad里制定用Unicode big endian来存盘,然后再用任何十六进制编辑器看看开头的两个字节为何。

事情到这里也有了解决方案了,我只要在实际取得pSchemaXml的时候在LockResource返回的内存地址上再加2个bytes就行了。

可是,最后我发现其实还有更简单的方法,因为各种的混乱,我自己疏忽了。

事实上,你是可以用一个单字节的字符串(也就是ANSI编码的字符串)来初始化一个CStringW的对象的,所以根本就没有必要特意将products.xsd存为Unicode,所以最后我直接将products.xsd以ANSI编码存盘,然后GetProdutsSchemaXml这么写就可以了:

   1: _bstr_t GetProductsSchemaXml()
   2: {
   3:     HMODULE hDll = ::GetModuleHandle(NAME_OF_THIS_DLL);
   4:     if (NULL == hDll)
   5:         return _bstr_t(L"");
   6:  
   7:     HRSRC  hResource = ::FindResource(hDll, MAKEINTRESOURCE(IDR_PRODUCTS_SCHEMA), L"BIN");
   8:     if (NULL == hResource)
   9:         return _bstr_t(L"");
  10:  
  11:     HGLOBAL hSchema = ::LoadResource(hDll, hResource);
  12:     if (NULL == hSchema)
  13:         return _bstr_t(L"");
  14:  
  15:     // the products.xsd resource is stored in ANSI encoding, so we cast the pointer to char* instead of WCHAR*
  16:     char* pSchemaXml = (char*)::LockResource(hSchema);
  17:     if (NULL == pSchemaXml)
  18:         return _bstr_t(L"");
  19:  
  20:     DWORD size = ::SizeofResource(hDll, hResource);
  21:  
  22:     return _bstr_t(CStringW(pSchemaXml, size));
  23: }

 

总之不算什么了不起的软件技巧啦,只是所有这些小东西加起来可能还有点意思,因此写下来和大家分享。

posted on 2009-09-09 16:51:37 by demonfox  评论(0) 阅读(2969)

 
2009年08月03日

几日前在Cafe午餐的时候,大家聊起一些在Windows操作系统源代码库中曾经看到过的一些趣闻逸事,比如那个著名的“because Exchange is a moron”(正好这天公司的Exchange服务器巨慢,所以大家更是大发一笑)的注释。这其中有人提到Windows代码中大量使用goto语句的这个事,这让我想起这样一个有趣的问题:

在程序代码中,我们为什么使用goto,或者,我们为什么不该使用goto呢?

我曾经不止一次地听某某义正言辞地向我宣传goto是邪恶的,但如果我追问这么说的理由为何时,通常的答案都是模模糊糊的人云亦云之类的回答。大部分的理由都会指出goto破坏了程序的可读性和可维护性,如果代码里到处都是goto来goto去,到最后谁都很难搞清程序goto到哪一个地方了。

这看似颇有道理的说辞其实充满了迂腐的书生气。稍微有点常识的程序员,难道真会如此到处使用goto么?显然不会。如果说真的有那么一位程序员是到处在用goto把他的程序逻辑拼接起来的话,那我想他不是天才(汇编写太多了,到处都要自己跳转)就是无知(完全无法结构化自己的算法思路)。而软件开发作为一个工程行业经过这么多年的发展,现实中已经很少会真的有这种滥用goto的现象了。这当然也要感谢于那些关于goto邪恶性的大力宣传,大家上procedural programming第一课开始,就被反复灌输了“不要用goto,不要用goto”的观念。

那为什么Windows操作系统代码中大量使用了goto?是不是微软总部都雇佣了些烂人,大家都在混饭吃?还是说对于goto的使用是其实很有选择性的?而从当年goto的大量出现到今天这个关键词在使用C#或Java写就的程序中几乎绝迹,这一切,其实都是有其历史背景和含义的?

要回答这些问题,我们首先讨论一下goto在Windows操作系统源码中的使用。如果仔细观察一下的话,你会发现goto的使用其实都是在一种很特定的场合,那就是:系统资源的回收和释放。这里,系统资源可能是一块字符串内存,可能是某个内核对象(比如event或mutex)的句柄(handle),也可能是更复杂一些的数据结构。所以,goto出现的代码段,通常有这样的结构:

void Func()
{
  ...
  Magic::Initialize();
  BSTR someString = ::SysAllocString(L"Some random string");
 
  hr = CallSomeAPI();
  if (FAILED(hr))
    goto EXIT;
  ...
 
  hr = CallSomeOtherAPI();
  if (FAILED(hr))
    goto EXIT;
  
  ...
 
EXIT:
  Magic::Uninitialize();
  ::SysFreeString(someString);
  ...
}

如此便不难理解为什么goto在这种特定情况下可以简化代码编写的结构,使之更清晰易懂了。试想如果不试用goto,我们的代码就会变成:

HRESULT Func()
{  
  ...  
  Magic::Initialize();  
  BSTR someString = ::SysAllocString(L"Some random string");  
  hr = CallSomeAPI();  
  if (FAILED(hr))    
  {
    Magic::Uninitialize();  
    ::SysFreeString(someString);
    return hr;
  }
  ...   
  hr = CallSomeOtherAPI();  
  if (FAILED(hr))
  {
    Magic::Uninitialize();  
    ::SysFreeString(someString);    
    return hr;
  }    
  ... 
  return S_OK;
}

需要做回收处理的资源越多,这样的写法就显得越冗长,因此goto在这里是很自然的一种选择。

但随着面向对象的编程模式(Object-Oriented Programming Paradigm)逐渐地开始取代过程式编程(Procedural Programming),程序员开始发现有一种更好的模式(Pattern)可以用来取代goto,那就是RAII(Resource Acquisition Is Initialization)模式(“资源分配与初始化同步”)。RAII的主要思想在于两点:1. 对象在且一定在被分配或构造(construct)的时候同时被初始化,这样就避免了资源在没有被适当初始化前就被用户调用。2. 对象在被析构(destruct)的时候释放所占有的资源,这样就防止了资源泄漏。这个模式最为大家所熟知的应用可能就是C++标准库或者COM编程中随处可见的“聪明指针”(smart pointer)了。比如在上面的例子中,我们就可以定义一个MagicPtr的类,然后在类的构造函数里做Initialize,在析构函数里做Uninitialize。而对于BSTR,微软已经提供了相应的类了,那就是_bstr_t

利用goto来释放资源在procedural programming的时代是一个自然的选择,所以在Windows的源代码中你会看到goto的踪影,因为Windows在OO思想大行其道之前就已经存在多年了。但随着OOP的深入人心,遵循RAII来管理资源就成为了最自然的选择。

另一个重要的原因,就是异常处理(exception handling)概念的兴起。goto虽然可以很干净地解决过程式资源回收的问题,但却对异常这个东东没有很好的解决方法。比如上面的程序要是哪里抛出一个异常的话,那goto的部分就根本不会被执行了。而另一方面,RAII却能很好地解决这个问题,因为在对象离开定义域之前(不管是return了还是exception thrown了),析构函数都会被执行的。

其实写这篇东西的另一个目的也是想说:每一件看似简单的事情背后,如果你花一些时间去思考和研究,也许就会发现很多更深刻的意义和结果。这并不是要我们变成一个多疑的偏执狂,而是我觉得思索和提问的习惯是有益的。对于一个看似简单的道理,我们能不能提出让自己信服的佐证来,我们是否有一种直觉,告诉自己:I am wondering if there is more to it。事实上,这个世界上的偏执狂是少数,多的,是人云亦云的大众。

posted on 2009-08-03 15:41:57 by demonfox  评论(8) 阅读(4125)

 
2009年07月30日

18个月前我第一次听到微软准备以62%的溢价收购Yahoo的时候,我以为不是我疯了就是某些人疯了。似乎从AOL收购Time Warner后我就再也没听到过更蠢的收购议案了。我们不仅打算花44600000000的现金和股票来收购一个技术上已毫无优势,团队士气极为低落,企业文化又似乎已经跟不上时代的公司,而且会史无前例地将微软从创立以来第一次置于负债的境地,还不说收购以后对公司内部相关团队的士气影响以及我们将会浪费多少时间来整合对方庞大的工程资源等等不利的因素。

所幸的是,致远.杨同学很有骨气的拒绝了我们的offer,从而再次证明了一个古老的真理:你傻不要紧,只要你的对手更傻就可以。

而今晨的火线头条自然是微软和雅虎达成10年的搜索合作协议的消息,不过不同的是这一次,I feel so good about it.

1年半前收购雅虎,微软的项庄之意显然不是他们的搜索引擎技术。从后来发生的事来推算,Bing引擎那时已经在秘密开发中了。那么那个收购案的目的就很显然,那就是雅虎的搜索市场份额。基于搜索的在线广告这个东东是“赢家通吃”(winner takes all) 的游戏。试想,如果你是一个广告投放商,如果一个投放点能让你的广告被68%(Google的市场份额)的用户看到,而另一个投放点的观众只有8%(Bing的市场份额),你会选择哪一处呢?再进一步,如果你知道一家搜索引擎能针对68%的搜索流量来优化他们的广告显示结果和客户相关性,而另一家只能针对8%的数据来进行优化,你会选择谁呢?

所以说基于搜索的在线广告(注意,不是传统的静态广告。基于搜索的广告是根据用户搜索的关键字来显示广告的,而静态广告则一般就是投放在流量巨大的门户站点上,至于谁会看见谁会点击,只有天知道了)是一个“赢家通吃”或者“赢家制定规则”的游戏。排名靠后的参与者也还是可以参与市场定价,但相对于领头羊,他们必须承担大幅度折扣才能将自己的推销出去(简单说:Google可以要求广告投放的商家:点击一次你给我10分钱,而Bing就只能告诉同样的客户说:点击一次我只要3分钱。)

这也是为什么Google在搜索广告的市场占有率为68%,但却垄断了超过80%的营收。

所以对微软来说,将Yahoo的那20%多的用户群拿到手,确实是一种巨大的诱惑,因为这样一次性地将Bing这个选项(之前是Live Search)的吸引力提升到了另一个层次。即使从数字上看Google的市场份额并没有改变,但它实际上的营收却会收到巨大影响,因为现在它无法再以过去那样3.33倍(以上面假设的数字为例)于第二名的价格来要价客户了。

所以这次的合作协议,微软的目标并没有改变。只不过上一次,微软是急功近利地采取了一种低智的曲线救国的方法(通过并购),所幸的是杨同学发扬毫不利己专门利人的国际主义精神,毅然地让他作为创始人的ego超越了一切商业理智。

这次的协议中,微软无疑是大大的赢家。我们得到了最想要也是最重要的实质内容:流量(尤其是大量的长尾搜索流量,Google在企业级搜索上的优势还是明显的)还有用户元数据(用户的搜索历史,点击习惯,等等)。而后者对于Bing提升返回结果的相关性和进一步优化广告显示逻辑有着巨大的推动作用。Yahoo可以继续经营在线广告的投放渠道和客户关系,但这部分的内容的规模效应是很有限的。换句话说:如果微软想要参与现有的销售过程或者建立新的过程,并非难事。而Yahoo,已经和搜索引擎事业彻底告别了。

故垒西边,江山如画,这场微软和Google的旷世之战真是越来越精彩。

posted on 2009-07-30 07:05:16 by demonfox  评论(4) 阅读(5885)

 
2009年07月10日

Google放出了Chrome OS的消息,于是大小媒体都像打了鸡血一样兴奋起来宣布Google正式向微软的核心阵地开始进攻了。

其实只有娱记和书虫才会这么想这么去挖掘新闻材料。思路稍微清晰一点的人都可以看透Google根本就没想搞一个真正完整的用户级操作系统。Linux搞了那么多年没搞成的东西,Google才没有兴趣也去搞一遍,就算它有这个实力有这个财力,它也知道是这件东东很可能是花了大钱还要吃不了兜着走的烫手山芋。

那为啥Google要左一个Android右一个Chrome OS地大张旗鼓呢?当然不是钱太多烧得慌,也不是时间太多闲得慌。Google多面多样的各种客户端应用背后唯一统一的最终思路就是将用户整合到它的服务平台(service back end)上来。Google希望看到一个分裂的原生平台世界(Mac, Linux, Windows, Symbian, iPhone, Android等等),越分裂越好。在一个分裂的客户端世界中,唯一统一的用户体验就是Google的服务平台。客户端的世界越分裂,就有越多的开发者不得不离开某一个原生平台而走向网络应用和网络服务。这才是Google真正想要的。它看似眼花缭乱的各种工作背后,唯一不变的主题和动力就是将越来越多的人(用户或开发者)吸引到它的服务平台(也是它的revenue generator)上来。

因此Google当然喜欢开源社区(当然,只要开源社区离他们的数据中心里的那些核心技术越远越好)。首先,开源社区最擅长的就是提供多样的解决方案,尤其在客户端方面。Google欢迎这样分化多样的选择方案。选择越多,对Google越有利。其次,开源社区还是一文不取的活雷锋,就好像Android平台,除了智能手机之外的内容几乎都是开源社区给创造的。而这次的Chrome OS也不例外。Google所要做的,就是抛出一个初步的想法或雏形,造成很大的声势,自然会有媒体给它免费宣传与关注,以后添砖加瓦的事大多就留给开源社区了。Google自己则将继续集中注意力于他们的核心竞争力上,也就是他们的服务后端。

如果说Google在向微软的核心阵地发起进攻,那也不是从Chrome OS开始的,那是从Google发布Gmail就开始了,从发布App Engine就开始了,从发布Docs就开始了。Chrome OS,只是很自然的下一个走下流水线的半成品。在新闻稿里,Google提到Chrome OS是计划在2010年的下半年发布的,那个时候,Win7都已经发布了一年了,市场上一定已经充斥着以Win7、以Ubuntu、以OS X为基础全面优化过的上网本(netbook),你还真的以为Google指望靠这个迟到了许久的产品在netbook市场上大赚一笔?

posted on 2009-07-10 16:43:48 by demonfox  评论(7) 阅读(3991)

 
2009年07月01日

 

3月份从Windows Live转到Windows Embedded后,越来越多地需要与C/C++相依为命了。以前在C#里写点字符串处理的东西基本不用动什么脑子,现在单单是把两个字符串连起来,就要调用一个有4个参数的API

最近在做的一个项目顶层的用户界面(UI, User Interface)还是选择用C#/WinForm写了,毕竟再用MFC之类的东西,身心受不了那个摧残。不过底层真正实现功能的API还是必须要用C/C++来写(有很多客观的原因必须如此),所以一个必然需要解决的问题就是如何让用托管代码写成的UI层来调用用非托管代码写成的API层里的函数/类。也不知这样的事大家现在是否还经常需要做,不知会用COM/ATL的还有多少人,不过既然就此做了一些调查,我想还是把结果整理一下,写下来,也许对一些朋友会有用呢。

本文只讨论如何从托管代码调用非托管代码,其实反向操作(非托管代码调用托管代码)也有很多类似的技术,大家可以自己查一下资料。

基本上从托管代码调用非托管代码的函数/类型有一下几类方法:

 

1. 通过Platform Invoke (P/Invoke)

这个在托管代码需要调用Windows API的时候是很常见的,比如要调用kernel32.dll里的SetDllDirectory这个native API,只要这样做就可以了:

using System.Runtime.Interopservices
 
public MyClass
{
  [DllImport("kernel32.dll", SetLastError=true)]
  static extern bool SetDllDirectory(string lpPathName);
 
   ...
 
   public static void CallSetDllDirectory(string path)
   {
       ...
       SetDllDirectory(path);
       ...
   }
}
 
 

利用P/Invoke最大的麻烦可能就在于这个native函数的P/Invoke的签名应该如何申明。最常见的一种方法叫做google,就是你把TheAPIYouWantToCall和P/Invoke这两个关键字放在一起google一把,基本就应该找到了,第一个连接十有八九是来自www.pinvoke.net这个网站。

不过如果你要是说你不喜欢Google怎么办?Google上找不到怎么办?要是Google当掉了怎么办?要是Google因为传播色情信息被封锁了怎么办?当然你可以说:我没有Google我有Bing,那也是可行的。不过其实还有更直接的方法,在2008年1月的MSDN杂志上的CLR Inside Out的专栏里就有这么一篇文章:Marshaling between Managed and Unmanaged Code,其中介绍了P/Invoke Interop Assistant这个工具。你不但可以通过这个工具来查找绝大多数公开的Windows API的P/Invoke的签名,而且也可以用它来产生你自己写的native API的P/Invoke签名,很方便。你可以在这里下载这个工具

 

2. 通过C++/CLI wrapper class

这个方法主要是利用Managed C++来写一个封装函数/类,并以此来调用非托管的函数/类,而在另一边,用Managed C++写成的封装类的dll又可以直接被C#等托管代码来调用。

假设你有如下的C++类:

class __declspec(dllexport) NativeClass
{
public:
    static void Func(LPTCSTR);
    ...
};

你可以在Visual Studio中建立一个Visual C++的CLR空项目:

image

记得调整配置的类型:

image

然后编写如下的Managed C++类:

 
public ref class MCppClass
{
  public:
    static void Func(String^ str)
    {
        NativeClass::Func((LPTSTR)Marshal::StringToHGlobalUni(str).ToPointer());
    }
    ...
};

编译后生成的dll就可以直接被托管代码调用了。当然,关于托管代码变量和非托管代码变量之间marshaling的问题也是很头疼的,不过这个不是本文的讨论内容。

 

3. 通过CALLI instruction以及Reflection.Emit直接调用非托管代码

CALLI是Intermediate Language (IL)的一个指令。老实说我也不太清楚这个东东怎么用,只是看有的文章提到说可以如此做,但没有深入地研究一下,有兴趣的同学可以参考一下David Broman的这篇文章

 

4. 通过COM interop

这个就留待明天的Part II再讲吧,因为我还想顺带提一下COM里的一些相关内容,篇幅可能有些长,并且时间也不早了…

posted on 2009-07-01 17:11:29 by demonfox  评论(5) 阅读(4450)

 
2008年10月28日

忙了大半年的project,今天终于publicly release了。

Yes, baby, it's live from PDC 2008.  Windows Azure, the cloud computing platform from Microsoft.

Azure Services Platform的主站点已经开放:http://www.azure.com (or http://www.microsoft.com/azure ),你可以通过这个站点理解全面的关于Azure Services Platform的信息。

北美时间中午12点,Azure Developer Portal将正式开放: http://lx.azure.microsoft.com。普通用户一开始只能通过创建"Live Services: Existing APIs"来感受Azure Services Platform。其他三种最新的Azure Services App (Storage App, Hosted Services App, and Live Framework App)都必须持有特殊的invitation token才能使用。第一批开放的只有今天晚些时候发送给PDC现场的100个VIP token.

所有最新的Azure Services App中最引人注目的当然是两个Windows Azure的cloud computing application: Storage & Hosted Services. 这是下一代Microsoft自己的云计算平台,也将是将来几年甚至十几年中微软发展的方向。

我下周就要开始休假了,从2月初开始一直都是每天10多个小时的高强度工作,确实应该歇一下了。当然,也让我有更多的时间可以整理很多的技术资料以及更新这个blog。希望能通过这里给大家带来更多关于Windows Azure的信息。

Welcome to Windows Azure.

===============================================================================

Update 1: Channel 9上有两段很好的interview,分别是Manuvir Das (Director of Development in Windows Azure) 和 Steve Marx (Lead Program Manager in Windows Azure)。如果想初步了解一下Windows Azure平台,这两段是很好的入门介绍。

Fun note: 在Manuvir的interview的最后,Charles(就是主持interview的那位只闻其声不见其人的同学)说了句:Of course we cannot get Cutler, he refuses to come up and talk...

这里的"Cutler",自然指的就是Windows Kernel的终极大牛David Cutler。有兴趣了解一下David的可以搜索一下CSDN上一个著名的blog系列“心目中的编程高手”。

既然是新一代的Windows平台,里面自然是少不了D爷爷的手笔,所做的,当然也是他最擅长的OS & Virtualization...

 

posted on 2008-10-28 01:56:49 by demonfox  评论(3) 阅读(3296)

 
2008年04月29日

 

周五在San Francisco举行的Web 2.0 Expo上,Microsoft正式公布了新的Live Mesh平台。Live Mesh不仅仅是一个用户平台,也更是一个开发者平台。而借此,Microsoft也第一次大声地喊出:PC不再是计算的中心了,而Services才是平台的中心。

对于用户,现在IT的趋势是个人拥有越来越多的终端:手机,PDA,笔记本电脑,台式机,XBox,iPod,等等。而另一方面,各个终端上的社会网络和信息资源又很难保持一致或进行整合。手机上有一堆号码,笔记本上有一堆会议记录,台式机上有一堆照片,XBox上有一堆网友。但如果你什么时候想在开会的时候偷个空看看哪些XBox的玩友正好在线呢,Oops,不好意思,做不到,因为事先没有把这部分内容同步过来。

而对于开发者,他们希望他们所写的应用程序不但能在常用的计算平台上运行,也希望当用户在使用移动设备或者有网络能力的娱乐设备的时候,同样能使用他们的应用程序。设想,如果你开发了一个共享照片的服务,在台式机或者笔记本上运行的好好的,但你是否也希望当你的用户手边只有一个手机的时候,他同样可以轻松地使用你的程序的mobile版本来下载他所想要的照片呢。写个用户界面是小事,难的地方是在于如何在手机平台上无缝地获取存储在cloud (cloud as in cloud computing)中的资料。而Live Mesh就是想为开发者提供解决这样问题的平台,而不是让每个人绞尽脑汁地开发如此底层又吃力不讨好的服务。

Live Mesh的核心概念在于以下几点:

1. 服务是平台的中心,而不是终端(PC也好,手机也罢,服务应该是独立于硬件的)。

2. 统一的API集。无论你的程序是运行在离线的客户端,还是作为浏览器的plug-in,还是作为一个动态网页(后台连接Live Mesh的cloud storage),还是一个连线的桌面程序,所有的模式需要使用的API应该是一致的。

3. 开放的可扩展的数据模型。

4. 弹性的应用程序模型。开发者可以写仅支持连线状态的程序,也可以写同时支持离线和连线状态的程序,也可以写仅作为浏览器插件的程序,等等。Live Mesh不强制某一种应用程序模型,一切完全由开发者决定他们想支持的用户场景(user scenario)。

 

Picture_15

 

CNET上有一些报道可以帮助有兴趣的朋友开始了解Live Mesh:

Live Meshing on the Gillmor Gang

Microsoft Live Mesh platform takes on Google, Adobe

而这里有Ori Amiga的介绍Mesh程序开发的视频:

Ori Amiga: Programming the Mesh

 

=====================================================================================

这半年经历了很多人事变动,周围的事一直不是很安定,希望以后的一段时间能安静地做些技术。关于Live Mesh我还有不少想写的,虽然对它的某些内容我还有自己的看法,但总体来说这是一个好方向。这次微软推出Live Mesh后收到的评价也颇为正面,和以往一出来什么东西都被口诛笔伐得一文不值大为异趣。可见群众的眼光还是雪亮的,大家并不针对你这个人/公司来评价你的产品,Google做得不好的要批评,Microsoft做得好的也可以表扬。做技术的本来就该如此,政治还是留给别人去搞吧。

posted on 2008-04-29 07:58:47 by demonfox  评论(0) 阅读(4287)

 
2007年11月17日

Windows Live Contacts Control是另一个应用很广的控件,第三方开发者只需要在自己的服务器端放置一个channel文件,然后就可以在任何网页中通过安插一段简单的Javascript来显示Contacts Control了。当访问者用他们的Windows Live ID登录后,Contacts Control可以显示他们所有的联系人已经相关的地址、电话等信息(当然,前提是相关联系人已经输入了这些信息 - 在注册Live ID的时候,通过Messenger客户端,通过http://login.live.com都可以)。你可以在这里试试Contacts Control的实例:

http://dev.live.com/mashups/trycontactscontrol/

Contacts Control还有一个“变体”,叫Presence Control,它可以用来显示你联系人当前的在线信息,你可以到这里试用看看:

http://dev.live.com/mashups/trypresencecontrol/

不过,你无法在FireFox中使用Presence Control,原因是Presence Control其实在底层调用了Messenger Client的一个ActiveX接口,而在FireFox里是无法调用native的ActiveX接口的。不过这个问题可能很快就有迂回解决的方法了,我会在那个新的API发布后写blog介绍的。

 

要改变这两个Control的外观也很容易,只要简单设置一些参数就好了,具体的信息你可以在这里获得:

http://dev.live.com/contactscontrol/

你可以看到这些Control都已经本地化了,要使用中文界面的话只要把language/market参数设为zh-cn就好了。

 

最后顺便提一句,Dev Live组其实已经发布了一个控件包,你可以在Visual Studio 2008 (Orcas)下安装这个控件包,然后在集成开发环境(IDE)中就可以简单地往网页上拖放这些控件了。这个控件包里包括:

Contacts Control

IDLogin Control

IDLoginView Control

SilverlightStreamingMedia Control.

请参看这里的消息:http://dev.live.com/blogs/devlive/archive/2007/11/08/167.aspx

 

注:有必要说明的是:Contacts Control和Windows Live Data Contacts API用的是完全不同的技术。两者的目的也是不同的,Contacts Control (以及其他的PresenceControl,SpacesPhotosControl等)是为了给第三方网络开发人员提供轻量级的控件集使他们可以在很小的技术投入下能迅速利用Windows Live积累的用户信息资源来丰富用户体验。而Windows Live Data更像一个平台级的API,它致力于提供大量的原始数据而不是直接的终端体验,至于如何整合使用这些数据完全由第三方开发人员决定。

==========================================================================================================================

关于Windows Live服务访问速度的问题开心大哥已经和我说过了,事实上这个并非服务本身的问题,因为大部分Windows Live服务在欧美地区都有大量用户在使用并且没有访问速度的问题。中国区用户的大部分问题其实都源自于Windows Live数据中心的部署,我不知道有多少内容是我可以在公开的blog上说明的,只能说很多Windows Live的team都意识到了这个问题,也在想办法提供解决方案。

 

posted on 2007-11-17 03:36:00 by demonfox  评论(1) 阅读(5317)

 
2007年11月15日

好吧,我承认6个月前我们就发布了Windows Live Data Alpha。为什么现在“再”发布一次Alpha,别问我,that's something out of my control, : (

 

Anyway,版本号还是Alpha,但确实是有新的Feature,现在除了用户的AddressBook,开发者还可以通过Windows Live Data来取得用户的Spaces Photos! Yup, 你现在不用一个个地跑去你朋友的MSN Spaces去看他们最新的照片了,你可以自己写一个desktop程序或者web application来获取所有朋友的Spaces相册和照片。

DevLive上有两个API的开发文档,也提供了各自的Interactive SDK (Contacts, SpacesPhotos)。

Angus的blog上也有图文并茂的介绍

其实这个版本主要的改进是在后台,也就是为了应对越来越大的用户流量所做的架构和技术上的改变,但这些对于Windows Live Data的用户来说都是没有太大意义的,他们需要的是看得见摸得着的新的feature(比如这次SpacesPhotos的API)。在以后的开发中我们也会注意内部infrastructure的改进和可见的feature的更新之间的平衡。

嗯,下一个是什么  Hotmail? Calendar? Events? 想想看,等Windows Live Data onboard了这些Live Service后,通过这个接口,第三方的开发者可以接触到多大容量的用户信息啊,然后再对这些信息进行整合又能开发出多少有趣的mashup呢。

 

posted on 2007-11-15 08:30:00 by demonfox  评论(4) 阅读(5231)

 
2007年11月14日

ASP.NET中你可以通过继承IHttpHandler这个接口来实现一个同步(Synchronous)处理用户请求的类。比如你希望对于一切类型为fakephp的请求都通过你的Http Hanlder来处理,你可以实现以下这个类:

 

using System; using System.Web; public class FakePHPHttpHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { //let's pretend we can handle PHP stuff } public bool IsReusable { get { return true; } } }

然后通过在IIS里将你的dll注册为.fakephp的handler就可以了。这些在以下的MSDN文档里都有介绍:

http://msdn2.microsoft.com/en-us/library/system.web.ihttphandler.aspx

 

这里想说的是如何实现一个异步(Asynchronous)d的HTTP Handler。说起来其实也简单,只要实现IHttpAsyncHandler这个接口就好了。

IHttpAsyncHandler有两个method:

 

Public method BeginProcessRequest
Initiates an asynchronous call to the HTTP handler.

Public method EndProcessRequest
Provides an asynchronous process End method when the process ends.

这显然是和.NET Framework中标准的Asynchronous Programming Model (或者叫“异步模式”, Asynchronous Pattern)是一致的:

参考: Asynchronous Programming Overview

异步模式的优势是ASP.NET的worker thread不会等待BeginProcessRequest返回而是会掉头去接收其他的用户请求(当然你也可以要求worker thread等待,不过这样就等于变成了Synchronous Handler)。因为通常处理一个web request的后台时间需要比较长。假设你每秒只能处理10个用户请求,在同步模式下如果有11个人同时访问你的服务,就有一个人会看到500 Internal Server Error之类的错误消息了。但如果是异步模式,worker thread只要调用BeginProcessRequest,而根据Asynchronous Programming Overview,BeginProcessRequest应该立刻返回(“立刻”的含义是它不应该进行长时间的操作,而应该调用QueueUserWorkItem之类的API将耗时的任务放到新线程里执行),这样worker thread就可以腾出手去接收下一个user request了。

注:ASP.NET的max worker thread上限可以通过processModel configuration element里的maxWorkerThreads属性来改变(参考:Improving ASP.NET Performance 以及 processModel element),但最大的值也只是100 (range from 5 to 100, default 20)。

由此引出的问题自然是:当异步操作完成时, ASP.NET是如何知道并做相应处理的。这有一下几种选择:

1) 当调用BeginProcessRequest的时候,ASP.NET可以同时传入一个AsyncCallback的delegate,而在你完成异步操作后,你应该调用这个回调函数来通知ASP.NET。

2) ASP.NET可以不停地查看IAsyncResult (这个是BeginProcessRequest的返回值)IsCompleted属性来确认异步操作是否已经完成了,当然,当你完成异步操作时,你有义务将IsCompleted设成true。

3) ASP.NET也可以等待AsyncWaitHandle的信号,AsyncWaitHandle是IAsyncResult的另一个属性,这个和经典的Win32里waiting on kernel object是类似的。

4) ASP.NET可以直接调用EndProcessRequest。

注意:3) 和 4) 是Asynchronous Programming Overview里规定的标准的blocking execution的方式,也就是说,如果你的主线程在异步操作完成前无法再做任何工作时,它可以通过3) 或者 4)来等待异步操作的完成。

从理论上来说,你应该保证你的IHttpAsyncHandler能满足以上所有4种方式。但现实中,你未必一定如此做。那么哪些是我们必须实现以匹配ASP.NET的要求的呢?或者ASP.NET究竟是如何实现异步调用及返回的呢?

事实上,ASP.NET采用了方法1,也就是说,在调用BeignProcessRequest的时候,ASP.NET传入了一个AsyncCallback,而你应该在完成异步操作后调用这个callback,而在这个AsyncCallback里,ASP.NET又调用了你的EndProcessRequest来做收尾工作。

根据上面的讨论,我们可以如下设计我们的Asynchronous Http Hanlder:

 

public class AsyncFakePHPHttpHandler : IHttpAsyncHandler { private void ProcessRequestCallback(object state) { AsyncResult async = (AsyncResult)state; // this is where the real work is done ProcessRequest(async.context); async.isCompleted = true; if (null != async.asyncCallback) async.asyncCallback(async); } public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback asyncCallback, object state) { AsyncResult async = new AsyncResult(context, asyncCallback, state); // if the callback is null, we can return immediately and let EndProcessRequest do all the job // if callback is not null, we will use our thread pool to execute the necessary asynchronous operations // what happens in ASP.NET is that the callback in NOT null, so QueueUserWorkItem will be used if (null != async.asyncCallback) threadPool.QueueUserWorkItem(ProcessRequestCallback, async); return async; } // this design also satisfies method 4), we implement it this way to follow the Asynchronous Pattern as much as we can public void EndProcessRequest(IAsyncResult result) { AsyncResult async = (AsyncResult)result; if (null == async.asyncCallback) ProcessRequest(async.context); } }

 

总结一下实现异步Http Handler的要点:

1) 所有在BeginProcessRequest中的耗时操作(比如IO什么的)都应该采用异步调用(比如BeginRead/EndRead)或者生成新的线程去执行。不错,你可以设计一个blocking BeginProcessRequest,没有人能阻止你这么做。But that's a BAD BAD idea.

2) 实现BeginProcessRequest/EndProcessRequest的目的是允许ASP.NET来异步调用你的Http Handler

3) 你应该创建一个实现IAsyncResult接口的类,在BeginProcessRequest中你会生成一个该类的实例并返回给ASP.NET(注意BeginProcessRequest的返回值类型)。而根据Asynchronous Pattern,ASP.NET在调用EndProcessRequest的时候会把这个实例再传回给你,你可以用这个实例来判断你所执行的任务的当前状态。

4) 我个人感觉比较容易导致困惑的是这里“两段式”的异步调用操作。首先ASP.NET是通过BeginProcessRequest/EndProcessRequest来异步调用我们的Http Handler的。然后我们在BeginProcessRequest又再次用异步模式(用QueueUserWorkItem或者其他的Begin*/End*操作)去完成真正的工作。实际上第二步的异步调用才是真正生成另一个thread来处理工作的地方。ASP.NET调用我们的BeginProcessRequest只是一种形式上的协议通知,因为是我们告诉ASP.NET:Hey,我是一个异步的handler。ASP.NET说:那好吧,既然你这么说的,我就用你异步的接口来调用你。事实上,在HttpRuntime的源代码中,可以看到ASP.NET的操作如下:

                if (app is IHttpAsyncHandler) {
                    // asynchronous handler
                    IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app;
                    context.AsyncAppHandler = asyncHandler;
                    asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context);
                }
                else {
                    // synchronous handler
                    app.ProcessRequest(context);
                    FinishRequest(context.WorkerRequest, context, null);
                }

====================================================================

最后的题外话,关于IIS/ASP.NET处理请求的工作流也是一个很有趣的问题,下面的这篇文章很棒(but the author's English writing kinda sucks, : ) )。比如,从中我们可以知道,整个流程中其实牵涉了两个Queue,一个是Kernel mode下的,一个是User mode下的,而后者就是ASP.NET所使用的Application Queue,而ASP.NET的worker thread就是从这个Queue里去取下一个需要处理的请求的。

Dissection of an ASP.NET 2.0 request processing flow

posted on 2007-11-14 06:50:00 by demonfox  评论(4) 阅读(7608)

 
2007年11月09日

这些天在考虑Windows Live Data新的DataAccessModule (DAM) 的设计,由于用户的大量增加,原来server端2-tier的设计已经无法满足要求了。原来的情况下,所有的用户请求全在接受到该请求的host上处理完成,但现在需要加入consistent hashing来分散用户请求的处理(因为用户人数的大量增加)。即,每个用户都有一个key,当前端(frontend)接受到一个用户请求(request)后,通过对这个key做consistent hashing,将请求分散到不同的后端host上处理。从2-tier到n-tier(这里我只提了frontend和后端的processing host,但其实还有其他几个分布式的tier)的转变是DAM架构的设计陡然变得复杂许多。所以我们必须严格证明这样的改变是有必要的,不然何必没事找事把东西弄得更复杂呢。最明显的,原来2-tier下请求的处理用的是同步模式(synchronous model),但n-tier下,我们必须使用异步模式(asynchrnous model),这样才可以使前端frontend能不断接受请求而不比等待后端的processing host完成工作(后端的工作显然比前端耗时要长的多)。

考察的重点在于ASP.NET对于connection pool,work thread等一系列benchmark statistics的要求和限制,因此看了一些资料,觉得很好,于是分享给大家(ASP.NET的大牛大概都看过了,呵呵):

1. Contention, poor performance, and deadlocks when you make Web service requests from ASP.NET applications

2. Improving .NET Application Performance and Scalability, Chapter 6 — Improving ASP.NET Performance

3. processModel Element (ASP.NET Settings Schema)

 

下一篇blog我想写一下ASP.NET下应该如何实现异步请求处理,以及ASP.NET内部是如何支持异步处理的。下午看了些.NET framework的源代码,觉得有些意思。

posted on 2007-11-09 12:13:00 by demonfox  评论(1) 阅读(4529)

 
2007年11月08日

今天Microsoft发布了又一个有趣的应用:Windows Live Messenger IM Control。你可以将Windows Live Messenger IM Control放置于你的主页、blog、或者社交网站(social network website like Facebook)个人页面上,这样访问那个页面的朋友就可以通过IM Control直接和你交流了。IM Control支持IE6, IE7, FireFox 2.0 on Windows以及FireFox 2.0 on Mac OS,并有32种语言界面可选择。

 

以下是简单的翻译介绍(原文出自Angus Logan的blog

1. 首先访问:http://settings.messenger.live.com/applications/websettings.aspx,然后用你的Windows Live ID登陆。

 

2. 选择"Allow anyone on the Internet to see my online status and send me messages":

 

3. 简单地选择你显示“在线状态”的方式:

选项一是只显示一个小图标

选项二是显示完整的Messenger对话框

 

4. 将上面的HTML代码粘贴到你的个人主页、blog、或者社交网站个人页面上。

 

5. 当有人访问配置了IM Control的那个页面时,他/她就可以通过IM Control(按底下的Begin a Conversation)给你发送即时消息了。

 

6. 你可以使用这个网页来测试IM Control: http://settings.messenger.live.com/Applications/CreateHtml.aspx

 

这边是我现在的在线状态 

你也可以通过这个给我发送消息 (最近比较忙碌,so,请手下留情,: ) )

 

觉得这会是个很有趣的应用,比如有在淘宝开店的朋友们就可以在他们的商品页上放一个IM Control,然后即时给浏览他们商品的客户提供服务,: )

 

posted on 2007-11-08 09:35:00 by demonfox  评论(9) 阅读(5973)

 
【第1页/共4页,55条】
首页
前页
1

Powered by: Joycode.MVC引擎 0.5.2.0