David's profileprowyh's spaceBlogLists Tools Help

Blog


    February 23

    ASP.NET页面参数传递(Parameter passing on page posting)

    对于页面
     
     
    三个选择框的参数需要在页面提交时传递。
     
    如果这三个选择框的内容是在后台生成并填充的,则ASP.NET会通过ViewState变量记住这些值,从而可以在PostBack处理代码中获得。但如果这些值是在前台通过Ajax方式获得并填充的,则由于ViewState变量中没有记录这些值,就无法在PostBack代码中获取用户的选择。
     
    既然这些值(部门选择框是根据所选公司而变化的,用户选择框则是根据公司和部门而变化的)是在前台填充的,则需要在“查询”按钮点击时能够把这三个选择框的值记录到一个变量中,而通过这个变量将参数值传递给后台的PostBack处理代码。
     
    前台代码:
     
    function Query_Click()
    {
    // obtain user's selects and store them to a var embeded in document
    var corpid = document.getElementById("corpid").value;
    var deptid = document.getElementById("deptid").value;
    var uid = document.getElementById("uid").value;
     
    var hobj = document.getElementById("hiddenObject");
    if (hobj)
    {
    hobj.value = corpid + "." + deptid + "." + uid;
    }
     
    // other processing
    }
     
    后台代码:
     
    public void Page_Load(object sender, EventArgs arg)
    {
    // postback code snippet
    if (Page.IsPostBack)
    {
    string[] params = Request.Form["hiddenObject"].ToString().Split(new char[] { '.' });
    // corpid => params[0];
    // deptid => params[1];
    // uid => params[2];
    }
    }
     
    隐含的传递变量(连接前台与后台的中介):
     
    <input type="hidden" id="hiddenObject" name="hiddenObject" />
     
    February 18

    再谈SQL Server跟踪器(More About SQL Server Tracer)

    以前曾写过一篇关于手写SQL Server跟踪器的小文,后来发现一个问题,TraceServer的Read()方法是一个 blocking method,即如果当前的SQL Server没有跟踪事件发生的话,Read()就会阻塞(blocking)。这样以来,就无法以程序方式退出跟踪状态,也就是说,无法以程序方式让Read()从阻塞状态中退出。
     
    经过一段时间的摸索,解决了这个问题,虽然不是很完美,但还算实用。
     
    基本思路是:既然Read()方法阻塞在SQL Server的跟踪事件上,要想以程序方式退出跟踪,必须以程序方式触发SQL Server的跟踪事件,以促使Read()退出阻塞状态。而这只需要向SQL Server发送一个无效果的SQL语句即可。
     
    代码示例:
     
    using System;
    using System.IO;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Data;
    using System.Data.SqlClient;
    using System.Reflection;
    using System.Text;
    using System.Threading;
     
    using Microsoft.SqlServer.Management.Common;
    using Microsoft.SqlServer.Management.Smo;
    using Microsoft.SqlServer.Management.Trace;
     
    public class program
    {
    public static void Main()
    {
    __st = new SqlTracer();
     
    // start tracing
    Thread t = new Thread(new ThreadStart(TracingThread));
    t.Start();
     
    // do somethings
    Thread.Sleep(1000);
     
    // start stopping_thread
    Thread s = new Thread(new ThreadStart(StoppingThread));
    s.Start();
     
    // wait for stopping_thread to run
    Thread.Sleep(100);
     
    // stop tracing...
    // although we raised a ThreadAbortException by invoke Abort() method,
    // thread t can not capture the exception immediately since it blocked on Read().
    // so we must issue a sql statement to release Read() from blocking by thread s.
    t.Abort();
     
    // stop stopping_thread
    s.Abort();
    }
     
    public static void TracingThread()
    {
    try
    {
    __st.Tracing();
    }
    catch (ThreadAbortException exp) { Console.WriteLine(exp.Message); }
    }
     
    public static void StoppingThread()
    {
    SqlConnectionInfo sci = new SqlConnectionInfo();
    sci.UseIntegratedSecurity = true;
     
    SqlConnection cn = new SqlConnection(sci.ConnectionString);
    SqlCommand cmd = new SqlCommand("SET NOCOUNT ON");
    cmd.CommandType = CommandType.Text;
    cmd.Connection = cn;
     
    cn.Open();
     
    try
    {
    while (true)
    {
    cmd.ExecuteNonQuery();
    Thread.Sleep(10);
    }
    }
    catch (Exception) { }
    finally
    {
    cn.Close();
    }
    }
     
    private static SqlTracer __st;
    }
     
    public class SqlTracer
    {
    public SqlTracer()
    {
    SqlConnectionInfo sci = new SqlConnectionInfo();
    sci.UseIntegratedSecurity = true;
     
    __ts = new TraceServer();
    __ts.InitializeAsReader(sci, "prowyh.tdf");
    }
     
    public void Tracing()
    {
    try
    {
    while (__ts.Read())
    {
    Console.WriteLine("FieldCount: {0}", __ts.FieldCount);
    }
    }
    catch (Exception) { }
    }
     
    private TraceServer __ts;
    }
     
    February 06

    编写SQL Server的跟踪器(Programming SQL Server Tracer)

    Microsoft SQL Server 提供了一个很好用的跟踪器SQL Server Profiler,可以查看SQL语句的执行情况。
     
    如何自己写一个这样的跟踪器呢?
     
    先来了解一下SQL Server profiler的发展历史(取自http://blogs.sqlserver.org.au/blogs/greg_linwood/archive/2004/09/19/7.aspx):
     
    SQL Server从6.5版开始增加了一个SQL Trace filter utility,SQL 7.0做了一些改进,直到SQL 2000,才有了一个GUI工具SQL Server Profiler,但没有提供API,所以只能使用这个工具对SQL Server进行跟踪,而不能开发自己的工具。
     
    SQL Server 2005提供了一个trace library,我们可以利用这个library编写自己的跟踪工具。
     
    下面给出一个简单的示例:
     
    trace.cs
    using System;
    using Microsoft.SqlServer.Management.Common;
    using Microsoft.SqlServer.Management.Smo;
    using Microsoft.SqlServer.Management.Trace;
     
    public class SQLTracer
    {
    public static void Main(string[] args)
    {
    SqlConnectionInfo sci = new SqlConnectionInfo();
    sci.UseIntegratedSecurity = true;
     
    TraceServer ts = new TraceServer();
    ts.InitializeAsReader(sci, args[0]);
     
    try
    {
    while (ts.Read())
    {
    Console.WriteLine("Field Count: {0}", ts.FieldCount);
    }
    }
    catch (Exception) { }
    finally
    {
    ts.Stop();
    ts.Close();
    }
    }
    }
     
    编译:
    csc /r:"C:\Program Files\Microsoft SQL Server\90\SDK\Assemblies\Microsoft.SqlServer.ConnectionInfo.dll" /r:"C:\Program Files\Microsoft SQL Server\90\SDK\Assemblies\Microsoft.SqlServer.Smo.dll" /r:"C:\Program Files\Microsoft SQL Server\90\SDK\Assemblies\Microsoft.SqlServer.SmoEnum.dll" /t:exe SQLTracer.cs
     
    运行:
    SQLTracer prowyh.tdf
     
    说明:
    将TraceServer初始化为读取器(Reader)需要两个参数:第一个是SqlConnectionInfo实例,表示与数据库的连接;第二个是跟踪定义文件(trace definition file),示例中是以命令行参数形式给出的。
     
    可以用两种方式与数据库建立连接,一种是集成安全性(Integrated Security),用Windows帐户登录,如例中所示;另外一种是用SQL Server的帐户登录,此时SqlConnectionInfo的实例需要如下设置:
     
    SqlConnectionInfo sci = new SqlConnectionInfo();
    sci.UseIntegratedSecurity = false;
    sci.UserName = "mySqlAccount";
    sci.Password = "mySqlPassword";
     
    通过设置ServerName可以跟踪指定的SQL实例。
    sci.ServerName = "mySqlInstance";
     
    注:MSDN关于UseIntegratedSecurity属性的文档有误,其文为:
     
    A Boolean value that specifies whether the connection uses integrated security. If True, the connection does not use integrated security. If False (default), the connection does use integrated security.
     
    事实恰恰相反,如代码所示。
     
    还有一点需要说明的是如何获得SQL Server的跟踪定义文件。SQL Server的标准定义文件存于
    C:\Program Files\Microsoft SQL Server\90\Tools\Profiler\Templates\Microsoft SQL Server\90
    目录中。可以使用SQL Server Profiler定义自己的文件,用户自定义文件缺省是存于
    C:\Documents and Settings\Administrator\Application Data\Microsoft\SQL Profiler\9.0\Templates\Microsoft SQL Server\90
    目录下。
     
    January 13

    C#泛型的一个瑕疵(A flaw in C# Generics)

    先来看一段C++代码:
     
    template <typename T>
    T median(vector<T> v)
    {
    typedef vector<T>::size_type vsize;
     
    sort(v.begin(), v.end());
    vsize size = v.size();
    vsize mid = size / 2;
     
    return size % 2 == 0 ? (v[mid] + v[mid - 1]) / 2 : v[mid];
    }
     
    这段代码是从一个vector中取中值,如果将其翻译为C#代码,如下:
     
    public class Utility<T>
    {
    public static T Median(List<T> v)
    {
    v.Sort();
     
    int size = v.Count;
    int mid = size / 2;
     
    return size % 2 == 0 ? (v[mid] + v[mid - 1]) / 2 : v[mid];
    }
    }
     
    这段C#代码与上述的C++代码功能等价,但 C# 不允许对 T 类型的 List 元素执行 + 和 / 操作,因为C#无法确认 T 类型支持 + 和 / 操作。
     
    这种限制无疑提高了C#代码的安全性,但另一方面也确实造成了不很方便。Jeffrey Richter在《CLR via C#》中也提到这一点,说Microsoft将在未来的版本中解决这个问题,但直到C#的 3.5 版,上述代码仍然无法通过编译。这也说明这个问题确实不好解决,如要在保证安全性的前提下解决这个问题,必须要 T 类型明确支持 +、-、*、/ 等等这些操作,但又如何说明或限制 T 类型支持这些操作呢?或许 C# 4.0 的 dynamic 有助于解决这个问题,但这样一来,就有点舍近求远了。
     
     
    December 24

    C#的泛型之美(The elegance of C# Generics)

    菜单一般都呈树型结构(即所谓的一级、二级、三级菜单等),而将菜单项表示为树的结点,如:
     
    public class MenuItem
    {
    public MenuItem(...) { }
     
    public int ID { get; set; }
    public int Parent { get; set; }
    public bool Visited { get; set; }
     
    public string Caption { get; set; }
    // other properties elided for clarity
    }
     
    MenuItem中的ID、Parent、Visited表示了树结点的特征。
     
    而菜单的操作又大都会涉及到对菜单树的遍历,所以,很自然地会想到,如果将树的操作抽象出来,代码会更加简化。
     
    为了提高树的代码的重用性,或者说,为了使树更加一般化,我们用泛型来表示,如:
     
    public class Tree<T>
    {
    public Tree(List<T> root)
    {
    __root = root;
    }
     
    public bool HasChild(T node)
    {
    bool yes = false;
     
    foreach (T t in __root)
    {
    if (t.Parent == node.ID)
    {
    yes = true;
    break;
    }
    }
     
    return yes;
    }
    private List<T> __root;
    }
     
    我们希望能用类型参数T表示树的结点类型,这样,就可以使得我们定义的Tree适用于一般化的情况。
     
    但这样编译通不过,因为编译器无法确知T含有Parent成员,所以t.Parent为非法结构。如何解决这个问题呢?
     
    解决的办法就是要让编译器知道T含有Parent成员,直接的办法是利用C#泛型的约束,如:
     
    public class Tree<T> where T : MenuItem
    {
    }
     
    但这样一来,Tree就绑定于MenuItem结构了,而这并不是我们所希望的。
     
    C#泛型的约束(constraints)是一个非常优美的设计,合理使用约束,可以写出简洁、高效、类型安全的代码。但对于我们的例子,如何让一般化的Tree不依赖于特定的MenuItem呢?
     
    在我们对Tree的定义中,T是树的结点类型,菜单树只是树的一种,我们希望在Tree中表达一般的树,而不仅仅是菜单树。仔细考察上面的定义,我们发现MenuItem的定义中,表示树的本质的是三个成员:ID, Parent, Visited,只要有一种办法能够让编译器知道T包含这三个成员就行了。由此我们自然想到了接口(interface),一般的约束都表示为接口,如果接口能够表示这三个成员,则问题即可迎刃而解。
     
    C#的接口不仅可以是method interface,还可以是property interface,如此,我们可以将上述三个成员表示为ITree接口,如:
     
    public interface ITree
    {
    int ID { get; set; }
    int Parent { get; set; }
    bool Visited { get; set; }
    }
     
    而将Tree<T>表示为:
     
    public class Tree<T> where T : ITree
    {
    }
     
    而此时的MenuItem可以表示为:
     
    public class MenuItem : ITree
    {
    }
     
    这样一来,我们就可以写如下代码:
     
    List<MenuItem> mList = MenuManager.GetMenuList();
    Tree<MenuItem> tree = new Tree<MenuItem>(mList);
    if (tree.HasChild(mid)) { }
     
    Thanks for C#'s Generics to write these elegant codes!
     
    June 01

    我的工作空间 (My Workspace)

    看来网络广告是真有效果,以前也知道有Microsoft Office Online版本的事情,没怎么在意。今天在spaces.live.com上看到office.live.com的广告,点了一下,就成了Office Live Workspace的用户了。Open-mouthed
     
    试用了一下,感觉不错。
     
     
    界面做的非常专业,不愧是Microsoft,一上来就是大手笔,比我做的界面强多了…… Hot
     
    试比较:
     
     
    虽然我认为已经很专业了,但和Microsoft相比,还是差那么一点点。Smile
     
    1) 从Microsoft的几个在线系统(spaces.live.com, hotmail.com, msdn.microsoft.com, workspace.office.live.com)来看,Microsoft已经舍弃了HTML中的Button。
     
    2) workspace.office.live.com只是一个Workspace,文档的编辑还是利用Office桌面软件,这一方面是Online版的编辑器现在还无法与桌面软件相比(spaces.live.com的编辑器已经足够好了,但从UE的角度来说,还是无法与Word相比),另一方面也是一种策略,Microsoft不可能丢掉Desktop Office这只Cash Bull的!
     
    3) 使用workspace.office.live.com需要安装(或打开,enable)几个ActiveX控件,利用这些控件来连接Web与Desktop,这样使得workspace.office.live.com与Desktop Office的交互更加顺畅,而不会太有两张皮的感觉。这恐怕只有Microsoft才能够做到,因为你如果只有Online System,无法与桌面进行顺畅的交互。而如果Microsoft没有IE,要想顺利实现这些也是很难的(甚至是不可能的)。
     
    Web的桌面化(Web as Your Desktop)是目前的大趋势,SaaS(Software as a Service)在起着助推器的作用,但完全实现这点目前还有技术上的难度,HTML、DOM还有待于继续完善,JavaScript还很弱(不论是编程模型,还是调试手段,以及执行效率),浏览器对标准的实现程度还是一个问题。
     
    May 25

    让人不知所以的IE(The Puzzled IE)

    IE提供了一些命令用于页面与外部环境的交互,其中Copy命令是将选中的内容拷贝(复制)到剪贴板。可以如下的JavaScript代码实现:
     
    function copyToClipboard(obj)
    {
    var range = document.body.createTextRange();
    if (range)
    {
    range.moveToElementText(obj);
    range.select();
    var result = range.execCommand("Copy");
    }
    }
     
    出于安全原因,IE会显示一个提示框,以提示用户是否允许复制到剪贴板,如下图。
     
    LevenSite Server
     
    我们需要根据用户选择“允许”还是“不允许”来给出进一步的提示信息,但却无法得知用户的选择!MSDN说:Returns true if the command is successful. 言外之意就是,如果执行不成功,应该返回false。可测试的结果却是:不管“允许”还是“不允许”,result总是true!
     
    execCommand之外,IE还提供了几个用于查询命令执行情况的方法:
    bEnabled = object.queryCommandEnabled(sCommand);
    bIndeterm = object.queryCommandIndeterm(sCommand);
    bDone = object.queryCommandState(sCommand);
    bSupported = object.queryCommandSupported(sCommand);
     
    但这些方法在这件事情上毫无用处,不管选择“允许”还是“不允许”,这些方法的结果都一样!如与本文目的直接有关的应该是queryCommandState,MSDN说:(This method ) returns a Boolean value that indicates the current state of the command.但不管“允许”还是“不允许”,该方法总是返回false!
     
    如何知道用户选择的是"Allow access”还是"Don't allow”?答案是“无法知道”!
     
    想当年,我们曾为IE的DHTML举手称庆!也为IE的巨大包容性而“心怀感激”!然而,在“坐稳江山”之后,IE却无可避免地“自满”起来,真是宿命!
     
    所以,竞争,才是推动技术进步的不二动力!
     
    March 27

    话说UNIX之春秋战国

    很久没有光顾UNIX这个百花园了,最近借着做UNIX培训,又徜徉了一把。
        
    从87年接触UNIX,到现在已有20年了!想当初为了踏上由UNIX/C开辟出的“新大陆”而秉烛夜战的热情,恍如昨日……
        
    不幸的是,由于AT&T的“慷慨”,UNIX source code走出了生她养她的Bell Laboratories,宛如亚当与夏娃走出了伊甸园,而使得地上“爬满”了芸芸众生一样,UNIX从此不再“UNI-X”!
        
    潘多拉的盒子一旦打开,世界也就不再安宁。从UI、OSF,到POSIX,再到Open Group,将UNIXes拉拢到一起的尝试一波接一波,但UNIXes仍然“我行我素”,仍然标榜“我最美”!特别是各种庶出之Linux,更是让UNIX这一本已“人满为患”的大家族更加“人声鼎沸”!
        
    乱花渐欲迷人眼,现在即使想找一个UNIXes的最小公分母,也不是一件容易的事。如果说dump命令不算UNIX的classical command的话,那cron可是“与生俱来”的,但每个用户的crontab文件以及cron.allow、cron.deny已经“找不着北”了……
        
    UNIX的设计理念之一是“统一”,或者说“一致”,如文件,文件是UNIX中特别伟大的概念及实现机制,就是因为其统一的处理方式。再如处理文件的命令,既可以接受作为命令行参数的文件名,也可以从标准输入读,作为过滤器使用,从而为通过管道构建强大的命令组合提供了实现机制。这种“逻辑的一致性”使得UNIX实现一些处理功能时非常优雅!
        
    可惜的是,后来人却没能坚持这种优雅的“逻辑一致性”,有些命令让人非常烦恼!
        
    chage是一个设置用户帐户/密码有效期的命令,可以方便地更改用户的最近(上次)修改密码的日期:
        
    chage -d lastday user
        
    但这里lastday要求必须是日期格式,实际上/etc/shadow中的lastday存储的是上次修改密码的日期与1970-1-1的差!而UNIX又没有提供计算两日期之差的命令,这样有时候就非常麻烦,如对于这样的任务:
        
    用户peter的密码已失效,如何做才能使peter能够登录而又能够强制其立即修改密码?
        
    要使peter能够登录,必须向前推算peter的lastday,而且必须将lastday转换为日期格式,才能用chage命令来设置,下面是完成此任务的Shell Script:
        
    # $HOME/bin/enable.sh
    ######################
    # for user whose password have expired,
    # enable him/her to login, and force him/her to update password
    #
    # tested on Turbo Linux
    #
    # 2008-3-27
    ######################
    #! /bin/bash
     
    if [ $# -le 0 ]; then
        echo "usage: $0 user"
        exit 0
    fi
     
    a=($(cat /etc/shadow | grep "^$1:" | sed "s/::/:0:/g" | tr ":" " "))
    maxdays=${a[4]}
    inactive=${a[6]}

    d=$(expr $maxdays + $inactive - 1)
    c=$(expr $(date +"%s") / 86400)
    c=$(expr $c - $d)
    years=$(expr $c / 365)

    days=0
    idx=0
    while [ $idx -lt $years ]
    do
        y=$(expr $idx + 1970)
        days=$(expr $days + 365)
        
        r4=$(expr $y % 4)
        r100=$(expr $y % 100)
        r400=$(expr $y % 400)
        if [ $r4 -eq 0 -a $r100 -ne 0 -o $r4 -eq 0 -a $r400 -eq 0 ]; then
            days=$(expr $days + 1)
        fi
        idx=$(expr $idx + 1)
    done
    year=$(expr $idx + 1970)

    r4=$(expr $year % 4)
    r100=$(expr $year % 100)
    r400=$(expr $year % 400)
    ma=(31 28 31 30 31 30 31 31 30 31 30 31)
    if [ $r4 -eq 0 -a $r100 -ne 0 -o $r4 -eq 0 -a $r400 -eq 0 ]; then
        ma[1]=$(expr ${ma[1]} + 1)
    fi
     
    day=$(expr $c - $days)
    idx=0
    while [ $day -gt ${ma[$idx]} ]
    do
        day=$(expr $day - ${ma[$idx]})
        idx=$(expr $idx + 1)
    done

    month=$(expr $idx + 1)
    chage -d "$year-$month-$day" $1
    chage -l $1

    该脚本的用法:
    # enable.sh peter
     
    执行过该命令之后,peter即可登录,而且必须立即更改密码。
     
    该脚本之所以如此冗长,是因为要将天数转换为chage要求的日期!也许chage命令的设计/实现者认为chage主要是用来手工设置用户的lastday等参数的,参数取日期形式会更加自然,所以在内部实现了从日期到天数的转换,而将“自然”留给了用户。但对于此例所言之情况,则是“太不自然”了:要写那么冗长的代码将天数转换为日期,而后chage内部再将日期转换为天数!My God!
     
    如果chage支持天数形式的-d选项,则enable.sh将会简洁、优雅得多:
     
    # $HOME/bin/enable2.sh
    ######################
    # for user whose password have expired,
    # enable him/her to login, and force him/her to update password
    #
    # 2008-3-27
    ######################
    #! /bin/bash
     
    if [ $# -le 0 ]; then
        echo "usage: $0 user"
        exit 0
    fi
     
    a=($(cat /etc/shadow | grep "^$1:" | sed "s/::/:0:/g" | tr ":" " "))
    maxdays=${a[4]}
    inactive=${a[6]}

    d=$(expr $maxdays + $inactive - 1)
    chage -d $d $1
    chage -l $1
     
    可惜,该脚本却是不能执行的。
     
    September 11

    江山代有才人出(三)

    Jason Hiner先生在其blog《Sanity check: Could one of these five companies be the next Microsoft?》 中列举了5家公司,认为这5家公司在技术与平台上各有所长,都有可能是下一个Microsoft!
     
    5家公司分别是:Zoho, Amazon, Apple, Google, Cisco
     
    其中Zoho却是闻所未闻的,作者也认为:This is the only real “little guy” on the list. 但为什么居然把这个“little guy”列为第一位呢?
     
    Office被认为是Microsoft的“现金牛”,自从Lotus 1-2-3、WordPerfect等软件出局以后,Microsoft Office即独步天下!虽然也有个别软件在某些方面比较优秀,但Word + Excel + PowerPoint的完美组合(当然,你也可以认为是垃圾组合!)却成为公司商用软件的霸主。以至于谁要想把Microsoft摇晃一下,必须做出一个更好的Office!
     
    前段时间Google推出了自己的Google Office在线组合产品,应该说已经做得很不错了,也可能我孤陋寡闻,好像没见有更进一步的动作。
     
    Zoho来了……
     
    如果将Microsoft在IE 4.01中实现XMLHTTPRequest对象作为Ajax技术诞生日的话,Ajax的历史已经很“久远”了,只是当时XMLHTTPRequest像个丑小鸭一样,无人注意!Google为她穿上了“水晶鞋”,一时间,引得众人趋之若骛,Ajax这才大放异彩!
     
    不过,我们好像除了通过divContainer.innerHTML = htmlDataFromXMLHTTPRequest;而更新页面部分内容之外,不知道Ajax还能干什么?
     
    Zoho告诉我们:Ajax不只是噱头!利用Ajax,可以把Online Office做得很漂亮!!
     
    Zoho Writer
     
    Zoho Sheet
     
    Zoho Show
     
    当然,Zoho的产品不只这些,但这足以说明Jason Hiner先生为什么要将这个"little guy"列为第一位了。
     
    Zoho这些漂亮的产品说明了两个问题:
     
    1、Ajax所能做的还有很多,我们需要学习的还有很多。
     
    2、这些在线产品并不关心哪个文档格式会成为ISO标准,不管谁成为ISO标准,我都能互相转换。这是产品生存至关重要的内容。
     
    与之相关的还有另外一个问题:这样的产品能成为未来的主导么?
     
    June 20

    江山代有才人出(二)

    Russia-based Quintura Search Engine为我们带来了另外的惊喜: Quintura号称“可视化的搜索引擎(visual search engine)”,试图以人们思考概念的自然方式进行搜索。
     
    Google.com是基于关键词(keyword)的搜索,没有表达概念相关性的手段。实际上,人们进行搜索的过程就是一个不断求精(refinement)的过程,而此求精过程就是通过概念的相关性来完成的。
     
    Ask.com通过Narrow your searches和Expand your searches来体现这个过程,Quintura.com则是通过“概念云图(concept cloud)”来实现。下图是搜索“search engine”的结果。
     
     
    Quintura.com的页面分为三个部分:上部为功能条,左部为云图区,右部为结果列表区。结果列表区为嵌入的IFrame,其余就是一个平板的页面(flat page),通过嵌入的DIV而区分为若干个功能区。应用了Ajax技术,使得整个页面非常具有动态性。
     
     
    由此图可以看出,search engine与很多概念相关,如submission, watch, plugins, web, 等等。其中:
    Query words - words from the search box. The search engine looks for these words and highlights them in your search results.
    Query context - words close to your query words by meaning. The context words let you refine your query.
    Selected word - a word you are pointing to. If the Autorefinement check box is selected in your Settings, relevant links are downloaded (as if you add this word to your search query).
    Selected word context - words close to the selected word by meaning. The context words let you change the direction of searching.
    Favorite icon - an icon to the left of a word referring to a website found as most relevant for the specific word.

    Quintura.com声称,概念云图由后台非常复杂的神经网络和服务器集群做支撑!所以,搜索引擎是非常tech-intensive的!
    但IT,特别是Internet(Web),就是这样不断由技术推动着,“从胜利走向胜利”……
    June 19

    江山代有才人出

    Internet信息的海量特征催生了搜索引擎,使得人们可以快速地搜索信息。
     
    从信息搜索的角度来说,Google.com可以说达到了目前的最高水平,以至甚至认为Google.com是不可超越的(连Microsoft也自叹弗如)!
     
    But really? No!
     
    Ask.com is a better one!
     
     
    Ask.com的首页不再是Google.com简单的文本页面,而代之以华丽的视觉感受!然而,这并不是Ask.com最重要的。
     
     
    这是以搜索pancreas一词而显示的Ask.com页面,整个页面分为左、中、右三栏结构。中栏(A)为页面主体,显示搜索结果。右栏由多个组件组成,首先是图片(B),该部分显示与搜索关键词相关的图片,此图中显示的是与pancreas(胰腺)相关的图片,使人文图对照,一目了然。下面的组件(C)是pancreas的百科全书条目,再下面的组件D是pancreas一词的词典定义(下面未显示部分还有相关的blog)。通过这些内容,可以对所搜索的词(pancreas)有更准确的理解。左栏的E是精确搜索(narrow your search),F是扩展搜索(expand you search),通过这两部分,可以对搜索进行窄化(内涵搜索)与泛化(外延搜索)。
     
    通过简单的介绍可以看出,Ask.com已经不再是简单的search,而是对search加入了data mining技术!Ask.com所有这些组件作为一个整体,大大提高了结果信息的相关性!
     
    当然,Ask.com不仅仅是search:
     
     
    Ask.com也在努力创造自己的社区!
     
    Ask.com和Answers.com在某种意义上体现了知识搜索的雏形!
     
    当然,Ask.com也并非完美,访问速度比较慢,而且有横向滚动条,其社区的页面还处于Yahoo.com早期的水平,对图片的编辑也不如flickr.com方便!Anyway, I love Ask.com!
    May 18

    又回到了全拼输入法

    为了解决中文输入的问题,各路高人各显神通。Windows Server 2003标准配置的输入法就有:内码、全拼、双拼、郑码、智能ABC、微软拼音输入法2003。据说现在研究输入法的仍大有人在!

    一直有一个“顽固”的想法:中文输入应该是一个自然的过程,不应该特意去学。所以对“五笔”一直敬而远之。 

    最早的时候是用的双拼,后来全拼输入法出来后,就一直采用全拼。现代汉语的特点是以“词”为组成单位,而不是“字”,而且,使用全拼可以建立自己的个性化词库。所以,理论上,如果词库足够“好”,则中文输入的速度将足够快。

    微软一直在研究自己的拼音输入法,许多人觉得很好用。趁着这次重装系统,也安装了微软拼音输入法,但今天,我还是把它卸掉了。

    微软拼音输入法采用了人工智能(AI)的机器学习技术,其目的是由机器(软件)辅助人的中文输入,具体体现为三点:

    ①     根据字与字的人工组合自动“组字成词”

    ②     根据字(或词)的使用频度动态调整字/词的出现位置(将使用频度高的字/词自动往前排列)

    ③     “组词成句”

    这看起来很合理,但实际使用中却不尽然。

    首先,“组字成词”需要一个人工“拣字”的过程,无疑降低了输入速度。

    其次,根据字/词的使用频度自动调整出现位置,最易出现错别字!因为人很容易记住某个字/词的出现位置,在随后的输入过程中,在输入完整或部分拼音后,直接按相应的数字键(表示字/词的出现位置的选择键)就可以完成输入,而一旦位置不再固定不变,则很容易输入同音的别字,否则就要在输入每个字/词时都要仔细查看其位置,这样一来,输入速度就可想而知了。

    最后,“组词成句”看起来很完美,但并不实用。因为很少有一句话会到处套用,所以其“复用”程度很低。

    虽然微软拼音输入法有这些问题,但其借助AI技术提高中文输入速度的努力是值得称道的,与之相比,全拼输入法这么多年在技术上却一直没有进步(实际情况不得而知,仅根据Windows上所配置的全拼输入法而言),真是可惜!

    全拼输入法如果在这些方面有所改进,则其fans将有福了:

    ①     完善词库管理

    一直不知道全拼的词库存放在哪里,所以每次重装系统,都会丢失以前所建的词库,又得从头开始一个词一个词地建。后来才知道,原来隐藏在一个很深的目录下,具体位置:

    C:\Documents and Settings\[user]\Application Data\Microsoft\IME\winpy\WINPY.EMB

    重装系统前,备份此文件,然后再恢复,即可。

    如果在词库的管理界面上提供一些管理功能,该有多好!(实现起来并不费事)

    ②     提高软件的适应性与容错能力

    有时候,位于屏幕左下角的全拼功能窗口无法“消隐”,一直固执地出现在那里,非常烦!而且有时候还会出现多个!!而且任何一个输入框都会默认设置为中文输入状态,不胜其烦!!!

    难道这就是为“爱”必须要付出的代价么?

    说起来,也可能是无可奈何罢。微软只对发展自己的拼音输入法有兴趣,而并无意于发展不属于自己的全拼输入法,即使全拼的开发商有意完善,恐怕也是没有办法的罢。

    或许,世界本不完美,要求完美本身就是一个西西弗斯的问题……

    January 13

    MSN,when do you Live?

    微软将MSN变成Live,原本指望能够真正"live"起来,可还是很微软……
     
    1、台海地震使得MSN变成了MS No!可为什么Google一直好用呢?
     
    2、如果说地震是天灾,是不可抗力的话,MSN有些令人啼笑皆非的事情就匪夷所思了:
    1)添加评论时,作者栏显示“(没有名字)”,而且更不可理解的是还有的显示“(没有名称)”!?
    2)提交评论时,系统显示“由于服务器故障,提交没有成功,请再次提交”(大意),可如果再次提交,就会发现前一次的提交已经成功,从而导致重复提交!
    3)发表的评论不能修改。这不仅仅是MSN如此,几乎所有的blog系统,评论都不能修改!这是否有点obtrusive呢?现在键盘敲字不像在纸上写字(哎,都快不会写了),不经意间就会打错,为什么不给人改正的机会呢?犯错误并不可怕,可怕的是没有机会改正错误!
     
    3、MSN Spaces没有提供备份工具,万一哪一天这个space crashed,那……?
     
    MSN,提供的已不仅仅是工具,而是一种生活方式,你什么时候才能Live起来呢?
    December 18

    程序语言之胡言乱语

    今天与朋友喝酒,席间谈起程序设计语言的问题,特别是C/C++,不禁唏嘘……
     
    上世纪80年代,伴随着Unix的崛起,C语言成为了软件开发的“标准”语言,Brian W. Kernighan and Dennis M. Ritche的《The C Programming Language》也随之而成为软件开发人员的经典。
     
    90年代,伴随着面向对象(Object-oriented)概念的兴起,C的后继者C++借助Visual C++而成为了Windows环境下软件开发的“王者”……
     
    然而,随着Windows平台由COM时代进入.NET时代,C/C++已然风光不再……
     
    目前存在着三条近乎平行的技术路线:
     
    1、Sun公司的Java平台
    2、Microsoft公司的.NET平台
    3、Linux平台
     
    Java平台的原生语言(也是唯一的语言)是Java语言,.NET平台的原生语言是C#(虽然.NET平台支持各种语言,但无疑C#是最“自然”的语言,而不论是Managed C++还是C++/CLI,看着总是那么别扭),而Linux平台目前尚缺乏商用的应用程序,在Linux平台上运行最多的还是LAMP(Linux + Apache + MySQL + PHP)的Web系统。
     
    由以上的简单分析,使我们不得不感叹C/C++的“美人迟暮”,令人唏嘘不已……虽然心痛,却也无可奈何……
    November 07

    DHTML Scriptlet(四)

    高级特性

    属性的行为在有些情况下与内存地址一样,但只是这样还不够,设想一个组件有一个颜色属性,在设置该属性后,希望能够立即在组件上反映出来,大多数组件都需要在改变它们的一些属性时能够立即反映出来。为了支持这一点,组件体系结构允许你将函数定义为组件的属性。将函数定义为属性是在函数名前加public_get_或public_put_前缀。

     例子
     下面的例子显示了函数如何用于实现属性:
     <Script Language=”Jscript”>
     property1 = ‘some text’;
     property1GetCount = 0;
      property1PutCount = 0;
     function public_get_property1()
      {
      property1GetCount++;  // 跟踪该函数的调用次数
       Return property1;   // 返回实际的属性值
     }
      function public_put_property1()
     {
     property1PutCount++;
      property1 = new_value;
     refresh();
    }
    </Script>

    在这个例子中,函数记录了自己被调用的次数,在put函数中一旦设置了新值,组件将以某种方式刷新,你仍然可以在容器页面中像其它属性一样引用Scriptlet的property1属性:

     Scriptlet1.property1 = ‘new text’;
     A = Scriptlet1.property1;

    在第一行中,给属性property1赋值导致public_put_property1函数被调用以存储属性值(“new text”字符串),第二行调用public_get_property1函数以取得property1属性的值。

    如果是用JScript写Scriptlet,则还有另外一个选择来描述Scriptlet的界面,即使用public_description。如果用public_description定义一个对象,则该对象的成员将成为外部可以引用的属性和方法,而不再需要用public_前缀。如果有带有public_前缀的全局变量或函数,则被忽略。

    目前版本的VBScript不允许像JScript那样用public_description创建对象,但将来的版本将可以。

    用什么机制来定义Scriptlet的界面呢?这在很大程度上是一个风格的问题。用public_description来描述Scriptlet的公共界面,由于集中在一起,因而比较简洁,而不是像用public_前缀那样,界面的定义散落在整个Scriptlet代码中。

    DHTML Scriptlet(三)

    如何写DHTML Scriptlet

    Scriptlet可以是HTML页面或ASP页面,你可以使用任何HTML著作工具(如Microsoft FrontPage或Visual InterDev)创建Scriptlet。当然,如果你的环境不支持脚本编程,你就要在设计好HTML页面后再手工加入脚本代码。

    Scriptlet只呈现全局变量、过程及函数。要在需要呈现给外部的变量或函数名之前加一个“public_”前缀,任何带有这个前缀的全局变量都成为了Scriptlet的可读写的属性,任何带有这个前缀的全局函数或过程都成为了Scriptlet的公共方法。但若你在Scriptlet之外引用这些属性或方法,则不需要这个前缀。例如,在Scriptlet中做如下声明:

     <Script Language=”JScript”>
      public_property1 = ‘some text’;
      function public_method1(param1, param2)
      {
       … ‘some code’…
      }
     </Script>

    则可以在包含该页面的脚本中引用这些属性或方法:
     Scriptlet1.property1 = ‘some different text’;
     A = Scriptlet1.method1(2, ‘sill more-text’);
     
    事件

    Scriptlet可以引发两种事件:onscriptletevent事件和标准窗口事件(如鼠标点击),标准窗口事件不能由Scriptlet随意产生,只能在某些情况下引发。

    Scriptlet可以认为本质上比传统的Windows程序更动态一些,对于在装入并分析过该Scriptlet以后才可知道的事件类型,Web著作工具并不能理解。基于此,Scriptlet与其容器之间的通信只能通过一种事件类型:onscriptletevent。Onscriptletevent事件包含两个参数:一个字符串及一个对象。事件处理程序可以根据传送的字符串决定如何响应此事件,对象参数则包含有关事件本身的一些信息。

    Scriptlet支持的标准窗口事件是通过传播产生于Scriptlet中的事件而引发的。如果你在Scriptlet中写了一个事件处理程序,来捕捉鼠标点击事件,则在事件处理程序中就可以调用bubbleEvent方法将该事件传播到Scriptlet的容器中,就像是从Scriptlet容器中产生的鼠标点击事件一样。

     例子:
     <INPUT TYPE=”BUTTON” onclick=”doClick()”>
     <Script Language=”JavaScript”>
      Function doClick()
      {
       // 将鼠标点击事件传播给容器
       window.external.bubbleEvent();
      }
     </Script>
     
    上下文菜单

    可以很容易地为Scriptlet创建上下文菜单。这是一个弹出式菜单,将鼠标移到Scriptlet组件上并按右键,就会弹出此菜单。可以为每一个Scriptlet都定义一个上下文菜单,也可以在任何时候替换(修改)该菜单。

    要创建一个上下文菜单,首先建立一个字符串数组,将数组元素每两个一组配对,其值为字符串:每对的第一个元素是将出现在上下文菜单中的标号字符串(Label String),即菜单项名称,第二个元素是当该菜单项被选中时所要执行的Scriptlet中的脚本函数名。数组初始化完成后,以该数组作为唯一参数调用window.external.setContextMenu函数。这样,当用户在组件上按鼠标右键时,将弹出上下文菜单。

     例子
     <Script Language=”VBScript”>
     sub window_onload
      dim a(6)
      a(0) = “Add &Hello”
      a(1) = “Hello”
      a(2) = “Add &Goodbye”
      a(3) = “Goodbye”
      a(4) = “&About”
      a(5) = “About”
      window.external.setContextMenu(a)
     end sub
     </Script>

    DHTML Scriptlet(二)

    如何在Web页面中使用DHTML Scriptlet

    要使用Scriptlet,需要在Web页面中加入OBJECT标记,以标准URL的形式命名要引用的Scriptlet。为了标识DHTML Scriptlet,IE4引入了一种新的MIME类型:text/x-scriptlet。IE4就是根据“text/x-scriptlet”的MIME类型识别嵌入在Web页面中的DHTML Scriptlet的。下面的例子表示了如何在Web页面中嵌入DHTML Scriptlet,注意在OBJECT标记中并没有CLSID。

     例子:
      <OBJECT width=200 height=123 TYPE=”text/x-scriptlet” DATA=”Calendar.htm”></OBJECT>
    其中,TYPE属性表示该OBJECT标识一个DHTML Scriptlet,DATA属性表示该Scriptlet的URL。
    所有平台(Windows、Macintosh、UNIX)的IE4均支持这种MIME类型。

    DHTML Scriptlet可以用任何脚本语言(如Microsoft JScript或Visual Basic Scripting Edition)来写。
     
    DHTML Scriptlet的实现

    前面我们已经说过,任何平台的IE4都支持DHTML Scriptlet,但平台不同,DHTML Scriptlet的实现方式可能也会不一样,在IE4支持的所有平台,一个DHTML Scriptlet就是一个Web页面。在Win32®平台,DHTML Scriptlet的实现充分利用了IE4的特性,以COM对象的方式实现DHTML Scriptlet,也就是说,一个DHTML Scriptlet就是一个COM对象,并且可以用于支持COM技术的其它应用程序中。

    由于COM对象封装了IE4中的HTML绘制引擎(HTML Rendering Engine,这是IE4中的一个组件,文件名为MSHTML.DLL,它负责分析、显示HTML页面、COM控件以及Java Applet等)并且隐藏了它的大部分功能,所以,与将绘制引擎呈现给其用户的Web控件对象不同,Scriptlet组件只向其用户(包含该Scriptlet的Web页面或其它容器)呈现该Scriptlet的接口。这种呈现是通过将该Scriptlet(Scriptlet即是一个HTML页面)装入IE4的绘制引擎并以通常方式运行页面的脚本而实现的。一旦装入完成后,Scriptlet组件就准备与其容器(Container)进行交互。在通常的Web环境下,容器当然是指IE4,但任何容器,包括用Visual Basic写的程序,或甚至Microsoft Word,都可以像插入任何其它ActiveX控件一样地插入DHTML Scriptlet。

    然后Scriptlet组件桥接层就作为一个代理(Broker)起作用,将对属性和方法的请求传送给Scriptlet,而将事件从Scriptlet中传出。桥接层也完成一些薄记功能(housekeeping)如在容器和绘制引擎之间协商Scriptlet的尺寸。
     
    DHTML Scriptlet的安全性

    Scriptlet的安全性与DHTML和Script本身一样,并且Scriptlet会识别出在其被放入一个如IE4这样的安全容器中时,遵守容器的安全策略。

    一般情况下,为了能够正确地操作,Scriptlet必须从其容器页面所在的Web服务器中装入,就像Java Applet一样。而且IE4的安全级别要设置为中(Medium)或低(Low)。

    DHTML Scriptlet -- Web中的可重用对象

    [注记] 这是1999年为了研究Scriptlet而整理的一篇文章。Scriptlet可以说是微软为了应对Java Applet而提出的运行于客户端的轻量型部件(light-weight applet,文中称为Web组件——Web Component)。现在Scriptlet已经升级为Behaviors,但此文对了解Behaviors仍有参考价值,另外也为了防止放在机器里而丢失,故发在这里。文章比较长,将分开发布。
     
    关于Demo的说明:文章本来附有一个Demo,在IE7里测试了一下,已经不支持了。由此也说明一个问题,由于自己不具有核心技术,在跟踪研究别人的技术时是要冒一定风险的。这也是没有办法的事,真正具有浏览器核心技术并能够领导潮流的又能有几家呢?
     
    随着在Web中引入脚本语言,Internet已经转变成为支持复杂编程的动态环境,特别是IE4中动态HTML(Dynamic HTML)的引入,标志着Web正在走向成熟,已经不仅仅只是发布、浏览HTML页面,也可以作为应用程序用户界面的框架。而这不仅对于Web程序员,而且对于任何Windows应用程序的开发者来说,都是一个“利好”消息,至少在开发软件时又多了一个选择。
     
    我们知道,用Visual Basic写程序非常容易,由于有大量的控件(Control)可以利用,用Visual Basic写程序就变成了一个组装控件并使其协同工作的过程。作为此点的自然延伸,微软公司在提出动态HTML(DHTML),使HTML具备足够的描述能力以后,又提出了Web组件(Web Component)的概念,而DHTML Scriptlet即为Web组件的一部分。
     
    DHTML Scriptlet是一个封装了HTML和脚本(Script)的Web组件,它可以充分发挥DHTML的功能,DHTML Scriptlet使Web作者可以创建交互的Web内容,但它最主要的优势是它的可重用性,就像Windows的许多控件一样,你可以将设计好的DHTML Scriptlet重用于其他的Web页面或应用程序中。DHTML Scriptlet是将组件编程的好处和动态HTML及脚本结合在一起的一种方式。
     
    IE4 4.0是微软公司从发行Visual Basic开发系统就开始的范式转移(paradigm shift)的一个例子。Visual Basic的强大功能不在于语言,而在于用Visual Basic将组件集成在一起的能力。IE4在这一点上又超出了Visual Basic,它不仅将组件集成在了一起,更加上网络和Windows环境。所以,在这个意义上,IE4是COM范式的一个自然进化:重用组件,而不是库。
     
    在一个Web站点的建设中,通常会有很多相同或相似的Web页面成分(HTML成分或脚本成分),做重复工作是一件非常令人讨厌的事情,一般情况下,总是尽可能地重用这些页面成分。而为了重用这些页面成分,Web作者经常是剪切、粘贴,可能还要加以适当的修改。而用DHTML Scriptlet,Web作者只需要将这些Web页面成分封装为一个Scriptlet,定义好需要呈现(expose)给用户的属性和方法,那么,其他的Web页面或应用程序就可以重用这些定义好的DHTML Scriptlet了。通过Scriptlet呈现的属性和方法,其他Web作者不需要理解实现细节,就可以根据自己的需要定制这些Scriptlet。
     
    在Web页面中使用DHTML Scriptlet,还可以加快页面的下载速度。因为DHTML Scriptlet只包含HTML和脚本,与ActiveX控件和Java Applet比较起来,是非常轻的,所以能够很快下载并运行。另外,DHTML Scriptlet下载到客户端后,就会缓存起来,这样,下次再浏览到包含该DHTML Scriptlet的页面时,就可以不用从网上下载,而直接从本机缓存中调用,从而加快了页面的下载速度。
    November 05

    关于网站的一些基本概念

    域名、IP地址、虚拟主机

    在我们的实际生活中,不管是人,还是各种各样的单位(如医院、学校、工厂、公司、村庄等等),都会有一个名字,作为这个人(或单位)的标识。同样,在数字空间中,为了标识某个数字化的实体,也需要一个名字,这个名字就是域名(Domain name)。

    域名例子:www.levensoft.com,ftp.levensoft.com,mail.levensoft.com。

    这些域名由英文字母组成并以点号(.)分隔,使人一看就明白是什么意思,但机器却无法懂得其中的意义,为了使得机器能够明白,人们又发明了IP地址。所以,域名是面向人的,而IP地址是面向机器的,对于非专业人士,不必去了解IP地址是什么意思。

    IP地址例子:61.129.81.66,192.168.0.150,127.0.0.1。

    域名和IP地址都是一种资源,而且具有唯一性。由于这些特性,所以域名必须经过注册,IP地址必须经过分配,才具有合法性,才能够在数字世界畅通无阻地使用。

    前面已说明,域名是面向人的,对机器无意义,而IP地址是面向机器的,对人(非专业人士)无意义。也就是说,人使用域名,而机器使用IP地址,这是两个各自说着不同语言的不同的世界(层面),如何使得这两个世界相通呢?方法是在域名和IP地址之间建立起对应关系,建立此对应关系的过程叫“域名解析”。

    每一个域名都表示一个主机系统,如www.levensoft.com表示立文软件公司的网站,mail.levensoft.com表示立文软件公司的邮件系统,等等。这些主机系统指的都是软件系统,既可以都存在于一台物理的机器上,也可以分开存在于不同的物理机器上,但对于用户而言,并无区别。所以,即使在一台机器上存在着成百上千个主机系统,对这些主机系统的用户而言,就像它们每一个都存在于自己的物理机器上一样。对这些存在于一台机器上的主机系统,我们称之为“虚拟主机(系统)”。

    域名与IP地址的对应关系是一种“多对一”的关系,也就是说,多个域名都可以对应于同一个IP地址,如www.levensoft.com -- 61.129.81.66,mail.levensoft.com -- 61.129.81.66,等等,所以,www.levensoft.com、mail.levensoft.com就都叫做61.129.81.66这台机器上的虚拟主机。

    网站、网页、栏目(频道)

    简单地说,网站就是由一些网页组成的一个可由浏览器访问的信息系统。实际上,现代网站的组成越来越复杂,尤其对于大型网站,往往是由几十台甚至几百台服务器组成的。人们为了便于把握一个复杂的网站系统,把网站从逻辑上分为三层:

    表示层:呈现在用户面前的HTML页面组成了网站的表示层,是网站中直接面向使用者的部分。

    应用层:中间用来完成复杂应用功能的程序模块。

    数据层:系统后端用来存储数据的逻辑层,也复杂与其他系统的接口。

    所以,一个网站可以很简单,只由几个页面组成,也可以非常复杂,由几百台服务器组成一个庞大复杂的信息系统。

    不管一个网站如何复杂,其复杂性主要体现在后两层(及应用层和数据层),体现在用户面前的总是由HTML语言所描述的页面(网页)。

    就像报纸有版面,书籍有章节一样,为了便于管理及访问网站内容,又将网站划分为若干个栏目(也叫频道),每一个栏目可以由多个网页组成,这些网页共同组成一个内容上相对独立的子系统,以反映网站的某一个方面。例如:一般的企业网站都有“关于我们”、“产品信息”、“业界动态”等等,虽然具体名称可能不同,但每一个栏目都反映了一个企业的某一个侧面,这些栏目共同组成了该企业的网站,也全面地描述了该企业。

    网址、链接地址

    网址是一个日常用语,概念比较含糊,一般是指域名,如说某网站的网址是什么,此时即指该网站的域名。但这样说并不确切,因为不是所有网站都有独立域名,没有独立域名的网站,其网址一般体现为一个链接地址(URL -- Universal Resource Locator)。

    如prowyh在MSN Spaces上的博客地址是一个域名:prowyh.spaces.live.com,而在新浪的博客地址blog.sina.com.cn/u/1181301981就只是一个链接地址。

    链接地址(URL)是一个更技术化的术语,也是一个更通用的概念。上述的域名标识的是一个网站,无法标识组成网站的网页,而URL既包括了域名的概念,也可以用来标识组成网站的页面,如www.levensoft.com/default.asp标识的就是www.levensoft.com网站根目录下的default.asp文件(页面)。

    October 21

    Haha......that's great!

    以前对服务器的远程控制与管理一直是通过Symantec PcAnywhere进行的,但PcAnywhere 10.0与Windows Server 2003不太兼容(可以运行,但经常出问题,如Windows远程桌面无法登录,File Transfer经常使得本地电脑处于死循环,而且运行PcAnywhere被控端的服务器经常无法显示桌面!),所以一直想卸掉,而改用Windows的远程桌面(Remote Desktop),但一直怕在卸载的过程中出问题,使得服务器失去控制,从而又不得不跑到机房。一想到跑机房的麻烦,就不得不忍忍吧,忍忍吧……

    服务器一旦出现无法显示桌面的情况,就得重启服务器,久而久之,则不胜其烦,再说一台用于生产环境下的服务器,老是重启也不是办法!今天终于横下一条心:宁肯服务器失去控制,也得把PcAnywhere卸掉!

    打开控制面板的“添加与删除程序”,找到Symantec PcAnywhere,怀着十分忐忑的心情,按下了“删除”按钮……正在收集应用程序信息……连接断开……PcAnywhere主控端关闭……打开Windows的远程桌面……登录……卸载完毕,需要重启系统,请按“确定”重启您的计算机……确定……重启……远程桌面连接关闭……等一会儿,重新登录……出现Windows桌面……PcAnywhere卸载成功!

    Oh, my God! My heart is putting down to her original place...........

    呵呵,您看到这里,可能会鄙夷:至于吗?我要说:非常“至于”……

    试想:

    1、如果在卸载过程中,PcAnywhere需要进一步的人工干预,而此时主控端已经失去了到服务器的连接!

    2、如果卸载程序在卸载过程出现问题,而没有完成卸载工作,此时PcAnywhere已经无法登录(因为一些程序已经被卸载),而Windows远程桌面也无法登录(影响Windows远程桌面无法登录的PcAnywhere中的那个模块恰恰没有被卸载!Windows Remote Desktop无法在运行PcAnywhere 10.0的机器上登录是因为PcAnywhere的登录模块截获了WinLogon)!

    这两种情况只要出现任何一种,此次在线远程卸载PcAnywhere主控端的工作即告失败!不得不等到下周去机房!

    所幸的是,这两种情况都没有出现,卸载工作干净利落地完成!行文至此,不得不对Symantec心生敬意!

    这……就是绅士风度!

    男人的绅士风度让女孩子心仪,说明该男人有着良好的家教与修养!软件的绅士风度让所有用户心生敬意,说明该软件的开发商有着良好的职业道德和以人为本的企业理念!

    相比于国内许多公司的流氓行为,Symantec让我们举手加额……

    那么,什么是软件的绅士风度呢?我以为,有如下几点:

    1、易于安装

    安装对于软件一直是一个很大的问题,因为软件面临的运行环境多种多样,既有比较清洁的系统,也有装了许多乱七八糟软件的“被污染”了的系统。绅士软件在安装时就要仔细检测系统环境,并自动作出相应处理。

    软件安装时还要尽量减少用户的人工干预。所有用户都很厌烦需要作出许多选择的软件安装过程,软件安装应尽量做到自动化,尽量减少用户的人工干预。

    2、易于卸载

    如果不想用该软件了,用户应该很容易地将该软件卸载掉。易于卸载一方面是指干干净净地将软件从系统中删除,包括自动stop service,自动删除安装时写入系统注册表中的信息,删除硬盘中的软件安装目录,不在系统中留下任何垃圾。另一方面是尽量减少用户的人工干预,一切卸载动作都是自动进行的。

    3、运行中的降级与日志

    正如如上安装所述,软件的运行环境多种多样,难免遇到不能正常运行的情况,此时一方面进行适当的降级(degrade),如果仍然不能运行,就应该“礼貌地”退出。这需要对所有的API调用进行状态检测,遇到问题,进行适当处理。亦即软件的运行不能失去控制(out of control)。

    另外就是在遇到问题时,尽可能地产生系统日志,记录问题出现的状况,以便用户能够进行troubleshooting。

    就像真正的绅士难得遇到一样,能够满足这几点要求的软件也是凤毛麟角,更多地是一些“半生不熟”的东西……

    1、QQ

    QQ大概有近10年的历史了,仍然是“半生不熟”,至少其卸载就在用户的电脑中留下了大量的垃圾。这个问题如果真正想去解决,其实并不难。

    2、CNNIC中文上网软件

    曾几何时,同事请求帮助,原因是无法正常使用!原来是CNNIC的中文上网软件!同事不知怎么不小心(由于不是IT Professional),在重装Windows系统时,竟然中间冒出来CNNIC的中文上网软件,该软件弹出一个对话框:选择“确定”安装软件,选择“取消”则取消软件安装。问题是选择确定也不行,选择取消也不行!徒唤奈何?最后没办法,建议同事reformat disk!

    还有一事,也事关CNNIC。客户不知从哪里知道的,告之要注册中文域名!所谓中文域名,即类似“立文软件.中国”或“立文软件.cn”之类的域名。客户的要求又不好不满足,只好为其注册了所要求的中文域名。中文域名从市场角度而言,是源自于3721的成功!但从技术角度观之,中文域名,就像中文程序设计语言一样,是一种半通不通、半男半女(从而非男非女)的东西,极其可笑!

    关于中文域名,将另文讨论。

    最后,再一次向Symantec表示敬意!