用心做人,用心做事!

发布新日志

  • 如何让Apache支持ASP与ASP.NET

    2010-12-16 11:35:22

    前言:本人用了phpnow、apm、WampServer等大部分集成Apache、php、mysql的软件,想完全的支持asp,都没有做成功,但是有相关的资料,和大家分享。有一个插件ActiveHTML支持asp,但是我试了,也不好用,总之太麻烦了,所以后来还是用的IIS+php+mysql(请看我的本分类中的另一篇文章)。如果不使用asp当然就非常容易了,而且很好用,但是我还是想使用asp,折腾了一天多,还是没戏,能运行,但是有的只支持一个asp网站,有的有其它问题,郁闷啦!不玩了!如果哪位高手有好的方法使之完美支持asp,请告知,不胜感激!

    注解:
    本文是基于WampServer 2.X套件讨论的,但适用于所有的Apache。其实我们也只是对WampServer中的Apache模块做了修改,使其能够支持ASP/ASP.NET。本文中Apache版本为2.2.8,WampServer 的目录地址为D:\wamp\ 。文中提及的配置文件.conf 及其mod,ActiveHTML,于文末提供下载。

    ASP.NET
    Apache支持ASP.NET比较简单,使用apache.org里提供的mod_aspdotnet即可。地址是:http://httpd.apache.org/modules/ 。

        说一下配置:首先下载mod_aspdotnet 。然后将其mod_aspdotnet.so释放到apache 的/modules/ ,如D:\wamp\bin\apache\apache2.2.8\modules 。然后,我们需要建立一个mod_aspdotnet 单独的配置文件.conf,如httpd-aspdotnet.conf,写好后放入apache的/conf/extra/中,这样比较规范。httpd-aspdotnet.conf,如下

    XML/HTML代码

    1. # Load asp.net module   

    2. LoadModule aspdotnet_module "modules/mod_aspdotnet.so"   

    3.    

    4. # Set asp.net extensions     

    5. AddHandler asp.net asax ascx ashx asmx aspx axd config cs csproj licx rem resources resx soap vb vbproj vsdisco webinfo   

    6.    

    7. # 其中"D:/wamp/www/aspx" 为我们的ASPX的文件的目录。需自己修改。:)  

    8. <IfModule mod_aspdotnet.cpp>   

    9.    # Mount the ASP.NET /ASP application   

    10.   AspNetMount /ASP "D:/wamp/www/aspx"   

    11.     

    12.   # Map all requests for /ASP to the application files   

    13.   Alias /ASP "D:/wamp/www/aspx"   

    14.     

    15.   # Allow asp.net scripts to be executed in the /ASP folder   

    16.   <Directory "D:/wamp/www/aspx">  

    17.     # Set asp.net options  

    18.     Options Indexes FollowSymLinks Includes +ExecCGI  

    19.     # Set asp.net permissions     

    20.     Order allow,deny   

    21.     Allow from all   

    22.     # Set asp.net default index page to .aspx and .htm   

    23.     DirectoryIndex index.aspx index.htm   

    24.    </Directory>   

    25.   

    26.   # Set aspnet_client files to serve the client-side helper scripts. 这里为我们的ASP.NET_Framework的安装地址,需自己修改。  

    27.   AliasMatch /aspnet_client/system_web/(\d+)_(\d+)_(\d+)_(\d+)/(.*) "C:/WINDOWS/Microsoft.NET/Framework/v3.5/ASP.NETClientFiles/$4"   

    28.   <Directory "C:/WINDOWS/Microsoft.NET/Framework/v3.5/ASP.NETClientFiles">   

    29.     Options FollowSymlinks   

    30.     Order allow,deny   

    31.     Allow from all   

    32.   </Directory>   

    33. </IfModule>   

    这样写好httpd-aspdotnet.conf后放入apache的/conf/extra/中,然后修改apache的主配置文件,/conf/httpd.conf 在其中适当位置加入代码

    XML/HTML代码

    1. # ASP.net ,写在httpd.conf 最后适当的位置。 只是为了规范。

    2. Include conf/extra/httpd-aspdotnet.conf

    然后,重启apache,现在/www/aspx/目录已经可以解析.aspx文件了。:)

    ASP

        让Apache完美的支持asp,目前网上也没有好的解决办法,很多东西都还是商业的,以前有个mod可以用,但只能用在apache1.X上,很老的东西了。 我找寻了很久也没有免费的解决办法。无意间得知phpnow有个插件可以支持asp,马上下载下来玩了一下,终于成功让这个插件脱离phpnow,使其支持任意的Apache。其实phpnow也是提取了试用版的ActiveHTML,效果还不错,我比较满意。

            过程如下:首先下载这个phpnow版的ActiveHTML,释放在一个适合的位置,如D:\wamp\bin\ActiveHTML\ ,ActiveHTML需要注册几个.dll,进入CMD在ActiveHTML目录,执行如下命令

    XML/HTML代码

    1. # 进入CMD在ActiveHTML目录,执行如下命令

    2. regsvr32.exe /s slASP3.dll  

    3. regsvr32.exe /s slDispatch.dll  

    4. regsvr32.exe /s MSXML4.dll  

    5. regsvr32.exe /s MSXML4R.dll

    然后,同样的我们需要为ActiveHTML写一个配置文件.conf,如httpd-ahtml.conf,如下

    XML/HTML代码

    1. ##################################################  

    2. #   Uncomment the following lines if you want  

    3. #   to use Authorization environment-variables  

    4. #   You may implement you own user   

    5. #   authentication using LOGON_USER and  

    6. #   LOGON_PASSWORD in your scripts (Login.asp)  

    7. ##################################################  

    8. LoadModule rewrite_module modules/mod_rewrite.so  

    9. RewriteEngine On  

    10. RewriteCond     %{HTTP:Authorization}   ^(.*)$ [NC]  

    11. RewriteRule     /.*             -       [E=HTTP_AUTHORIZATION:%1]  

    12. ##################################################  

    13.   

    14. # Supports Imagemaps  

    15. AddHandler imap-file map  

    16.   

    17. ##################################################  

    18. #   Do not allow access to global.asa  

    19. ##################################################  

    20. <Files ~ "global.asa">  

    21. Order allow,deny  

    22. Deny from all  

    23. Satisfy All  

    24. </Files>  

    25.   

    26.   

    27. ##################################################  

    28. #   Add ActiveHTML-Handler 其中”D:/wamp/bin/ActiveHTML“ 为ActiveHTML释放的目录,需自己修改。

    29. ##################################################  

    30. ScriptAlias /asp_bin "D:/wamp/bin/ActiveHTML"  

    31. Action ActiveHTML "/asp_bin/AHTML.exe"  

    32. AddHandler ActiveHTML .asp  

    33. DirectoryIndex index.asp default.asp  

    34.   

    35.   

    36. ##################################################  

    37. #   Add Sampledirectory Alias 其中"D:/wamp/www/asp/"为asp文件所在的目录,需自己修改。

    38. ##################################################  

    39. <Directory "D:/wamp/bin/ActiveHTML">  

    40. Options Indexes MultiViews FollowSymLinks  

    41. AllowOverride None  

    42. Order allow,deny  

    43. Allow from all  

    44. </Directory>  

    45.   

    46. Alias /asp/ "D:/wamp/www/asp/"  

    47. <Directory "D:/wamp/www/asp/">  

    48. Options Indexes MultiViews FollowSymLinks  

    49. AllowOverride all  

    50. Order allow,deny  

    51. Allow from all  

    52. </Directory>  

    同样的,在apache的主配置文件httpd.conf 里适当的位置加入代码

    XML/HTML代码

    1. # ASP ,写在httpd.conf 最后适当的位置。 只是为了规范。

    2. Include conf/extra/httpd-ahtml.conf

    然后,重启apache,现在/www/asp/目录已经可以解析.asp文件了。:)

    PS

    文中提及的配置文件.conf 及其mod,ActiveHTML,下载:extra.rar

    其它的方法:

    ==================Apache支持ASP===================

    前言:
    好多人都在寻找apache下支持asp的方法,有的无非要建两个端口,通过代理实现支持asp,执行效率不是太好,现在终于有一种新的办法,通过模块实现支持asp,简单,直接.其实这个方法以前就有过,不过说的很含糊,很多地方需要更正,而且年久失传,原有的地址都找不到了,我翻遍了搜索引擎,终于在一个台湾的论坛找到了那个组件,自己亲自调试了下,现在把自己的成功经验与落伍的兄弟们分享.

    ①首先下载一个组件,这个是关键,也就是好多人寻找的东西

    下载: http://mis.enc.hlc.edu.tw/upload/files/openasp-b1-win32.zip
    ②下载完apasp.DLL后,放置在apahce的modules文件夹,在httpd.conf的最下面(一定要在最下面,不然就没效果了)加入这一行:

    LoadModule asp_module modules/apasp.DLL

    ③在httpd.conf中 <IfModule mod_mime.c>和</IfModule>之间的某个合适位置加入

    AddType text/x-asp .asp

    其实最好在AddType application/x-httpd-php .php下方加入,这样apache就能解释asp扩展名的文件了.

    重新启动下apache,放个探针试下,是不是支持asp了?是不是很简单?

    我的apache版本是1.33,这个组件好象只支持1.3的版本.

  • dos、vbs相关

    2010-05-06 12:13:39

    一、给注册表编辑器解锁
      用记事本编辑如下内容:
    DIM WSH
    SET WSH=WSCRIPT.CreateObject("WSCRIPT.SHELL") '击活WScript.Shell对象
    WSH.POPUP("解锁注册表编辑器!")
    '显示弹出信息“解锁注册表编辑器!”
    WSH.Regwrite"HKCU\Software\Microsoft\Windows\CurrentVersion
    \Policies\System\DisableRegistryTools",0,"REG_DWORD"
    '给注册表编辑器解锁
    WSH.POPUP("注册表解锁成功!")
    '显示弹出信息“注册表解锁成功!”
    保存为以.vbs为扩展名的文件,使用时双击即可。
      二、关闭Win NT/2000的默认共享
      用记事本编辑如下内容:
    Dim WSHShell'定义变量
    set WSHShell=CreateObject("WScript.shell") '创建一个能与操作系统沟通的对象WSHShell
    Dim fso,dc
    Set fso=CreateObject("Scripting.FileSystemObject")'创建文件系统对象
    set dc=fso.Drives '获取所有驱动器盘符
    For Each d in dc
    Dim str
    WSHShell.run("net share"&d.driveletter &"$ /delete")'关闭所有驱动器的隐藏共享
    next
    WSHShell.run("net share admin$ /delete")
    WSHShell.run("net share ipc$ /delete")'关闭admin$和ipc$管道共享
      现在来测试一下,先打开cmd.exe,输入net share命令就可以看到自己机子上的共享。双击执行stopshare.vbs后,会看见窗口一闪而过。然后再在cmd里输入net share命令,这时候没有发现共享列表了
      三、显示本机IP地址
      有许多时候,我们需要知道本机的IP地址,使用各种软件虽然可以办到,但用VBS脚本也非常的方便。用记事本编辑如下内容:
    Dim WS
    Set WS=CreateObject("MSWinsock.Winsock")
    IPAddress=WS.LocalIP
    MsgBox "Local IP=" & IPAddress
      将上面的内容保存为ShowIP.vbs,双击执行即可得到本机IP地址。
      四、利用脚本编程删除日志
      入侵系统成功后黑客做的第一件事便是清除日志,如果以图形界面远程控制对方机器或是从终端登陆进入,删除日志不是一件困难的事,由于日志虽然也是作为一种服务运行,但不同于http,ftp这样的服务,可以在命令行下先停止,再删除,在命令行下用net stop eventlog是不能停止的,所以有人认为在命令行下删除日志是很困难的,实际上不是这样,比方说利用脚本编程中的VMI就可以删除日志,而且非常的简单方便。源代码如下:
    strComputer= "."
    Set bjWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate,(Backup)}!\\" & _
    strComputer & "\root\cimv2")
    dim mylogs(3)
    mylogs(1)="application"
    mylogs(2)="system"
    mylogs(3)="security"
    for Each logs in mylogs
    Set colLogFiles=objWMIService.ExecQuery _
    ("Select * from Win32_NTEventLogFile where LogFileName='"&logs&"'")
    For Each objLogfile in colLogFiles
    objLogFile.ClearEventLog()
    Next
    next
      将上面的代码保存为cleanevent.vbs文件即可。在上面的代码中,首先获得object对象,然后利用其clearEventLog()方法删除日志。建立一个数组,application,security,system,如果还有其他日志也可以加入数组。然后用一个for循环,删除数组中的每一个元素,即各个日志。
      五、利用脚本伪造日志
      删除日志后,任何一个有头脑的管理员面对空空的日志,马上就会反应过来被入侵了,所以一个聪明的黑客的学会如何伪造日志。利用脚本编程中的eventlog方法创造日志非常简单,请看下面的代码:
    set ws=wscript.createobject("Wscript.shell")
    ws.logevent 0 ,"write log success" '创建一个成功执行日志
      将上面的代码保存为createlog.vbs即可。这段代码很容易理解,首先获得wscript的一个shell对象,然后利用shell对象的logevent方法。logevent的用法:logevent eventtype,"description" [,remote system],其中eventtype为日志类型,可以使用的参数如下:0代表成功执行,1执行出错,2警告,4信息,8成功审计,16故障审计。所以上面代码中,把0改为1,2,4,8,16均可,引号中的内容为日志描述。利用这种方法写的日志有一个缺点,即只能写到应用程序日志,而且日志来源只能为WSH,即Windows Scripting Host,所以不能起太多的隐蔽作用,在此仅供大家参考。
      六、禁用开始菜单选项
      用记事本编辑如下内容:
    Dim ChangeStartMenu
    Set ChangeStartMenu=WScript.CreateObject("WScript.Shell")
    RegPath="HKCR\Software\Microsoft\Windows\CurrentVersion\Policies\"
    Type_Name="REG_DWORD"
    Key_Data=1
     
    StartMenu_Run="NoRun"
    StartMenu_Find="NoFind"
    StartMenu_Close="NoClose"
     
    Sub Change(Argument)
    ChangeStartMenu.RegWrite RegPath&Argument,Key_Data,Type_Name
    MsgBox("Success!")
    End Sub
     
    Call Change(StartMenu_Run) '禁用“开始”菜单中的“运行”功能
    Call Change(StartMenu_Find) '禁用“开始”菜单中的“查找”功能
    Call Change(StartMenu_Close) '禁用“开始”菜单中的“关闭系统”功能
      将以上代码保存为ChangeStartMenu.vbs文件,使用时双击即可。
      七、执行外部程序
      用记事本编辑如下内容:
    DIM objShell
    set bjShell=wscript.createObject("wscript.shell")
    iReturn=objShell.Run("cmd.exe /C set var=world", 1, TRUE)
      保存为.vbs文件即可。在这段代码中,我们首先设置了一个环境变量,其名为var,而值为world,用户可以使用%Comspec%来代替cmd.exe,并且可以把命令:set var=world改成其它的命令,这样就可以使它可以运行任意的命令。
      八、重新启动指定的IIS服务
      用记事本编辑如下内容:
    Const ADS_SERVICE_STOPPED = 1
    Set bjComputer = GetObject("WinNT://MYCOMPUTER,computer")
    Set bjService = objComputer.GetObject("Service","MYSERVICE")
    If (objService.Status = ADS_SERVICE_STOPPED) Then
    objService.Start
    End If
      将它以startsvc.vbs为名保存在C盘根目录。并通过如下命令执行:cscript. c:\startsvc.vbs。运行后,经你指定的IIS服务项将被重新开启。
      最后,我们再说说开篇时提到的VBS脚本病毒的防范方法。VBS病毒的执行离不开WSH,在带给人们便利的同时,WSH也为病毒的传播留下可乘之机。所以要想防范VBS病毒,可以选择将WSH卸载,只要打开控制面板,找到“添加/删除程序”,点选“Windows安装程序”,再鼠标双击其中的“附件”一项,然后再在打开的窗口中将“Windows Scripting Host”一项的“√”去掉,然后连续点两次“确定”就可以将WSH卸载。或者,你也可以点击“我的电脑”→“查看”→“文件夹选项”,在弹出的对话框中,点击“文件类型”,然后删除VBS、VBE、JS、JSE文件后缀名与应用程序的映射,都可以达到防范VBS脚本病毒的目的。
  • QTP 图片验证 图片比较 二进制流对比法

    2009-11-14 14:49:14

    图片验证主要是文件对比,其中我们可以利用二进制的方法读取图片信息,然后进行对比,达到对比的效果,本例子利用fso对象的文件流的方法实现,代码如下

    Public Function CompareFiles (FilePath1, FilePath2)
       Dim FS, File1, File2
       Set FS = CreateObject("scrīpting.FileSystemObject")

       If FS.GetFile(FilePath1).Size <> FS.GetFile(FilePath2).Size Then
             CompareFiles = True
             Exit Function
       End If

       Set File1 = FS.GetFile(FilePath1).OpenAsTextStream(1, 0)
       Set File2 = FS.GetFile(FilePath2).OpenAsTextStream(1, 0)

       CompareFiles = False
       Do While File1.AtEndOfStream = False
             Str1 = File1.Read(1000)
             Str2 = File2.Read(1000)

             CompareFiles = StrComp(Str1, Str2, 0)

             If CompareFiles <> 0 Then

                      CompareFiles = True

                      Exit Do
             End If
       Loop

       File1.Close()
       File2.Close()
    End Function

    返回值:
    如果两个文件相同,该功能将返回 0 或 False,否则将返回 True。

    示例:
    File1 = "C:\1.jpg"
    File2 = "C:\2.jpg"


    If CompareFiles(File1, File2) = False Then
       MsgBox "Files are identical."
    Else
       MsgBox "Files are different."
    End If

  • GetTextLocation —— 依据文本值动态获取坐标进行点击、拖拽操作

    2009-10-23 21:23:53

    在实际工作中我们经常会遇到对一些不能被捕获的对象进行点击、拖拽操作,直接去录制可能得到的就是Object.Click xxx,yyy ,这样的代码健壮性和可维护性都非常的差。
    如果需要被操作的位置上有相对唯一的文本值,那么就可以考虑使用GetTextLocation方法来辅助我们来动态定位到预期的坐标,这样就不会因为界面位置一点点的变动导致脚本运行失败了。

    先在帮助文档中查找GetTextLocation方法看看,我们会惊奇的发现几乎所有的对象都能支持该方法,这就使得该方法的应用范围会比较广,帮助文档中的描述如下:
    Syntaxobject.GetTextLocation (TextToFind, Left, Top, Right, Bottom, [MatchWholeWordOnly])


    Syntax Details
    Argument Description
    TextToFind Required. A String value. The text string you want to locate.
    Left Required. A Variant value. The left coordinate of the search area within the window or screen.
    Top Required. A Variant value. The top coordinate of the search area within the window or screen.
    Right Required. A Variant value. The right coordinate of the search area within the window or screen.
    Bottom Required. A Variant value. The bottom coordinate of the search area within the window or screen.
    Note: Set the Left, Top, Right, and Bottom coordinates to -1 to search for the text string within the object’s entire window.
    MatchWholeWordOnly Optional. A Boolean value. If True, the method searches for occurrences that are whole words only and not part of a larger word. If False, the method does not restrict the results to occurrences that are whole words only. Default value = True


    下面我们来看一个运用GetTextLocation的实例

    先简单的描述下需求,现在需要点击自定义的菜单栏上的"Generate Report"按钮,但按钮控件不能被捕获,只能识别到整个菜单栏对象为VbWindow("Window").WinObject("Menu"),点击按钮的代码如下:

    CODE:

    '获取"Generate Report"文本在WinObject("Menu")中的坐标范围,并返回给L(left),T(top),R(right),B(bottom)
    VbWindow("Window").WinObject("Menu").GetTextLocation strText,L,T,R,B,True
    '点击该文本所在坐标区域的正中心位置
    VbWindow("Window").WinObject("Menu").Click (L+R)/2, (T+B)/2
    为了便于使用,我们也可以进行简单的封装,例如:

    CODE:

    Function  ClickText(objOB, strText)
       If objOB.Exist(1) Then
         objOB.GetTextLocation strText,L,T,R,B,True
         objOB.Click (L+R)/2, (T+B)/2
       Else
         Reporter.ReportEvent micFail , "Can not find the specified object!", ""
       End If
    End Function

    ClickText VbWindow("Window").WinObject("Menu"),"Generate Report"
    进行拖拽操作也是一样的,通过GetTextLocation方法来获取文本中心位置坐标,动态的做为Drag和Drop的参数,这样就能比较精确的完成拖拽操作。
    下面也举个简单的例子吧

    CODE:

    Sub MoveItemByText(objFrom,strFrom,objTo,strTo)
    Dim l,r,t,b,l1,r1,t1,b1
    'Drag Item by specified text
    objFrom.GetTextLocation strFrom, l, t, r, b
    objFrom.Drag (l+r)/2, (t+b)/2, 1

    ' Drop Item on specified text
    objTo.GetTextLocation strTo, l1, t1, r1, b1
    objTo.Drop (l1+r1)/2, (t1+b1)/2, 1
    End Sub
    '将hsjzfling从Names列表中拖拽到Result列表的Champion子列表中
    Set bjAct = VbWindow("frmOvertime").ActiveX("ActiveX").ActiveX("ActiveX").ActiveX("ActiveX")
    MoveItemByText objAct.VbTreeView("Names"), "hsjzfling", objAct.VbTreeView("Result"), "Champion"
  • On Error Resume Next (On Error GoTo 0 )

    2009-10-14 13:06:05

    On Error GoTo 0
    表示禁止当前过程中任何已启动的错误处理程序。

    On Error Resume Next
    说明当一个运行时错误发生时,控件转到紧接着发生错误的语句之后的语句,并在此继续运行。访问对象时要使用这种形式而不使用 On Error GoTo。

    On Error GoTo line
    启动错误处理程序,且该例程从必要的 line 参数中指定的 line 开始。line 参数可以是任何行标签或行号。如果发生一个运行时错误,则控件会跳到 line,激活错误处理程序。指定的 line 必须在一个过程中,这个过程与 On Error 语句相同; 否则会发生编译时间错误。

    以上MSDN上的解释

    一般情况下,如果在对我们创建的对象或控件进行错误捕捉,需要使用On Error Resume Next ,然后在判断它的Err.Number,根据错误类型来做相应的处理。
    参考资料:MSDN

    On Error Resume Next
    On Error GoTo 0
    说明
    如果在您的代码中未使用 On Error Resume Next 语句, 所发生的运行时错误将显示错误信息,同时,代码的执行也随之终止。但是运行代码的主机决定了具体操作。主机有时可有选择地处理各类错误。在有些情况下,它可以在出错的地方激活脚本调试器。而在另一些情况下,由于主机无法通知用户,因此对所发生的错误没有明确说明。至于如何处理错误则完全取决于主机的功能。

    在任意一个特殊过程中,只要在调用堆栈的地方启用错误处理程序,所发生的错误一般不会是致命性的。如果在一个过程中没有启用局部错误处理程序,当发生错误时,控制可通过堆栈调用转移,直到找到一个具有错误处理程序的过程,并在出错的地方处理错误。如果在调用堆栈的过程中没有找到错误处理程序,则在出错的地方显示错误信息,同时终止代码执行,或者通过主机来正确处理错误。

    On Error Resume Next 会使程序按照产生错误的语句之后的语句继续执行,或是按照最近一次所调用的过程(该过程含有 On Error Resume Next 语句)中的语句继续运行。这个语句可以不顾运行时错误,继续执行程序,之后您可以在过程内部建立错误处理例程。在调用另一个过程时,On Error Resume Next 语句变为非活动的。所以,如果希望在例程中进行内部错误处理,则应在每一个调用的例程中执行 On Error Resume Next 语句。

    当调用另一过程时,禁止使用On Error Resume Next 语句,因此如果您想在例程中嵌入错误处理程序,则需要在每次调用例程时都应执行 On Error Resume Next 语句。当退出一个过程时,错误处理程序可恢复到它在进入所退出过程之前的状态。

    如果您已启用 On Error Resume Next 错误处理程序,则可使用 On Error GoTo 0禁用错误处理程序。

    下面例子举例说明如何使用 On Error Resume Next 语句:

    On Error Resume Next
    Err.Raise 6 ' 引发溢出错误。
    MsgBox ("Error # " & CStr(Err.Number) & " " & Err.Description)
    Err.Clear '清除该错误。  

  • Manipulate Object Repositories

    2009-10-14 11:57:32

    'The following example retrieves an object repository's objects and properties,
    'looks for specific test objects using several methods, and copies a test object
    'to another object repository.
    Dim ImageObj, PageObj, RepositoryFrom, RepositoryTo
    Set RepositoryFrom = CreateObject("Mercury.ObjectRepositoryUtil")
    Set RepositoryTo = CreateObject("Mercury.ObjectRepositoryUtil")
    RepositoryFrom.Load "C:\QuickTest\Tests\Flights.tsr"
    RepositoryTo.Load "E:\Temp\Tests\Default.tsr"

    Function EnumerateAllChildProperties(Root)
    'The following function recursively enumerates all the test objects directly under
    'a specified parent object. For each test object, a message box opens containing the
    'test object's name, properties, and property values.
        Dim TOCollection, TestObject, PropertiesCollection, Property, Msg
        Set TOCollection = RepositoryFrom.GetChildren(Root)
        For i = 0 To TOCollection.Count - 1
                Set TestObject = TOCollection.Item(i)
                Msg = RepositoryFrom.GetLogicalName(TestObject) & vbNewLine
                Set PropertiesCollection = TestObject.GetTOProperties()
                For n = 0 To PropertiesCollection.Count - 1
                    Set Property = PropertiesCollection.Item(n)
                    Msg = Msg & Property.Name & "-" & Property.Value & vbNewLine
                Next
                MsgBox Msg
    EnumerateAllChildProperties TestObject
        Next
    End Function

    Function EnumerateAllObjectsProperties(Root)
    'The following function enumerates all the test objects under a specified object.
    'For each test object, a message box opens containing the test object's name,
    'properties, and property values.
     Dim TOCollection, TestObject, PropertiesCollection, Property, Msg
        Set TOCollection = RepositoryFrom.GetAllObjects(Root)
        For i = 0 To TOCollection.Count - 1
            Set TestObject = TOCollection.Item(i)
                        Msg = RepositoryFrom.GetLogicalName(TestObject) & vbNewLine
                Set PropertiesCollection = TestObject.GetTOProperties()
                For n = 0 To PropertiesCollection.Count - 1
                    Set Property = PropertiesCollection.Item(n)
                    Msg = Property.Name & "-" & Property.Value & vbNewLine
                Next
            MsgBox Msg
        Next
    End Function

    Function RenameAllImages(Root)
    'The following function sets a new name for all image test objects under a specified object.
     Dim TOCollection, TestObject, PropertiesCollection, Property
        Set TOCollection = RepositoryTo.GetAllObjectsByClass("Image")
        For i = 0 To TOCollection.Count - 1
                Set TestObject = TOCollection.Item(i)
                RepositoryTo.RenameObject (TestObject, "Image " & i)
                RepositoryTo.UpdateObject TestObject
        Next
    End Function

    Function RemoveAllLinks(Root)
    'The following function recursively enumerates all the test objects under a specified object.
    'It looks for all test objects of class Link and removes them from their parent objects.
        Dim TOCollection, TestObject, PropertiesCollection, Property
        Set TOCollection = RepositoryFrom.GetChildren(Root)
        For i = 0 To TOCollection.Count - 1
                Set TestObject = TOCollection.Item(i)
                TOClass = TestObject.GetTOProperty("micclass")
             If TOClass = "Link" Then
                    RepositoryFrom.RemoveObject Root, TestObject
                End If
            EnumerateAllChildProperties TestObject
        Next
    End Function

    Call EnumerateAllChildProperties(Null)
    Call EnumerateAllObjectsProperties(Null)
    Call RenameAllImages(Null)
    Call RemoveAllLinks(Null)
    Set ImageObj = RepositoryFrom.GetObject("Browser(""CNN.com"").Page(""CNN.com"").Image(""Remains identified"")")
    If (Not IsNull(ImageObj)) Then
     MsgBox RepositoryFrom.GetLogicalName(ImageObj)
     Else: MsgBox "null"
    End If
    Set PageObj = RepositoryTo.GetObjectByParent("Browser(""CNN.com"")", "Page(""CNN.com"")")
    If (Not IsNull(PageObj)) Then
     MsgBox RepositoryTo.GetLogicalName(PageObj)
     Else: MsgBox "null"
    End If
    RepositoryTo.AddObject ImageObj, PageObj
    RepositoryFrom.Save
    RepositoryTo.Save
  • QTP中如何获取错误信息

    2009-10-14 11:35:52

    在执行QTP脚本的时候,如果出现错误,例如对象不识别等,如何捕捉到错误信息呢?

    Vbscript提供了Err对象
    Err.Number 返回VBScript. 错误编号或 SCODE 错误值的整数
    Err.Source 返回生成错误的应用程序
    Err.Description 返回包含错误说明的字符串表达式

    可以运行以下代码,查看运行结果中三个属性的返回值是什么

    1. On Error Resume Next
    2. Err.Raise 6   ' 引发溢出错误。
    3. MsgBox ("Error # " & CStr(Err.Number) & " " & Err.Description & Err.Source)
    4. Err.Clear   ' 清除该错误
  • GetTOProperty、GetROProperty、GetTOProperties的区别

    2009-08-21 12:28:55

    GetTOProperty和GetROProperty都用于取出对象的某个具体属性的值,它们的语法和用法几近相同,唯一的区别就是GetTOProperty取出的是录制对象的属性值,而GetROProperty取出的是在回放过程中运行的对象的属性值.
    GetTOProperties,是指取出录制对象的所有属性。
    GetTOProperty和GetROProperty语法都是:
    object.GetTOProperty(Property)
    或者是:
    object.GetROProperty(Property)
    其中Property,对象的属性,是必填项.
    GetTOProperties的语法为:
    object.GetTOProperties
    实例:
    Dialog("Login").Activate
    Dialog("Login").WinEdit("Agent Name:").Set "training"
    Dialog("Login").WinEdit("Agent Name:").Type  micTab
    Dialog("Login").WinEdit("Password:").SetSecure "429376f06698f739df8bb1c09eab3d78dee316f7"
    Set TableDesc = Dialog("Login").WinEdit("Password:").GetTOProperties
    Properties=TableDesc.Count
    reporter.ReportEvent micdone,"属性数目",Properties
    If Dialog("Login").WinEdit("Password:").GetROProperty("attached text")="Password:" then
    reporter.ReportEvent micdone,"name控件","存在"
    else
    reporter.ReportEvent micdone,"name控件","不存在"
    end if
    If Dialog("Login").WinEdit("Password:").GetTOProperty("attached text")="Password:" then
    reporter.ReportEvent micdone,"name控件2","存在"
    end  if
    Dialog("Login").WinButton("OK").Click
    'did the flight reservation window appear?
    Window("Flight Reservation").Check CheckPoint("Flight Reservation")
    Set Mydes=Descrīption.Create()
    Mydes("attached text").value="Name:"
    Mydes("nativeclass").value="Edit"

    Set winedites=Window("Flight Reservation").ChildObjects(Mydes)
    NoOfChildObjs =winedites.Count
    For Counter=0 to NoOfChildObjs-1
    if(Counter=0) then
           winedites(Counter).Set "ON"
      end if
    Next
  • getElementById 方法及用法

    2009-08-15 15:57:57

    顾明思义,get-Element-By-Id,就是通过ID来设置/返回HTML标签的属性及调用其事件与方法。用这个方法基本上可以控制页面所有标签,条件很简单就是给每个标签分配一个ID号:

    document.getElementById("link").href;
    document.getElementById("link").target;
    document.getElementById("img").src;
    document.getElementById("img").width;
    document.getElementById("img").height;
    document.getElementById("input").value;

    那么如何取得<div></div>以及<a></a>之间的值呢?如<div id="div">aaa</div>中的aaa,<a href="#" id="link">bbb</a>中的bbb,也很简单,利用innerHTML就可以了:

    document.getElementById("div").innerHTML;
    document.getElementById("link").innerHTML;


    getElementById 方法
    返回具有指定 ID 属性值的第一个对象的一个引用。

    语法
    oElement = document.getElementById(sIDValue)

    参数
    sIDValue 必选项。指明 ID 属性值的字符串

    返回值
    返回 ID 属性值与指定值相同的第一个对象。

    注释
    如果 ID 属于一个集合,getElementById 方法返回集合中的第一个对象。
    getElementById 方法与使用 all 集合上的 item 方法等同。例如,以下代码样本表示如何从 document 对象中取回 ID 为 oDiv 的第一个要素。

    使用 DHTML 对象模型:
    var VDiv = document.body.all.item("oDiv");
    使用文档对象模型(DOM):
    var VDiv = document.getElementById("oDiv");

    示例
    以下例子表示如何使用 getElementById 方法返回 ID 属性值 oDiv 的第一次出现。
    <script>
    function fnGetId(){
    //         Returns the first DIV element in the collection.
             var VDiv=document.getElementById("oDiv1");
    }
    </script>
    <DIV ID="oDiv1">Div #1</DIV>
    <DIV ID="oDiv2">Div #2</DIV>
    <DIV ID="oDiv3">Div #3</DIV>
    <INPUT TYPE="button" VALUE="Get Names" nclick="fnGetId()">

     

    getElementById 方法
    返回具有指定 ID 属性值的第一个比如说有个网页中有个text框的id叫text1
    getElementById(text1)就能得到这个text1框的对象,并使用text框的所有属性和方法

       

    这个是JS的一个方法,意思是通过控件ID取得元素的值,如一个form里包函text、label等,他们都是FORM的元素,有一个分配的ID,getElementById()是取得这些元素的text值的。

     

    这个是JS的一个方法,意思是通过控件ID取得元素的值,如一个form里包函text、label等,他们都是FORM的元素,有一个分配的ID,getElementById()是取得这些元素的text值的。

     

    程序举例

     

    <html>
    <head>
    <script. type="text/javascript">
    function alignRow()
    {
    var x=document.getElementById('myTable').rows
    x[0].align="right"

    }
    </script>
    </head>

    <body>
    <table width="60%" id="myTable" border="1">
    <tr>
    <td>行1 单元格1</td>
    <td>行1 单元格2</td>
    </tr>
    <tr>
    <td>行2 单元格1</td>
    <td>行2 单元格2</td>
    </tr>
    <tr>
    <td>行3 单元格1</td>
    <td>行3 单元格2</td>
    </tr>
    </table>
    <form>
    <input type="button" nclick="alignRow()" value="右对齐第一行文字">
    </form>
    </body>

    </html>
       

    支队一个单元隔进行对齐

    <html>
    <head>
    <script. type="text/javascript">
    function alignCell()
    {
    var x=document.getElementById('myTable').rows[0].cells
    x[0].align="center"

    }
    </script>
    </head>

    <body>
    <table id="myTable" border="1" width="100%">
    <tr>
    <td>单元格1</td>
    <td>单元格2</td>
    </tr>
    <tr>
    <td>单元格3</td>
    <td>单元格4</td>
    </tr>
    </table>
    <form>
    <input type="button" nclick="alignCell()" value="居中对齐第一个单元格的内容">
    </form>
    </body>   

    </html>

     

    改变colspan的值

    <html>
    <head>
    <script. type="text/javascript">
    function setColSpan()
    {
    var x=document.getElementById('myTable').rows[0].cells
    x[0].colSpan="2"
    x[1].colSpan="6"
    }
    </script>
    </head>

    <body>
    <table id="myTable" border="1">
    <tr>
    <td colspan="4">单元格1</td>
    <td colspan="4">单元格2</td>
    </tr>
    <tr>
    <td>单元格3</td>
    <td>单元格4</td>
    <td>单元格5</td>
    <td>单元格6</td>
    <td>单元格7</td>
    <td>单元格8</td>
    <td>单元格9</td>
    <td>单元格10</td>
    </tr>
    </table>
    <form>
    <input type="button" nclick="setColSpan()" value="改变colspan值">
    </form>
    </body>

    </html>

  • QTP判断对象是否存在

    2009-08-15 15:50:03

    1.      判断getElementById("")获取的对象存不存在

    方法一:可以通过错误处理来实现

    On  Error  Resume  Next    

         getElementById("")的操作

           (注意:如果是用set bj = object. getElementById("") 时,即使object. getElementById("")为空,也会返回一个nothing值,因此,Err.Number会等于0。因此如果是直接判断对象的话,还需要和is nothing配合使用。)

          而如果直接是对象的相关操作的话,就不用和is nothing结合使用了。如使用value =object. getElementById("")如果对象不存在,Err.Number就会不等于零了,因此可以直接判断了。

    On  Error  GoTo 0

    If  Err.Number  <>  0  Then 

       处理

    Err.Clear

    End If  

          方法二:用is nothing来判断

          注意,不能用is not nothing,否则如果对象为空则会报错。

          If object.getElementById("") is nothingThen

              处理

     

          End If

    方法三:用Erris nothing结合来判断

    最好是编写独立的函数,这样有关Err的配置就不会影响到大程序中有关Err的设置了。

    可以用Err来保证判断不会报脚本错误,用is nothing来判断对象不为空。

    2.      QTP内置支持的对象存不存在判断

    对于QTP内置支持的对象(如,Browser不能使用is nothing来判断是否存在,因为即使对象不存在,也会返回一个值的。

    例:

    Set bjBrowser = Browser(“name:=不存在”)

    即使Browser是不存在的,也会返回一个“CoBrowser”对象,这时objBrowser就不会等于nothing了。因此objBrowser is nothing的值会永远为false的。达不到判断对象是否存在的效果。

    解决方法:使用Exist,如下:

          Dim chkBrowser

          chkBrowser = Browser(“name:=不存在”).Exist(0)

          If chkBrowser then

                …..

          End if

     

    [注] 当然可以加一个DO...Loop 便可以实现判断对象识别是否超时


  • 低端录制和模拟录制的介绍

    2009-05-29 12:15:10

    Analog recording(模拟录制)

    模拟录制用于您要在其中录制鼠标的实际移动的应用程序。这些可能包括绘制鼠标签名或者使用通过拖动鼠标创建图像的绘图应用程序。您可以用模拟录制模式相对于屏幕或特定窗口进行录制。
       
    相对于指定窗口录制-如果对其执行操作的对象位于一个窗口内部而且该窗口在模拟录制会话期间没有移动。这可以确保在运行会话过程中,QuickTest将准确地标识在其上执行模拟步骤的窗口位置,即使当您运行模拟步骤时窗口位于不同的位置。QuickTest不会录制在指定窗口外部执行的任何单击或鼠标移动。当使用这种模式时,QuickTest不会捕获任何Active Screen图像。
       
    相对于屏幕录制-如果在其上录制模拟步骤的窗口在录制过程中移动,或者您所执行的操作是针对位于多个窗口内的对象。这可能包括将对象从一个窗口拖放到另一个窗口中。当使用这种模式时,QuickTest将捕获您在其中进行录制的窗口的最终状态的Active Screen图像。
       
    使用模拟录制录制的步骤被保存在单独的数据文件中。该文件与在其中录制模拟步骤的操作或组件一起存储。
       
    当以模拟录制模式进行录制时,QuickTest将向测试或组件添加一个调用所录制的模拟文件的“RunAnalog”语句。相应的Active Screen将显示在模拟录制会话期间执行的最后一个模拟步骤的结果。
    low-level recording(
    低级录制)
       
    使用低级录制在不受QuickTest支持的环境或对象上进行录制。当您需要在应用程序屏幕上录制操作的精确位置时,请使用低级录制。当以正常模式录制时,QuickTest将在对象上执行步骤,即使该对象已经移动到屏幕上的新位置。如果对象的位置对于您的测试或组件非常重要,请切换到低级录制以使QuickTest能够按照屏幕上的x坐标和y坐标录制该对象。这样,该步骤只有在对象位于正确的位置时才能通过。

       
    当使用低级录制时,QuickTest将所有父类对象录制为Windows测试对象,将所有其他对象录制为WinObject测试对象。它们在Active Screen中显示为标准Windows对象。低级录制对每个测试对象支持下列方法:
    WinObject测试对象- ClickDblClickDragDropType
    Window
    测试对象- ClickDblClickDragDropTypeActivateMinimizeRestore
    Maximize
       
    低级录制模式录制的每个步骤都显示在关键字视图和专家视图中。(模拟录制仅录制关键字视图中那个调用外部模拟数据文件的步骤。)

    模拟录制和低级录制的规则
       
    模拟录制和低级录制产生的脚本无法插入检查点,而且应用程序界面稍有变动则脚本无法正常运行,所以非万不得已(QTP正常支持的B/S结构以外的情况)应该避免这两种录制方式。有时候因为无法正常录制或者无法正常运行,初学者就可能考虑使用这两种方式录制,事实上,只要是C/S(其实部分结构简单一点的B/S)结构,基本上都能够避免所遇到的这些问题,具体方法,参见第二部分。

    当选择模拟录制低级录制时,请考虑下列规则:

    只有当QuickTest的正常录制模式不能准确录制您的操作时,才应使用模拟录制低级录制模拟录制低级录制要求比正常录制模式更多的磁盘空间。对于特定的步骤,您可以在录制会话期间切换到模拟录制低级录制。在以模拟录制低级录制模式录制了必要的步骤之后,就可以返回到正常录制模式来完成录制会话的其余部分。

  • 使用 CreationTime 属性标识对象

    2009-04-20 19:46:14

    在录制期间,如果 QuickTest 不能仅基于测试对象描述唯一标识浏览器对象,它将为“CreationTime”测试对象属性分配一个值。该值指示该浏览器打开的顺序,相对于其他描述都相同的其他打开的浏览器。
        在运行会话期间,如果 QuickTest 不能只是基于其测试对象描述来标识某个浏览器对象,那么,它将检查浏览器的打开顺序,然后使用“CreationTime”属性标识正确的对象。
        例如,如果您在三个浏览器上录制测试或组件,这三个浏览器除了打开时间分别在 9:01 pm、9:03 pm 和 9:05 pm 之外,其他方面完全相同,那么,QuickTest 将把 CreationTime 值 0 分配给 9:01 pm 浏览器,1 分配给 9:03 pm 浏览器,2 分配给 9:05 pm 浏览器。
        在 10:30 pm,当您返回测试或组件时,假定浏览器分别在 10:31 pm、10:33 pm 和 10:34 pm 打开。QuickTest 将使用 CreationTime = 0 的浏览器测试对象标识 10:31 pm 浏览器,使用 CreationTime = 1 的测试对象标识 10:33 pm 浏览器,使用 CreationTime = 2 的测试对象标识 10:34 pm 浏览器。
        如果打开几个浏览器,则 CreationTime 最高的浏览器是最后一个打开的浏览器,CreationTime 最低的浏览器是第一个打开的浏览器。例如,如果打开三个或更多的浏览器,则 CreationTime = 2 的浏览器是第三个打开的浏览器。如果恰好有七个或更少的浏览器,则 CreationTime = 6 的浏览器是最后一个打开的浏览器。
        例如,如果在 CreationTime = 6 的浏览器上录制步骤,并且具有该 CreationTime 值的浏览器没有打开,该步骤将在当前打开的、CreationTime 值最高的浏览器上运行。例如,如果在运行会话期间,当前打开了 CreationTime = 0 和 CreationTime = 1 的两个浏览器,那么该步骤不会由于找不到 CreationTime = 6 的浏览器而失败,相反,它会在最后一个打开的浏览器上运行,如果是这种情况,浏览器 CreationTime = 1。
        注意:可能会出现这样的情况,在会话期间的特定时间,可用的 CreationTime值并不有序。例如,如果您在录制或运行会话期间打开六个浏览器,然后在该会话期间,您关闭第二个和第四个浏览器(CreationTime 值为 1 和 3),那么在会话结束时,打开的浏览器将是 CreationTime 值为 0、2、4 和 5 的浏览器。
  • Explore.EXE找不到序数:无法定位序数968于动态链接库LIBEAY32.dll上。

    2009-04-13 19:55:52

     

      解决方案:用LoadRuner\bin下的LIBEAY32.dll覆盖c:\windows\system32下的

    同名文件。覆盖后,此错误提示框不再出现

  • (转)web_custom_request应用示例

    2009-04-10 18:35:48

    LoadRunner提供的web_custom_request函数可以用于实现参数的动态生成。在LoadRunner中,web_reg_save_param和custom_request都常于处理参数的动态生成。

    web_reg_save_param函数是大家都已经熟悉的了,它的主要作用是从一个response中获得后续的request需要使用的数据,然后将其作为一个参数保存下来,供后续步骤使用。该方法在LoadRunner中被称为Correlation(关联)。

    而web_custom_request函数则可以用于完全自定义向服务端发送的request。

    接下来我们用一个实际的例子说明一下web_custom_request的具体应用:

    Mercury自带的MercuryWebTours例子为例,假设我们希望在登录进入后将用户的前两条记录删除,我们来看看用web_custom_request如何实现这个目标。

    首先,我们尝试用HTML方式对该操作进行录制。录制后的脚本中与删除相关的部分大致如下:

    web_url("welcome.pl",
        
    "URL=http://localhost/MercuryWebTours/welcome.pl?page=itinerary",
            
    "Resource=0",
            
    "RecContentType=text/html",
        
    "Referer=http://localhost/MercuryWebTours/nav.pl?page=menu&in=home",
        
    "Snapshot=t3.inf",
        
    "Mode=HTML",
        EXTRARES,
        
    "URL=images/in_itinerary.gif""Referer=http://localhost/MercuryWebTours/nav.pl?page=menu&in=itinerary", ENDITEM,
        
    "URL=images/home.gif""Referer=http://localhost/MercuryWebTours/nav.pl?page=menu&in=itinerary", ENDITEM,
            LAST);
    lr_think_time(
    2);

    web_submit_form(
    "itinerary.pl",
        
    "Snapshot=t4.inf",
        ITEMDATA,
        
    "Name=1""Value=on", ENDITEM,
        
    "Name=2""Value=on", ENDITEM,        "Name=removeFlights.x""Value=116", ENDITEM,
        
    "Name=removeFlights.y""Value=8", ENDITEM,
        LAST);

    我们通过树型模式查看一下在submit form的时候实际向服务器发出的请求的内容:

    从请求内容中可以看到,我们通过POST方法发出了请求,请求发送的目的URL是/MercuryWebTours/itinerary.pl,发送的内容是:
    "1=on&flightID=384-798-JM&2=on&flightID=3026-1592-JM&3=on&flightID=1194-2326-JM&.cgifields=1&.c"
    "gifields=2&.cgifields=3&removeFlights.x=116&removeFlights.y=8"

    从发送的内容中可以很明显的分析得出,1=on表示第一个checkbox是被选中的,flightID=384-798-JM表示的是第一条记录所对应的flightID。因此,如果我们需要自己组成这个发送的request的话,必须首先通过关联的方式获得前两条记录的flightID,然后再组成request的内容。

    web_custom_request方法的原型是:
    int web_custom_request (const char *RequestName, <List of Attributes>,[EXTRARES, <List of Resource Attributes>,] LAST );

    其中List of Attributes的主要项目是Method,URL和BODY等。对这个例子来说,我们可以很容易构造出我们需要的request的BODY内容。
    ……
     strcpy(creq, "Body=1=on&flightID=");
     strcat(creq, lr_eval_string("{fID1}"));
     strcat(creq, "&2=on&flightID=");
     strcat(creq, lr_eval_string("{fID2}"));
     strcat(creq, "&.cgifields=1&.cgifields=2");
     strcat(creq, "&removeFlights.x=116&removeFlights.y=8");
    ……

    其中{fID1}、{fID2}等都是通过关联获得的flightID的数据。

    因此,我们可以根据图中的数据编写custom_request语句:

    web_custom_request("itinerary.pl",
    "Method=POST"
        
    "URL=http://localhost/MercuryWebTours/itinerary.pl",
        
    "RecContentType=text/xml",
        creq,
        
    "Snapshot=t4.inf",
        LAST);

    较为完整的代码如下:
    Action()
    {
        
    char creq[500];

        web_reg_save_param(
    "fID1""LB=INPUT TYPE=\"hidden\" NAME=\"flightID\" VALUE=\"""RB=\"""ORD=1"
            
    "SEARCH=BODY", LAST);
        web_reg_save_param(
    "fID2""LB=INPUT TYPE=\"hidden\" NAME=\"flightID\" VALUE=\"""RB=\"""ORD=2"
            
    "SEARCH=BODY", LAST);
        web_url(
    "welcome.pl",
            
    "URL=http://localhost/MercuryWebTours/welcome.pl?page=itinerary",
            
    "Resource=0",
            
    "RecContentType=text/html",
            
    "Referer=http://localhost/MercuryWebTours/nav.pl?page=menu&in=home",
            
    "Snapshot=t3.inf",
            
    "Mode=HTML",
            EXTRARES,
            
    "URL=images/in_itinerary.gif""Referer=http://localhost/MercuryWebTours/nav.pl?page=menu&in=itinerary", ENDITEM,
            
    "URL=images/home.gif""Referer=http://localhost/MercuryWebTours/nav.pl?page=menu&in=itinerary", ENDITEM,
            LAST);
        lr_think_time(
    2);

        strcpy(creq, 
    "Body=1=on&flightID=");
        strcat(creq, lr_eval_string(
    "{fID1}"));
        strcat(creq, 
    "&2=on&flightID=");
        strcat(creq, lr_eval_string(
    "{fID2}"));
        strcat(creq, 
    "&.cgifields=1&.cgifields=2");
        strcat(creq, 
    "&removeFlights.x=116&removeFlights.y=8");

        lr_output_message(creq);

        web_custom_request(
    "itinerary.pl",
    "Method=POST"
            
    "URL=http://localhost/MercuryWebTours/itinerary.pl",
            
    "RecContentType=text/xml",
            creq,
            
    "Snapshot=t4.inf",
            LAST);

        
    return 0;
    }
  • QuickTest Plus帮助文档中EXCEL相关函数

    2009-04-04 10:00:26

    QuickTest Plus帮助文档中EXCEL相关函数

    打开QTP,test--settings--resources,将附件文件添加进去就可以使用相关函数了

    ?
    Dim ExcelApp    'As Excel.Application
    Dim excelSheet  'As Excel.worksheet
    Dim excelBook   'As Excel.workbook
    Dim fso         'As scrīpting.FileSystemObject

    ' *********************************************************************************************
    ' 函数说明:创建一个Excel应用程序ExcelApp,并创建一个新的工作薄Workbook;
    ' 参数说明:无
    ' 调用方法:
    '           CreateExcel()
    ' *********************************************************************************************

    Function CreateExcel()
        Dim excelSheet
        Set ExcelApp = CreateObject("Excel.Application")
        ExcelApp.Workbooks.Add  
        ExcelApp.Visible = True
        Set CreateExcel = ExcelApp
    End Function

    ' *********************************************************************************************
    ' 函数说明:关闭Excel应用程序;
    ' 参数说明:
    '          (1)ExcelApp:Excel应用程序名称;
    ' 调用方法:
    '           CloseExcel(ExcelApp)
    ' *********************************************************************************************
    Sub CloseExcel(ExcelApp)
        Set excelSheet = ExcelApp.ActiveSheet
        Set excelBook = ExcelApp.ActiveWorkbook
        Set fso = CreateObject("scrīpting.FileSystemObject")
        On Error Resume Next
        fso.CreateFolder "C:\Temp"
        fso.DeleteFile "C:\Temp\ExcelExamples.xls"
        excelBook.SaveAs "C:\Temp\ExcelExamples.xls"
        ExcelApp.Quit
        Set ExcelApp = Nothing
        Set fso = Nothing
        Err = 0
        On Error GoTo 0
    End Sub

    ' *********************************************************************************************
    ' 函数说明:保存工作薄;
    ' 参数说明:
    '          (1)ExcelApp:Excel应用程序名称;
    '          (2)workbookIdentifier:属于ExcelApp的工作薄名称;
    '          (3)path:保存的路径;
    ' 返回结果:
    '          (1)保存成功,返回字符串:OK
    '          (2)保存失败,返回字符串:Bad Worksheet Identifier
    ' 调用方法:
    '           ret = SaveWorkbook(ExcelApp, "Book1", "D:\Example1.xls")
    ' *********************************************************************************************
    ' 函数说明:保存工作薄;
    ' 参数说明:
    '          (1)ExcelApp:Excel应用程序名称;
    '          (2)workbookIdentifier:属于ExcelApp的工作薄名称;
    '          (3)path:保存的路径;
    ' 返回结果:
    '          (1)保存成功,返回字符串:OK
    '          (2)保存失败,返回字符串:Bad Worksheet Identifier
    ' 调用方法:
    '           ret = SaveWorkbook(ExcelApp, "Book1", "D:\Example1.xls")
    ' *********************************************************************************************
    Function SaveWorkbook(ExcelApp, workbookIdentifier, path) 'As String
        Dim workbook
        On Error Resume Next  '启用错误处理程序
        Set workbook = ExcelApp.Workbooks(workbookIdentifier)
        On Error GoTo 0   '禁用错误处理程序
        If Not workbook Is Nothing Then
            If path = "" Or path = workbook.FullName Or path = workbook.Name Then
                workbook.Save
            Else
                Set fso = CreateObject("scrīpting.FileSystemObject")
                '判断路径中是否已添加扩展名.xls
                If InStr(path, ".") = 0 Then
                    path = path & ".xls"
                End If
                '删除路径下现有同名的文件
                On Error Resume Next
                fso.DeleteFile path
                Set fso = Nothing
                Err = 0
                On Error GoTo 0
               
                workbook.SaveAs path
            End If
            SaveWorkbook = "OK"
        Else
            SaveWorkbook = "Bad Workbook Identifier"
        End If
    End Function
    ' *********************************************************************************************
    ' 函数说明:设置工作表excelSheet单元格的值
    ' 参数说明:
    '          (1)excelSheet:工作表名称;
    '          (2)row:的序号,第一列为1;
    '          (3)column:行的序号,第一行为1;
    '          (4)value:单元格要设置的值;
    ' 返回结果:
    '          无返回值
    ' 调用方法:
    '           SetCellValue excelSheet1, 1, 2, "test\"
    ' *********************************************************************************************
    Sub SetCellValue(excelSheet, row, column, value)
        On Error Resume Next
        excelSheet.Cells(row, column) = value
        On Error GoTo 0
    End Sub
    'The GetCellValue returns the cell's value according to its row column and sheet
    'excelSheet - the Excel Sheet in which the cell exists
    'row - the cell's row
    'column - the cell's column
    'return 0 if the cell could not be found
    ' *********************************************************************************************
    ' 函数说明:获取工作表excelSheet单元格的值
    ' 参数说明:
    '          (1)excelSheet:工作表名称;
    '          (2)row:的序号;
    '          (3)column:行的序号;
    ' 返回结果:
    '          (1)单元格存在,返回单元格值;
    '          (2)单元格不存在,返回0;
    ' 调用方法:
    '           set CellValue = GetCellValue(excelSheet, 1, 2)
    ' *********************************************************************************************
    Function GetCellValue(excelSheet, row, column)
        value = 0
        Err = 0
        On Error Resume Next
        tempValue = excelSheet.Cells(row, column)
        If Err = 0 Then
            value = tempValue
            Err = 0
        End If
        On Error GoTo 0
        GetCellValue = value
    End Function
    ' *********************************************************************************************
    ' 函数说明:获取并返回工作表对象
    ' 参数说明:
    '          (1)ExcelApp:Excel应用程序名称;
    '          (2)sheetIdentifier:属于ExcelApp的工作表名称;
    ' 返回结果:
    '          (1)成功:工作表对象Excel.worksheet
    '          (1)失败:Nothing
    ' 调用方法:
    '           Set excelSheet1 = GetSheet(ExcelApp, "Sheet Name")
    ' *********************************************************************************************
    Function GetSheet(ExcelApp, sheetIdentifier)
        On Error Resume Next
        Set GetSheet = ExcelApp.Worksheets.Item(sheetIdentifier)
        On Error GoTo 0
    End Function
    ' *********************************************************************************************
    ' 函数说明:添加一张新的工作表
    ' 参数说明:
    '          (1)ExcelApp:Excel应用程序名称;
    '          (2)workbookIdentifier:属于ExcelApp的工作薄名称;
    '          (2)sheetName:要插入的工作表名称;
    ' 返回结果:
    '          (1)成功:工作表对象worksheet
    '          (1)失败:Nothing
    ' 调用方法:
    '           InsertNewWorksheet(ExcelApp, workbookIdentifier, "new sheet")
    ' *********************************************************************************************

    Function InsertNewWorksheet(ExcelApp, workbookIdentifier, sheetName)
        Dim workbook 'As Excel.workbook
        Dim worksheet 'As Excel.worksheet

        '如果指定的工作薄不存在,将在当前激活状态的工作表中添加工作表
        If workbookIdentifier = "" Then
            Set workbook = ExcelApp.ActiveWorkbook
        Else
            On Error Resume Next
            Err = 0
            Set workbook = ExcelApp.Workbooks(workbookIdentifier)
            If Err <> 0 Then
                Set InsertNewWorksheet = Nothing
                Err = 0
                Exit Function
            End If
            On Error GoTo 0
        End If

        sheetCount = workbook.Sheets.Count  '获取工作薄中工作表的数量
        workbook.Sheets.Add , sheetCount '添加工作表
        Set worksheet = workbook.Sheets(sheetCount + 1)  '初始化worksheet为新添加的工作表对象

        '设置新添加的工作表名称
        If sheetName <> "" Then
            worksheet.Name = sheetName
        End If

        Set InsertNewWorksheet = worksheet
    End Function

    ' *********************************************************************************************
    ' 函数说明:修改工作表的名称;
    ' 参数说明:
    '          (1)ExcelApp:Excel应用程序名称;
    '          (2)workbookIdentifier:属于ExcelApp的工作薄名称;
    '          (3)worksheetIdentifier:属于workbookIdentifier工作薄的工作表名称;
    '          (4)sheetName:修改后的工作表名称;
    ' 返回结果:
    '          (1)修改成功,返回字符串:OK
    '          (2)修改失败,返回字符串:Bad Worksheet Identifier
    ' 调用方法:
    '           set ret = RenameWorksheet(ExcelApp, "Book1", "Sheet1", "Sheet Name")
    ' *********************************************************************************************

    Function RenameWorksheet(ExcelApp, workbookIdentifier, worksheetIdentifier, sheetName)
        Dim workbook
        Dim worksheet
        On Error Resume Next
        Err = 0
        Set workbook = ExcelApp.Workbooks(workbookIdentifier)
        If Err <> 0 Then
            RenameWorksheet = "Bad Workbook Identifier"
            Err = 0
            Exit Function
        End If
        Set worksheet = workbook.Sheets(worksheetIdentifier)
        If Err <> 0 Then
            RenameWorksheet = "Bad Worksheet Identifier"
            Err = 0
            Exit Function
        End If
        worksheet.Name = sheetName
        RenameWorksheet = "OK"
    End Function

    ' *********************************************************************************************
    ' 函数说明:删除工作表;
    ' 参数说明:
    '          (1)ExcelApp:Excel应用程序名称;
    '          (2)workbookIdentifier:属于ExcelApp的工作薄名称;
    '          (3)worksheetIdentifier:属于workbookIdentifier工作薄的工作表名称;
    ' 返回结果:
    '          (1)删除成功,返回字符串:OK
    '          (2)删除失败,返回字符串:Bad Worksheet Identifier
    ' 调用方法:
    '           set ret = RemoveWorksheet(ExcelApp, "Book1", "Sheet1")
    ' *********************************************************************************************

    Function RemoveWorksheet(ExcelApp, workbookIdentifier, worksheetIdentifier)
        Dim workbook 'As Excel.workbook
        Dim worksheet 'As Excel.worksheet
        On Error Resume Next
        Err = 0
        Set workbook = ExcelApp.Workbooks(workbookIdentifier)
        If Err <> 0 Then
            RemoveWorksheet = "Bad Workbook Identifier"
            Exit Function
        End If
        Set worksheet = workbook.Sheets(worksheetIdentifier)
        If Err <> 0 Then
            RemoveWorksheet = "Bad Worksheet Identifier"
            Exit Function
        End If
        worksheet.Delete
        RemoveWorksheet = "OK"
    End Function

    ' *********************************************************************************************
    ' 函数说明:添加新的工作薄
    ' 参数说明:
    '          (1)ExcelApp:Excel应用程序名称;
    ' 返回结果:
    '          (1)成功:工作表对象NewWorkbook
    '          (1)失败:Nothing
    ' 调用方法:
    '          set NewWorkbook = CreateNewWorkbook(ExcelApp)
    ' *********************************************************************************************

    Function CreateNewWorkbook(ExcelApp)
        Set NewWorkbook = ExcelApp.Workbooks.Add()
        Set CreateNewWorkbook = NewWorkbook
    End Function

    ' *********************************************************************************************
    ' 函数说明:打开工作薄
    ' 参数说明:
    '          (1)ExcelApp:Excel应用程序名称;
    '          (2)path:要打开的工作薄路径;
    ' 返回结果:
    '          (1)成功:工作表对象NewWorkbook
    '          (1)失败:Nothing
    ' 调用方法:
    '          set NewWorkbook = CreateNewWorkbook(ExcelApp)
    ' *********************************************************************************************

    Function OpenWorkbook(ExcelApp, path)
        On Error Resume Next
        Set NewWorkbook = ExcelApp.Workbooks.Open(path)
        Set ōpenWorkbook = NewWorkbook
        On Error GoTo 0
    End Function

    ' *********************************************************************************************
    ' 函数说明:将工作薄设置为当前工作状态
    ' 参数说明:
    '          (1)ExcelApp:Excel应用程序名称;
    '          (2)workbookIdentifier:要设置为当前工作状态的工作薄名称;
    ' 返回结果:无返回值;
    ' 调用方法:
    '          ActivateWorkbook(ExcelApp, workbook1)
    ' *********************************************************************************************

    Sub ActivateWorkbook(ExcelApp, workbookIdentifier)
        On Error Resume Next
        ExcelApp.Workbooks(workbookIdentifier).Activate
        On Error GoTo 0
    End Sub

    ' *********************************************************************************************
    ' 函数说明:关闭Excel工作薄;
    ' 参数说明:
    '          (1)ExcelApp:Excel应用程序名称;
    '          (2)workbookIdentifier:
    ' 调用方法:
    '           CloseWorkbook(ExcelApp, workbookIdentifier)
    ' *********************************************************************************************

    Sub CloseWorkbook(ExcelApp, workbookIdentifier)
        On Error Resume Next
        ExcelApp.Workbooks(workbookIdentifier).Close
        On Error GoTo 0
    End Sub

    ' *********************************************************************************************
    ' 函数说明:判断两个工作表对应单元格内容是否相等
    ' 参数说明:
    '          (1)sheet1:工作表1的名称;
    '          (2)sheet2:工作表2的名称;
    '          (3)startColumn:开始比较的行序号;
    '          (4)numberOfColumns:要比较的行数;
    '          (5)startRow:开始比较的序号;
    '          (6)numberOfRows:要比较的数;
    '          (7)trimed:是否先除去字符串开始的空格和尾部空格后再进行比较,true或flase;
    ' 返回结果:
    '          (1)两工作表对应单元格内容相等:true
    '          (2)两工作表对应单元格内容不相等:flase         
    ' 调用方法:
    '           ret = CompareSheets(excelSheet1, excelSheet2, 1, 10, 1, 10, False)
    ' *********************************************************************************************

    Function CompareSheets(sheet1, sheet2, startColumn, numberOfColumns, startRow, numberOfRows, trimed)
        Dim returnVal 'As Boolean
        returnVal = True

        '判断两个工作表是否都存在,任何一个不存在停止判断,返回flase
        If sheet1 Is Nothing Or sheet2 Is Nothing Then
            CompareSheets = False
            Exit Function
        End If

        '循环判断两个工作表单元格的值是否相等
        For r = startRow to (startRow + (numberOfRows - 1))
            For c = startColumn to (startColumn + (numberOfColumns - 1))
                Value1 = sheet1.Cells(r, c)
                Value2 = sheet2.Cells(r, c)

                '如果trimed为true,去除单元格内容前面和尾部空格
                If trimed Then
                    Value1 = Trim(Value1)
                    Value2 = Trim(Value2)
                End If

                '如果单元格内容不一致,函数返回flase
                If Value1 <> Value2 Then
                    Dim cell 'As Excel.Range
                    '修改sheet2工作表中对应单元格值
                    sheet2.Cells(r, c) = "Compare conflict - Value was '" & Value2 & "', Expected value is '" & Value1 & "'."  
                    '初始化cell为sheet2中r:c单元格对象
                    Set cell = sheet2.Cells(r, c) '
                    '将sheet2工作表中对应单元格的颜色设置为红色
                    cell.Font.Color = vbRed  
                    returnVal = False
                End If
            Next
        Next
        CompareSheets = returnVal
    End Function
  • VBScript编码约定

    2008-2-13

    VBscrīpt编码约定

    编码约定是帮助您使用 Microsoft Visual Basic scrīpting Edition 编写代码的一些建议。编码约定包含以下内容:

    • 对象、变量和过程的命名约定
    • 注释约定
    • 文本格式和缩进指南

    使用一致的编码约定的主要原因是使脚本或脚本集的结构和编码样式标准化,这样代码易于阅读和理解。使用好的编码约定可以使源代码明白、易读、准确,更加直观且与其他语言约定保持一致。

    常数命名约定

    VBscrīpt 的早期版本不允许创建用户自定义常数。如果要使用常数,则常数以变量的方式实现,且全部字母大写以和其他变量区分。常数名中的多个单词用下划线 (_) 分隔。例如:

     USER_LIST_MAX
    NEW_LINE

    这种标识常数的方法依旧可行,但您还可以选择其他方案,用 Const 语句创建真正的常数。这个约定使用大小写混合的格式,并以“con”作为常数名的前缀。例如:

     conYourOwnConstant

    变量命名约定

    为提高易读和一致性,请在 VBscrīpt 代码中使用以下变量命名约定:

    子类型 前缀 示例
    Boolean bln blnFound
    Byte byt bytRasterData
    Date (Time) dtm dtmStart
    Double dbl dblTolerance
    Error err errOrderNum
    Integer int intQuantity
    Long lng lngDistance
    Object obj objCurrent
    Single sng sngAverage
    String str strFirstName

    变量作用域

    变量应定义在尽量小的作用域中。VBscrīpt 变量的作用域如下所示:

    作用域 声明变量处 可见性
    过程级 事件、函数或子过程。 在声明变量的过程中可见。
    scrīpt 级 HTML 页面的 HEAD 部分,任何过程之外。 在脚本的所有过程中可见。

    变量作用域前缀

    随着脚本代码长度的增加,有必要快速区分变量的作用域。在类型前缀前面添加一个单字符前缀可以实现这一点,而不致使变量名过长。

    作用域 前缀 示例
    过程级 dblVelocity
    scrīpt 级 s sblnCalcInProgress

    描述性变量名和过程名

    变量名或过程名的主体应使用大小写混合格式,并且尽量完整地描述其目的。另外,过程名应以动词开始,例如 InitNameArray 或 CloseDialog。

    对于经常使用的或较长的名称,推荐使用标准缩写以使名称保持在适当的长度内。通常多于 32 个字符的变量名会变得难以阅读。使用缩写时,应确保在整个脚本中保持一致。例如,在一个脚本或脚本集中随意切换 Cnt 和 Count 将造成混乱。

    对象命名约定

    下表出了 VBscrīpt 中可能用到的对象命名约定(推荐):

    对象类型 前缀 示例
    3D 面板 pnl pnlGroup
    动画按钮 ani aniMailBox
    复选框 chk chkReadOnly
    组合框、下拉列表框 cbo cboEnglish
    命令按钮 cmd cmdExit
    公共对话框 dlg dlgFileOpen
    框架 fra fraLanguage
    水平滚动条 hsb hsbVolume
    图像 img imgIcon
    标签 lbl lblHelpMessage
    直线 lin linVertical
    列表框 lst lstPolicyCodes
    旋钮 spn spnPages
    文本框 txt txtLastName
    垂直滚动条 vsb vsbRate
    滑块 sld sldScale

    代码注释约定

    所有过程的开始部分都应有描述其功能的简要注释。这些注释并不描述细节信息(如何实现功能),这是因为细节有时要频繁更改。这样就可以避免不必要的注释维护工作以及错误的注释。细节信息由代码本身及必要的内部注释来描述。

    当传递给过程的参数的用途不明显,或过程对参数的取值范围有要求时,应加以说明。如果过程改变了函数和变量的返回值(特别是通过参数引用来改变),也应在过程的开始部分描述该返回值。

    过程开始部分的注释应包含以下区段标题。相关样例,请参阅后面的“格式化代码”部分。

    区段标题 注释内容
    目的 过程的功能(不是实现功能的方法)。
    假设 其状态影响此过程的外部变量、控件或其他元素的列表。
    效果 过程对每个外部变量、控件或其他元素的影响效果的列表。
    输入 每个目的不明显的参数的解释。每个参数都应占据单独一行并有其内部注释。
    返回 返回值的解释。

    请记住以下几点:

    • 每个重要的变量声明都应有内部注释,描述变量的用途。
    • 应清楚地命名变量、控件和过程,仅在说明复杂细节时需要内部注释。
    • 应在脚本的开始部分包含描述该脚本的概述,列举对象、过程、运算法则、对话框和其他系统从属物。有时一段描述运算法则的假码是很有用的。

    格式化代码

    应尽可能多地保留屏幕空间,但仍允许用代码格式反映逻辑结构和嵌套。以下为几点提示:

    • 标准嵌套块应缩进 4 个空格。
    • 过程的概述注释应缩进 1 个空格。
    • 概述注释后的最高层语句应缩进 4 个空格,每一层嵌套块再缩进 4 个空格。例如:
    '*********************************************************
    ' Purpose: Locates the first occurrence of a specified user
    '          in the UserList array.
    ' Inputs: strUserList(): the list of users to be searched.
    '         strTargetUser: the name of the user to search for.
    ' Returns: The index of the first occurrence of the strTargetUser
    '          in the strUserList array.
    '          If the target user is not found, return -1.
    '*********************************************************
    Function intFindUser (strUserList(), strTargetUser)
       Dim i   ' Loop counter.
       Dim blnFound   ' Target found flag
       intFindUser = -1
       i = 0   ' Initialize loop counter
       Do While i <= Ubound(strUserList) and Not blnFound
          If strUserList(i) = strTargetUser Then
             blnFound = True   ' Set flag to True
             intFindUser = i   ' Set return value to loop count
          End If
          i = i + 1   ' Increment loop counter
       Loop
    End Function
  • [回顾]清华申请退学博士作品:完全用Linux工作

    2009-02-03 11:58:29

    我已经半年没有使用 Windows 的方式工作了。Linux 高效的完成了我所有的工作。

    GNU/Linux 不是每个人都想用的。如果你只需要处理一般的事务,打游戏,那么你不需要了解下面这些了。

    我不是一个狂热的自由软件份子,虽然我很喜欢自由软件。这篇文章也不是用来推行自由软件运动的,虽然我觉得自由软件运动是非常好的。

    这篇文章也不是用来比较 Linux 和 Windows 内核效率,文件系统,网络服务的。我现在是作为一个用户而不是一个开发者来说话的,我们的讨论是基于操作,应用层面的。是为了告诉大学里还不了解,或者不理解 UNIX 的科学工作者和大学生,UNIX 比 Windows 更适合用于科学研究工作,请大家理解 UNIX 的工作方式,不要用 Windows 的标准来要求 Linux,而要用一个科学工作者的标准来要求自己,用UNIX 的思想来武装自己。

    我显然是反对在大学,特别是理工科专业推广 Windows 的。我也反对在对"娃娃"们的计算机启蒙教育中使用 Windows。因为 Windows 不论从技术上,经济上,思想风格上都是与我们培养高科技人才的目标格格不入的。Windows 的流行属于历史遗留问题,爷爷一级的人当然已经不可救药,但是我们不应该让下一代继续走上歧途。

    UNIX 不是计算机专家的专利

    当我建议一些非计算机专业的人用 Linux 的时候,很多人说:"UNIX 是计算机系的人用的,我们不能理解。" "UNIX 是男孩用的,我们女孩不用。"

    但是其实世界上的大多数科学家和工程师几乎用的都是 UNIX 作为他们的电脑工具。就因为它简单,可靠,稳定,强大,有趣。甚至很多时候 UNIX 就是唯一的选择。

    你说:"我们都会用 UNIX 的话,你们计算机专业的人还用来干什么?" 很容幸的告诉你,计算机专业的有一部分人就是专门为你们提供这样强大而方便的计算机工具的。如果他们制造的工具只有自己会用的话,那这个工具还有什么用?

    理解 GNU/Linux 不要用 Windows 的标准来要求 Linux。

    由于GNU/Linux这个词太长,下面如果没有特别指明,"Linux"就是指GNU/Linux"。

    在这个年代,恐怕没有人需要我来介绍 Linux 是什么了吧?如果你觉得"Linux 只不过是跟 DOS 差不多的东西",那请问问你旁边的 Linux 用户,Linux 到底是什么?

    那为什么我还要写一篇这样的文章?因为,我发现还有很多人不不理解 Linux 和 UNIX,虽然他们也在用它,但是他们有时会问:"为什么 Linux 不能像 Windows 那样 ……?","怎么Redhat Linux不能 mount NTFS 分区!","Linux 下用什么整理硬盘?","什么时候OpenOffice才能完全兼容Word文件啊?","现在还有什么Windows能干的事情Linux干不了的? "……

    他们有40G的硬盘,却只为 Linux 分配了2G空间,有时还抱怨"这个东西怎么占这么多硬盘!" 似乎 Windows 该占用大部分硬盘。他们把重要的数据装在Windows的分区,似乎信不过Linux。他们总是到处寻找新奇的,好看的GUI程序,对命令行的东西一概不屑一顾。他们对Drag&Drop,菜单配置,自动升级非常感兴趣。他们如果找到一个很像 Windows 程序的 Linux 程序,一定会很高兴的说:"哈哈!Linux 也能……了!"如果Linux在某种测试中胜过Windows,他们会高兴得跳起来。他们没有办法用Linux 解决问题的时候,甚至用Wine来运行Windows程序。有时实在没办法,只好重起到Windows,或者干脆省得麻烦,在 Windows 下装一个 VMWare 虚拟一个 Linux 玩。

    你如果出现了上面的情况,说明你的思想受到了 Windows 的某种潜移默化的影响和误导。你没有能够从本质上理解存在于 Linux 身上的 UNIX 思想。你支持 Linux,你喜欢 Linux,你能从中感觉到快乐,这非常好。你现在只需要明白的是:Linux 从来就不是一个玩具,它是天才UNIX的后代。UNIX 是自晶体管发明以来最伟大的发明,它从诞生那一天开始就比 Windows 的设计出色。

    你要体会什么叫做"设计",一个糟糕的设计并不是到后来缝缝补补就可以变好的,而一个出色的设计,不但可以以不变应万变,而且可以影响到后来者。一个出色的设计配上一个出色的实现,那就是非常出色的发明。Linux 就是这样的一个出色的发明。Linux 并不需要追赶 Windows,也不需要打垮微软。它的最终目标是改变整个计算机世界,还人们自由,给人们乐趣和方便。

    Unix 是简单的,你不需要成为一个天才也能理解这种简单。

    UNIX 的设计者 Dennis Ritchie 说:"Unix is simple. It just takes a genius to understand its simplicity." 但是我不这么认为,因为我不是一个天才,但是我却勇敢的把 Windows 完全删除掉,遇到不明白的事情的时候努力用 UNIX 的方式去解决,而不是寻求 Windows 的帮助。现在我体会到了 UNIX 的思想和好处,我可以用比 Windows 高效几倍的效率工作。因为我相信这样的信念:"Windows 能办到的事 Linux 一定能办到,而且办的更好。"

    这小节开头的话应该改成:"Unix 是简单的,你不需要成为一个天才或是计算机专家。但是在这个冲斥着 Windows 错误观念的世界,你需要信念和勇气才能理解它的简单。" 我下面就告诉你一些我理解到的东西。首先,你要知道的是微软在国际科学领域是根本没有地位的。

    微软的地位

    微软的名声在欧洲和美国的大学里,特别是在计算机系里之坏,大家可能有所耳闻。我认识的 MIT,Stanford 的教授,贝尔实验室的专家,甚至一个欧洲小国的高中计算机老师都绝口不提微软的名字。在他们眼里,微软只是一个没有真技术,专靠在落后国家商业宣传和垄断经营的小公司。这个"小"并不是说它人少,钱少,而是说它先进技术少。

    我上次和王益合作写了一个算法演示程序,那个算法是贝尔实验室一位科学家Steven Fortune很天才的发明,为了程序能够被身边大多数人使用,我们选择了 VC+MFC 作为平台。我在分析算法时还得到 Fortune 很热情的鼓励,寄给我一份资料,还多次回信耐心的给我讲解了很多细节。但是程序完成之后,我把样品发给 Fortune,他回信说:"对不起。我机器上没有 MFC。" 话说的很客气,但是我已经感觉到了他对 Windows的不屑。然后我把 MFC 静态编译进程序再发给他,他就没有再回信了。他显然不是瞧不起我,而是确实有难处。

    你能感觉到这位科学家对微软和 Windows 是什么态度了吧?不是反感,而是他心里根本没有 Windows 这个东西!微软在高科技领域没有发展,那么它怎么生存呢?到发展中国家去发展一下,他们的人民还对电脑一无所知,我说不定甚至可以打入大学的计算机系呢。我送他们软件,我捐钱盖大楼,我出钱找图灵奖获得者来演讲,让他们觉得我们都是科学家!

    好了,现在全国的大学包括清华,几乎所有人机器必装盗版 Win2000,Office XP,学校的选课系统是非IE不能正确浏览,论文用 Word 编辑,演示用ppt做,email 的通知附件是 doc 文件,你不用 Word 打不开,连 863 项目都用 VC 写程序了。我很久以前就看到一份报纸说,"微软为什么不严厉打击盗版?" 这篇文章说,微软非但不打击中国的盗版行为,而且有放任之趋势。放长线吊大鱼,"以后我要你们加倍的来还我!" 确实如此,它的目的快实现了。

    Windows 笼罩下的中国计算机教育

    说句丢脸的话,比尔盖茨很久以前是我的偶像……

    在中国,比尔盖茨被很多人奉为神圣,"少年电脑天才",甚至有的人提到他的名字就做出"抱拳对天"的姿势。很多人谈到微软的"新技术","高科技" 都是眉飞色舞。各种"VC编程圣经","深入了解 Visual C++"之类的书,在开头几页都会出现非常肉麻的字眼,"在那团团的混沌中,一个开天辟地的精灵,Windows 1.0,诞生了……"

    微软的软件被这么多人盗用,那么人们是怎样使用这些盗版程序的呢?先看看电脑培训班,教的都是一些 DOS 命令,打字,Windows 基本操作,Word 文档处理,PowerPoint,高级班可能有 Excel,Access…… 参加各种微软认证考试,MCSE,MSDE 的人络绎不绝。考试辅导班都贴出了"280元,考过为止"之类的字样。考试参考资料更是昂贵,有些电脑书店整整两书架都是"Microsoft Press"的东西。我有个同学参加认证考试,每门考试都要200多元。而且你一次考不过可以再考,又要交钱。他后来还津津乐道跟我说,看我,花了 XXXX(一个四位数)元考过了微软认证,得到一张比尔盖茨亲笔签名的证书和价值6000元的 Windows XP 内部发行版。

    "电脑要从娃娃抓起",我们再来看看娃娃们学的是什么。大部分家长给孩子买了电脑之后,他们首先就会装一个盗版的 Windows,然后买来盗版的游戏开始玩。如果哪个孩子会用 Delphi 编程序,那可不得了。报社记者,电视台争相报导,说,某某学校的初中生某某,在别人都还在玩电脑游戏这种"初级阶段"的时候就已经用 Delphi 写程序了。镜头还瞄准了他显示器上面的像框中的比尔盖茨头像!

    我刚进入大学计算机系时还不懂得什么是操作系统,因为我以前只用过"中华学习机"。看到新入学的同学们各个谈论的都是 "Windows 95","VC"…… 我简直觉得我落后了好几十年一样,整个一土人,根本跟他们答不上话。好不容易找到一个比较熟的同学问了一下:"你们天天谈论的瘟95是什么啊?"答: "win95就是一个操作系统,跟DOS是一类。""朵死是什么?" "你连DOS都不知道是什么?别在计算机系混了。" 学校上课当然不讲VC编程之类的东西,但是上 Pascal 的老师有一次就说:"嗨,我们学校真是落后。现在别人都用 C, C++,甚至 VC 了,我们还在讲 Pascal。不知道什么时候才能有VC课啊。你们出去也是要用VC的,只好自学了。" 于是,有些同学很多时候上课都捧着一本很重的"Windows 编程大全"之类的书,根本没有听课。吃饭时就念念有词的跟我说,"代码的优化是无止境的","匈牙利命名法真是伟大的发明" …… 这就是中国很多大学计算机系的情况。

    感觉到无知了?这不是偶然的,而是微软长久以来埋下的伏笔。它要让无知的大家都把它奉为神圣,它要让支持UNIX,Xwindow的人一旦说 UNIX 好,Xwindow 好的时候,都被一群人围着说教:"这个 Windows 也能做到","你对 Windows 有偏见","微软才是主流啊","你敢瞧不起 win2k?",".NET 就是世界潮流","微软的毕竟是新技术","有钱就是有技术"…… 甚至在一番论战比较后败下来还是要说:"Windows 性能差点,但是易用性强","Windows 是老百姓用的,要求别那么","微软那么有钱,以后想超过 UNIX 还不容易吗?"……

    发达国家的计算机教育

    我前段时间在 USENET 发文问有关 Scheme 语言的问题时,认识了一位丹麦人。他解决了我所有的问题,并且建议我阅读一些很"深奥"的有关程序语言语法,文法的书,他告诉我很多网站可以学习 LISP,Scheme,人工智能,算法。他叫我看 Jonathan Rees 的论文 "Syntactic Closures"。他还打包给我寄过来一份 MIT 的 "How to Design Programs"。他说他在自己的 PC 机上装的是 Linux,他用 Emacs 编辑,运行Scheme 程序。他对 Emacs 的了解和爱好真是使人惊讶。他大学本科毕业时做的毕业设计是一个 Scheme 解释器。这对于我来说是望尘末及了。

    他是那么的不厌其烦,我的每一个问题他都详细的回答。我有时都觉得过于详细了,怎么这么耐心啊?我觉得他似乎是我的高中老师。他是什么样的人呢?我好奇的打听了他的情况。原来,他是丹麦一所普通高中的计算机老师。

    他说他在高中里讲授程序设计和算法,计算机语言文法。他说用 Scheme,他的学生不用再为内存泄漏等程序语言本身的问题而烦恼,而专注于问题和算法本身。有利于培养学生解决问题的能力,特别是用计算机解决数学问题的能力。

    天哪!为什么欧洲出现那么多数学家,几何学家?你看看别人重视的是什么!我们的计算机教育如果继续这样下去,只会沿着弯路越走越远!

    微软和它的朋友们的如意算盘

    下面来看看微软的收入是怎么来的。首先,Windows 98系列操作系统,一个就是 100多美元,每次升级又是几乎同样的价钱。Windows NT 还要贵几倍,而且有用户数目限制,5个用户的,10个用户的…… 以后如果要增加用户数目还要按比例付钱。

    花了如此多钱买来的操作系统就能用了吗?它竟然连压缩程序都没有提供!你装上Windows 之后一般第一件事就是去下载一个 WinZip 吧,"只要 29 美元"。Windows会中病毒啊,马上花 70 美元买一个 Norton AntiVirus 吧。还有黑客呢?再买一个Norton Internet Security 好了,100 美元。系统需要优化,磁盘需要整理,买一个Norton System Works 是你最佳的解决方案,100美元。

    可是你现在还是不能干正事啊!你想要一个 Word, PowerPoint?那就买一套 Office XP 吧,一起买便宜些,$459.90。

    那些程序不会用啊!那些菜单怎么设置,到底有什么功能啊?看"帮助"也学不会。买本书看看吧,我推荐"Special Edition Using Microsoft Office XP",不贵,$27.99。这本书里面大部分是屏幕抓图,还是买一本旧的比较划算,$17.85。

    你如果只是当个秘书,上面的差不多还凑合了。可是你有更高的追求,你想成为 Windows程序员。首先买一个 Visual Studio.NET 吧,要不然怎么编译程序。$494.95。

    为了紧跟微软动向,世界潮流,不能不注册个 MSDN 什么的吧?这个贵一点,不过物有所值啊,$2,799。

    嗯,你现在已经是上层阶级,白领人士了。你现在可以像这样"自由"的,"安全"的生活了。

    为什么要反对使用 Windows

    很多人都说不应该完全否定 Window,Windows 也有它的长处。不应该骂微软。

    对。 Windows 容易操作,适合普通用户。如果微软把它自己定位在 P&G,Philips 那样的地位,能够给我们的百姓提供周到的,完善的,价廉物美的服务。那我肯定是很喜欢它的。但是从上面的种种情况说明,微软是一个野心极大的国际垄断组织!它的产品没有一个是不出问题的:Windows 不稳定,容易中病毒,而微软不为大家免费提供杀毒软件。我就是要让你们花钱买我的朋友 Symantec 的杀毒软件,谁叫你们已经上了我的贼船?这叫什么售后服务啊!

    你买来微软的程序,安装的时候一般都有一个协议,说:" 由于微软的程序造成你的数据损坏或丢失,微软概不负责。" 我想很多人肯定觉得这个不合理,不想按那个 "I accept"。但是你的软件买都买来了,钱都花了,现在一按 "I decline",安装程序马上就会退出。你只好被迫点击了 "I accept"!这不是不平等条约吗?

    我已经目睹了好几个朋友的文档被 Microsoft Word 损坏,有的是编辑了十多天的30多页的论文,有的是费了很大工夫做出来的个人简历,那个朋友为此失去了到自己向往的P&G 工作的机会。就在他要投简历的前一个晚上,就在那一瞬间…… 不知道他痛哭的时候有没有想起要投诉微软,可是谁叫我们用的都是盗版呢,况且你还点击了 "I accept"。

    微软仗势已经占有大部分PC市场,制定不符合国际标准的"微软的标准",以不合理的方式压制其它公司的软件,这个问题已经在美国司法部闹了很久了。他甚至在 Windows系列操作系统中放置能够通过网络泄漏用户信息的代码,以至于 Windows 刚进入澳大利亚时被澳大利亚政府禁止使用。

    有些人说:"微软毕竟开创了一个历史,造就了今天的 IT 行业。" 但是,如果没有微软,我们今天早就用上非常稳定,非常可靠,非常方便,非常"傻瓜"的软件了!微软是阻挡信息技术发展的罪魁祸首。

    微软的程序的工作方式(注意,我只是说操作方式,病毒的事情另外算)确实适合于一般家庭,上上网,发发邮件,打打游戏都不错。可是微软却要把自己包装成什么 "高科技"企业,要在世界各地设置"研究院",在大学计算机系赠送不适合用于科研的 Windows产品,甚至出钱请图灵奖得主来中国畅谈"二十一世纪的计算",还在大会上宣传自己的 .NET 技术。非要把别人认为自己是科学的,自己是领导世界高科技的。但是呢?它什么高科技也没有。欧洲,美国,哪一个关键部门在用微软的东西?NASA? DOE? CERN?你仔细想一想,微软的程序对人类到底有什么重大作用?

    什么是 Windows 能干而 Linux 干不了的事情?---
    "Windows 能干而 Linux 干不了的事情,那就是不需要干的事情。"

    有个朋友看我半年没有用 Windows,有时就会问我:"你只用 Linux,有没有发现有些Windows 能处理的事情 Linux 干不了?"---
    我回答说:"Windows 能干而 Linux 干不了的事情,那就是不需要干的事情。"


    Windows 能做的有益的事情 Linux 都能做---
    Windows 下的某些功能确实是我们需要的,那么 Linux 的开发者们和用户也需要这种功能,他们就会去实现这种功能,而且比 Windows 的方式好得多。由于大多数科学家,工程师用的都是 Linux 或者某种商业 UNIX, 所以几乎所有商业的科学工程程序,比如Matlab, Mathematica, AutoCAD, Candence的,Synopsys的,Avant! 的……全都是先有UNIX 的版本(包括Linux),然后再考虑移植给 Windows,甚至根本不移植给Windows,因为 Windows 的机器一般没有足够的能力运行这样的程序。你不要以为只有 Windows 才有 PSpice, UNIX 的 HSpice 要好得多,而且可以运行在大型主机上。当然它们不是免费的,但是它们值那个价钱。

    但是 Windows 下有些东西在 Linux 下没有很相似的,或者你找到很多类似的,但是它们每一个比起 Windows 的那个程序都要差很多,那么原因有两种可能性:

    有一个完全类似的程序,但是由于它乍一看不漂亮,被你忽略了。而其它程序虽然看起来很漂亮,但是它们是一些初学编程的人写的。现在由于 Gtk, Qt 的诞生,Linux 下开发图形界面程序极其简单,很多初中生甚至小学生都可以随手编出一些漂亮不中用的程序。如果你整天寻找这样的程序挑来挑去,永远也找不到你满意的。当然也有一流的程序用 Gtk 和 Qt,比如 GVIM 就可以用 Gtk 作为图形界面,我还知道 Synopsys 一些程序用了 Qt。

    我曾经也犯过这样的错误,从外表区分一切。结果优秀的 FVWM, lftp, Mutt, wget 都被我忽略过。当我找回它们的时候,我是那么的羞愧不已,它们现在都是我的朋友 我第一次看到 FVWM 觉得它只不过是一个有很厚很难看边框的东西。可是现在,我的同学看到 FVWM 都说:"哇!真漂亮。"

    有另一种完全不同的方式可以达到相同的目的,甚至更好。

    很多人很关心 Open Office, Star Office, AbiWord, ... 他们多么盼望有一天某一个Linux 程序能够完全兼容的打开一个复杂的 doc 文档。但是你永远也不可能有那一天。为什么呢?因为微软为了占有市场,必定不会让其它系统的程序能够完全兼容它的文档格式。它一定会不断变化 doc 文档的内部结构,隐藏一些秘密,让其它公司的程序打开 doc 文档时总是有某种问题,从而你必需购买 Microsoft Office 和 Windows。

    你应该想一下,那么多的高智商的大学教授,科学家,学生,他们用的都是 Linux 或者其它类型的 UNIX,他们没有 Word 可用,怎么处理文档呢?这么多年没有一个像Open Office 的程序出现,难道大家没有办法写文档吗?

    显然不是这样。你看看那些高水平的学术杂志,论文,那些大学教授的网页,那些漂亮的幻灯片,它们是什么做的?原来 UNIX 用户早就有非常方便的 troff, LaTeX, SGML等东西可以处理文档,而且它们比起 Word 都要高明的多。Word 显然被这些大拿忽略了,以至于很久以来没有人想在 Linux 下开发一个类似 Word 的程序,除非某些公司想抢微软的饭碗。

    很多人留着 Windows 在硬盘上的原因无非是为了用 Word 和 PowerPoint。我见过一个教授,他的 Windows 笔记本电脑上除了 PowerPoint 什么都没有。有一天演示的时候,他指着堆乱字符说:"对不起,这是一个公式……怎么每次都是这样……" 其实有比PowerPoint 好几百倍的东西可以制造幻灯片,你可以用最简单的方法制造世界一流效果的论文和幻灯片。你待会儿可以看看我的TeX网页,你就会知道为什么我可以完全离开 Windows。

    Windows 能做的那些没用的事情 Linux 永远做不好

    电脑游戏
    有些人说 Linux 下不能玩 Windows 下所能得到的所有游戏。的确,Linux 下虽然也有少量的游戏,比如 Quake。但是它没有 Counter Strike, 没有 Star Craft, ……

    并不是说电脑游戏不该玩,但是应该适可而止。电脑是用来处理事务,帮助你学习,解决问题的工具,而不是一个玩具!整天沉迷于电脑游戏中,而不出去感觉外面的世界,你会变得越来越冷酷,越来越缺乏人情味。你与真实的世界越来越远。

    你可以在 CS 里杀人,你可以在 Tomb Raider 里探险,你甚至可以在 Tony Hawk's Pro Skaters 里滑板…… 但是 It's not real!你虽然有很高的"反恐技巧",但是遇到歹徒的时候,你是那么的怯懦;你虽然控制 Laura 伸手敏捷,但是你打篮球的时候怎么总是被人断球?你虽然可以轻易的在 THPS 里作出一个 "360 kickflip to hangten grind to fakie",但是你踩在自己的滑板上的时候还不会 ollie!

    说回来,如果你偶尔玩一下电脑游戏未尝不可。但是世界上有远比 Windows + PC 更好的游戏方式。Sony 的 PlayStation2, SEGA 的 DreamCast, Nintendo 的 N64,Namco的街机……每一个都比 Windows 游戏精彩,每一个都有如此高的3D性能,以至于Pentium4, Itanium + GForce4 都无法与它们比美!

    Linux 的用户们都是关心解决世界的关键问题的份子,他们哪里有时间用自己的机器来玩游戏啊?他们每天用Linux高效的做完自己的工作就到阳光下享受自然去了。要玩游戏也是玩一些类似推箱子,贪吃蛇之类的智力小游戏。所以,你知道为什么 Linux 几乎没有游戏了吧?

    "整理硬盘,优化系统"

    这是一个非常有意思的话题,仅次于有关"病毒"的话题。相信很多 Windows 用户都有整理硬盘的经历。在很多 Windows 用户眼里,"硬盘用久了,会出现碎片,速度会减慢,需要一个程序来整理,整理硬盘的时候不要做其它工作",这好像是天经地义的事情。

    我也曾经津津有味的看着 Norton Defrag 一点一点的把我的硬盘排序,调整,用图形的方式显示出来,然后报告100% 没有碎片。你的硬盘现在已经达到最佳状态。" 我现在才发觉我那时是多么的幼稚。

    Linux 和 UNIX 用户似乎从来没有"整理硬盘"这种说法呢?你觉得很奇怪吗?如果你觉得很奇怪,那说明你的思想在某种程度上被微软的垃圾程序禁锢了。你需要明白,UNIX 的大型主机很多必须是一天24小时,一年365又1/4天不停运转的,要是每个星期都要整理一次硬盘,在整理的时候几乎不能干任何事情,那是绝对行不通的!

    Linux 机器根本不用整理硬盘,这就是为什么没有看到过 Linux 用户整理硬盘。Linux 的文件系统是比 Windows 的 FAT, FAT32, NTFS 高明得多的文件系统,它们不但可以对文件设置权限,实施完全的保护,而且可以"越用越整齐","越用碎片越少"!你应该把文件大部分放在 Linux 的分区,而不是 Windows 分区,因为它比 Windows分区可靠得多。

    还有更滑稽的事情就是有很多"Norton System Doctor","Windows 优化大师","超级兔仔注册表魔法" 之类的程序存在,而且价格昂贵。似乎一个操作系统本来应该有很多问题,需要别的厂商做程序来"优化"它,而且为了得到优化,你需要付钱!这些问题 Linux 根本就没有,所以不需要什么优化。Linux 内核本身就是高度优化的。


    IDE

    有些人在抱怨为什么 Linux 没有一个良好的 IDE 开发环境。Linux 现在已经有一些IDE 了,但是总是有很多问题。你是不是正在寻找,正在期望 Linux 某一天可以有一个VC那样的开发环境?你有没有发现你正在进入微软给你设下的怪圈?你为什么一定要用 IDE?你说:"IDE 开发迅速,调试方便,适合大型程序……" 那说明微软的程序在你脑子里已经比较根深蒂固,你需要好好清醒一下了,看看我来告诉你。

    高明的 UNIX 程序员不用 IDE,IDE 从来就是给初级 Windows 程序员用的。

    你看看大型的 UNIX 程序,包括 Linux 内核,各种网络服务程序,Xwindow 程序在内,哪一个是 IDE 搞出来的?我们实验室的 EDA 程序也没有一个是 IDE 弄的,我还知道Candence, Synopsys,Mentor 的高性能的图形界面 EDA 程序也都不是 IDE 写的。你信不信,微软的人在写 Windows 本身的时候也根本不用 IDE。微软内部程序员最喜欢的编辑器其实是 VIM,用 VIM 的微软程序员上次向乌干达的可怜儿童捐助了1000多美元,这是值得称赞的。

    有一次某杂志采访一些出名的 Linux 内核程序员,包括 Linus 在内,没有一个人用IDE,有的人用 VIM,有的用 Emacs,只有 Linus 说"GNU Emacs is evil",但是其实他用的是一种跟 Emacs 有同样键绑定功能的 MicroEmacs。大家都是用编辑器编辑了程序文件,然后用 make 这样的自动工具调用 gcc 编译器完成编译工作的。甚至高级的 Windows 程序员也不用 IDE,他们可以从命令行调用 cl,nmake 来编译自己的程序。虽然这样的 Windows 程序员很少,但是他们却是最了解 Windows,最高明的Windows 程序员。

    为什么 UNIX 程序员不用 IDE?明白了这个道理你就能体会到 UNIX 的设计思想了。首先,一个 IDE 集成了编辑器,编译器,汇编器,调试器,跟踪器…… 这个编辑器功能肯定比不上 VIM 或 Emacs,编译器比不上 GCC,汇编器比不上 as,调试器比不上 gdb,ddd, 跟踪器比不上 strace, ltrace, truss。你得到的是一套整合的低能的程序。如果你对调试器的功能不满意,你只好换用另外一套 IDE,但是这套 IDE 的热键,菜单,编辑器功能,按钮…… 跟原来那个有很大不同。你不得不花很多时间来熟悉新的环境,而不能保持原来的某些东西。

    而在 UNIX 下就不一样了。你可以用你最喜欢的 VIM 编辑程序,你在 VIM 里可以调用GNU make,make 可以调用 gcc, ld, ... make 的出错信息可以被 VIM 捕获,VIM 能帮你在源程序里定位。你如果喜欢 icc, 你可以让 make 用 icc 而不是 gcc。你如果觉得 gdb 跟踪变量时比较麻烦,你可以用 ddd 来显示各种数据结构之间的关系。你还可以在 Emacs 里调用 gdb,那样就可以同步显示源代码了。而且 VIM 和 Emacs 还可以编辑很多其它东西,比如信件,LaTeX 文档,HTML,配置文件…… 你不用另外找一个什么编辑器来干这些杂活了。很多程序比如 Mutt, tin 都可以在内部使用 VIM,这样就更方便了。实际上 make 在其它方面还能帮你很多忙,我的每一个比较大型的 LaTeX文档都是用 make 维护的。

    Linux 能干的高精尖的事情 Windows 都干不了

    当然有很多事情是Linux/UNIX的专利了。因为 Windows 只能装在 PC 机上,好像以前也有 Alpha 可以使用 Windows NT,但是就是没见到有人用。PC 机的能力是很低的,像我们编程序处理 NP-Hard 问题的人,用 Windows 的机器显然速度不够,而且有时一个问题算上几天甚至几个星期,Windows 机器是以"死机"著称的,我们怎么能放心?所以几乎所有科学计算程序,EDA 程序,高性能图像处理程序都不是 Windows 的。他们有时也会移植一些给 Windows,但是常常降低那些程序的能力。你比较过 Windows 版本的 Mathematica 和 Linux 的有什么区别吗?

    IBM 制造的最大的并行计算机有 8000 多个处理器,Windows 不可能有能力管理这么多处理器,它用的是什么操作系统?答案是 Linux。

    《泰坦尼克号》电影里的三维动画,那么细腻逼真,Windows机器能做出来吗?不行。那也是 Linux 机器做的。

    民航总局用来训练地情人员的虚拟现实训练设备,Windows 当然无能为力。那都是商业的 IRIX 机器。

    UNIX 是最早支持 TCP/IP 网络协议的系统。它上面有很多可以互相协作的网络服务程序,它们经过多年的使用和修订,已经达到比较完善的程度。而就在1997年,微软的比尔盖茨还在扬言:"Internet 是没有前途的。" 微软的这个"远见卓识"大家应该都已见识,它后来加上的网络服务程序IIS漏洞之多,让公安部都频频发出警报,大家也是见识了的。

    其实你知道了,Windows 没有一样有用的事情能比 UNIX 干的更好。

    Linux 干不了的有用的事情 Windows 照样干不了
    当然 Linux 不是万能的。它也有不能干的事情,电脑也有干不了的事情。但是 Linux干不了的事情,Windows 肯定也干不了。这些事情就是我们需要探索,需要努力的事情了。在你探索的过程中,Linux 必定是你的好伙伴。

    不要把Linux和Xwindow掩盖起来!不要把我们的用户当成傻瓜。

    什么?你早就知道 Windows 是垃圾?噢!你怎么不早说呢!害我废话这么多。嘿嘿。

    "好了。你知道 Windows 是垃圾,你现在用什么"

    "Linux + Xwindow"

    "那我问你,Xwindow 是什么样的?"

    "不就是跟 Windows 差不多吗?只不过 'Start' 按钮比较方,而且上面不是一个Windows 标志,而是一个脚丫子。点击一下居然还有很漂亮的中文菜单。我喜欢!"

    "你知道什么是'根窗口'吗?"

    "不知道。从来没听说过呢?"

    "根窗口就是遮盖整个屏幕的那个最大的窗口。"

    "哪儿有什么窗口啊!我没有看到呢?"

    你发现了问题吗?这些 Linux 用户说是在用 Linux 和 Xwindow,但是他们对 Linux和 Xwindow 几乎完全不了解。很多人用了那么久 Xwindow 都不知道根窗口是什么东西,不知道其实按钮也是窗口,不知道窗口管理器和其它程序有什么关系,大家都以为窗口上面的按钮是程序自己放上去的,不知道窗口? quot;class name","resource name"是什么东西。他们也不知道 .Xdefaults 是用来干什么的。特别是他们很多人都不知道 Xwindow 的字体是如何命名的,什么是 fontset,有了一个新的字体也不知道怎么安装。

    他们被遮在 Linux 之上的一层一层的包装迷惑了,他们等待有图形界面的工具来帮助完成一切事情,他们认为 Linux 跟 Windows 一样,只是麻烦一点。他们知道 Linux内核很好,但是他们感觉不到 Linux 和 Xwindow 在操作层面的天生的先进性,随后不久就把 Linux 完全删除掉了。你发现没有,要用户理解 UNIX 和 Xwindow 的操作层面的先进性,才是留住用户的最好办法。如果用户体会不到操作时的方便和高效,内核再好他们也不会理会。

    但是用摹仿 Windows 的作法来吸引用户,永远会失败的。因为 Linux 如果摹仿Windows那一套低效率的方式,那么 Linux 的这套"低效率方式"永远比不上Windows 的那一套"低效率方式"。那么用户就会说:"这个 Linux,没有一样比的上 Windows。"

    Linux 天生就是继承了 UNIX 的高效的工作方式,为什么我们要把它掩盖起来?我们为什么只告诉用户 KDE 的菜单怎么用?我们为什么不能像早期的 Xwindow 书籍那样第一节就告诉用户什么是 X server, 什么是 X client,什么是 Window Manager, 什么是根窗口。第二章就告诉用户窗口有哪些属性,什么是 classname, resource name, hint,怎样使用 .Xdefaults, xrdb ……

    在这里我又不得不说一下那些 Linux 的发行公司和写书的人,他们把 Linux 和Xwindow 包装起来,却没有从基本上告诉用户 Xwindow 的工作原理。很多书籍讲授的层次就是在Gnome, KDE 的菜单操作的层次,靠大量抓图来占篇幅,"繁荣"Linux 书籍市场。

    现在很多人已经把能够利用别人的库写出一个好看的程序作为自己编程水平的象征。在这"图形化","可视化" 的年代,你如果还在用 troff, LaTeX 写文档,你还在用VIM 自己编辑 HTML,用 Mutt 处理邮件,你还在用文本模式的 gdb 调试程序,你还在用Xlib 写程序, 你还在用 tin 上 USENET,你还在自己写 Makefile,写机器代码,你还在玩 Clossal Cave 这样的字符模式冒险游戏,那你就是老古董。

    其实这种思想是错误的。虽然你是一个坚决的 Linux 支持者,但是你的思想是 Windows的思想。你认为图形界面,菜单,按钮就可以解决一切问题,就可以给你高效方便。你还是没能摆脱微软给你的潜移默化的东西。你其实离不开 Windows 那样的环境,你迟早会删掉自己的 Linux。
    查看(686) 评论(4) 收藏 分享 管理

  • 对常见的WEB服务器和应用服务器的介绍

    2009-01-24 15:36:21

           在UNIX和LINUX平台下使用最广泛的免费HTTP服务器是W3C、NCSA和APACHE服务器,而Windows平台NT/2000/2003使用IIS的WEB服务器。

      在选择使用WEB服务器应考虑的本身特性因素有:性能、安全性、日志和统计、虚拟主机、代理服务器、缓冲服务和集成应用程序等,下面介绍几种常用的WEB服务器。

      ① Microsoft IIS

      Microsoft的Web服务器产品为Internet Information Server (IIS), IIS 是允许在公共Intranet或Internet上发布信息的Web服务器。IIS是目前最流行的Web服务器产品之一,很多著名的网站都是建立在IIS的平台上。IIS提供了一个图形界面的管理工具,称为 Internet服务管理器,可用于监视配置和控制Internet服务。

      IIS是一种Web服务组件,其中包括Web服务器、FTP服务器、NNTP服务器和SMTP服务器,分别用于网页浏览、文件传输、新闻服务和邮件发送等方面,它使得在网络(包括互联网和局域网)上发布信息成了一件很容易的事。它提供ISAPI(Intranet Server API)作为扩展Web服务器功能的编程接口;同时,它还提供一个Internet数据库连接器,可以实现对数据库的查询和更新。

      ② IBM WebSphere

      WebSphere Application Server 是 一 种功能完善、开放的Web应用程序服务器,是IBM电子商务计划的核心部分,它是基于 Java 的应用环境,用于建立、部署和管理 Internet 和 Intranet Web 应用程序。 这一整套产品进行了扩展,以适应 Web 应用程序服务器的需要,范围从简单到高级直到企业级。

      WebSphere 针对以 Web 为中心的开发人员,他们都是在基本 HTTP服务器和 CGI 编程技术上成长起来的。IBM 将提供 WebSphere 产品系列,通过提供综合资源、可重复使用的组件、功能强大并易于使用的工具、以及支持 HTTP 和 IIOP 通信的可伸缩运行时环境,来帮助这些用户从简单的 Web 应用程序转移到电子商务世界。

    WebLogic Server 是一种多功能、基于标准的web应用服务器,为企业构建自己的应用提供了坚实的基础。各种应用开发、部署所有关键性的任务,无论是集成各种系统和数据库,还是提交服务、跨 Internet 协作,起始点都是 BEA WebLogic Server。由于 它具有全面的功能、对开放标准的遵从性、多层架构、支持基于组件的开发,基于 Internet 的企业都选择它来开发、部署最佳的应用。

      BEA WebLogic Server 在使应用服务器成为企业应用架构的基础方面继续处于领先地位。BEA WebLogic Server 为构建集成化的企业级应用提供了稳固的基础,它们以 Internet 的容量和速度,在连网的企业之间共享信息、提交服务,实现协作自动化。BEA WebLogic Server 的遵从 J2EE 、面向服务的架构,以及丰富的工具集支持,便于实现业务逻辑、数据和表达的分离,提供开发和部署各种业务驱动应用所必需的底层核心功能。

      ④ IPlanet Application

      IPlanet Application Server作为Sun与Netscape联盟产物的iPlanet公司生产的iPlanet Application Server 满足最新J2EE规范的要求。它是一种完整的WEB服务器应用解决方案,它允许企业以便捷的方式,开发、部署和管理关键任务 Internet 应用。该解决方案集高性能、高度可伸缩和高度可用性于一体,可以支持大量的具有多种客户机类型与数据源的事务。

      iPlanet Application Server的基本核心服务包括事务监控器、多负载平衡选项、对集群和故障转移全面的支持、集成的XML 解析器和可扩展格式语言转换(XLST)引擎以及对国际化的全面支持。iPlanet Application Server 企业版所提供的全部特性和功能,并得益于J2EE系统构架,拥有更好的商业工作流程管理工具和应用集成功能。

    ⑤Oracle IAS

      Oracle iAS的英文全称是Oracle Internet Application Server,即Internet应用服务器,Oracle iAS是基于Java的应用服务器,通过与Oracle 数据库等产品的结合,Oracle iAS能够满足Internet应用对可靠性、可用性和可伸缩性的要求。

      Oracle iAS最大的优势是其集成性和通用性,它是一个集成的、通用的中间件产品。在集成性方面,Oracle iAS将业界最流行的HTTP服务器Apache集成到系统中,集成了Apache的Oracle iAS通信服务层可以处理多种客户请求,包括来自Web浏览器、胖客户端和手持设备的请求,并且根据请求的具体内容,将它们分发给不同的应用服务进行处理。在通用性方面,Oracle iAS支持各种业界标准,包括 JavaBeans、CORBA、Servlets以及XML标准等,这种对标准的全面支持使得用户很容易将在其他系统平台上开发的应用移植到Oracle平台上。

      ⑥ Apache

      Apache源于NCSAhttpd服务器,经过多次修改,成为世界上最流行的Web服务器软件之一。Apache是自由软件,所以不断有人来为它开发新的功能、新的特性、修改原来的缺陷。Apache的特点是简单、速度快、性能稳定,并可做代理服务器来使用。本来它只用于小型或试验Internet网络,后来逐步扩充到各种Unix系统中,尤其对Linux的支持相当完美。

      Apache是以进程为基础的结构,进程要比线程消耗更多的系统开支,不太适合于多处理器环境,因此,在一个Apache Web站点扩容时,通常是增加服务器或扩充群集节点而不是增加处理器。到目前为止Apache仍然是世界上用的最多的Web服务器,世界上很多著名的网站都是Apache的产物,它的成功之处主要在于它的源代码开放、有一支开放的开发队伍、支持跨平台的应用(可以运行在几乎所有的Unix、Windows、Linux系统平台上)以及它的可移植性等方面。

      ⑦ Tomcat

      Tomcat是一个开放源代码、运行servlet和JSP Web应用软件的基于Java的Web应用软件容器。Tomcat Server是根据servlet和JSP规范进行执行的,因此我们就可以说Tomcat Server也实行了Apache-Jakarta规范且比绝大多数商业应用软件服务器要好。

      Tomcat是Java Servlet 2.2和JavaServer Pages 1.1技术的标准实现,是基于Apache许可证下开发的自由软件。Tomcat是完全重写的Servlet API 2.2和JSP 1.1兼容的Servlet/JSP容器。Tomcat使用了JServ的一些代码,特别是Apache服务适配器。随着Catalina Servlet引擎的出现,Tomcat第四版号的性能得到提升,使得它成为一个值得考虑的Servlet/JSP容器,因此目前许多WEB服务器都是采用Tomcat。

  • C++中,头文件stdlib.h主要包含的函数

    2009-01-23 17:21:04

    包含函数: 
    1函数名称: calloc 
    函数原型: void * calloc(unsigned n,unsign size); 
    函数功能: 分配n个数据项的内存连续空间,每个数据项的大小为size 
    函数返回: 分配内存单元的起始地址,如果不成功,返回0 

    2函数名称: free 
    函数原型: void free(void* p); 
    函数功能: 释放p所指的内存区 
    函数返回: 
    参数说明: p-被释放的指针 

    3函数名称: malloc 
    函数原型: void * malloc(unsigned size); 
    函数功能: 分配size字节的存储区 
    函数返回: 所分配的内存区地址,如果内存不够,返回0 

    4函数名称: realloc 
    函数原型: void * realloc(void * p,unsigned size); 
    函数功能: 将p所指出的已分配内存区的大小改为size,size可以比原来分配的空间大或小 
    函数返回: 返回指向该内存区的指针.NULL-分配失败 

    5函数名称: rand 
    函数原型: int rand(void); 
    函数功能: 产生0到32767间的随机整数(0到0x7fff之间) 
    函数返回: 随机整数 

    6函数名称: abort 
    函数原型: void abort(void) 
    函数功能: 异常终止一个进程. 

    7函数名称: exit 
    函数原型: void exit(int state) 
    函数功能: 程序中止执行,返回调用过程 
    函数返回: 
    参数说明: state:0-正常中止,非0-非正常中止 

    8函数名称: getenv 
    函数原型: char* getenv(const char *name) 
    函数功能: 返回一个指向环境变量的指针 
    函数返回: 环境变量的定义 
    参数说明: name-环境字符串 

    9函数名称: putenv 
    函数原型: int putenv(const char *name) 
    函数功能: 将字符串name增加到DOS环境变量中 
    函数返回: 0:操作成功,-1:操作失败 
    参数说明: name-环境字符串 

    10函数名称: labs 
    函数原型: long labs(long num) 
    函数功能: 求长整型参数的绝对值 
    函数返回: 绝对值 

    11函数名称: atof 
    函数原型: double atof(char *str) 
    函数功能: 将字符串转换成一个双精度数值 
    函数返回: 转换后的数值 
    参数说明: str-待转换浮点型数的字符串 

    12函数名称: atoi 
    函数原型: int atoi(char *str) 
    函数功能: 将字符串转换成一个整数值 
    函数返回: 转换后的数值 
    参数说明: str-待转换为整型数的字符串 

    13函数名称: atol 
    函数原型: long atol(char *str) 
    函数功能: 将字符串转换成一个长整数 
    函数返回: 转换后的数值 
    参数说明: str-待转换为长整型的字符串 

    14函数名称: ecvt 
    函数原型: char *ecvt(double value,int ndigit,int *dec,int *sign) 
    函数功能: 将浮点数转换为字符串 
    函数返回: 转换后的字符串指针 
    参数说明: value-待转换底浮点数,ndigit-转换后的字符串长度 

    15函数名称: fcvt 
    函数原型: char *fcvt(double value,int ndigit,int *dec,int *sign) 
    函数功能: 将浮点数变成一个字符串 
    函数返回: 转换后字符串指针 
    参数说明: value-待转换底浮点数,ndigit-转换后底字符串长度
  • C语言的常用类型转换函数

    2009-01-23 13:37:24

    atof(将字符串转换成浮点型数)
    相关函数
         atoi,atol,strtod,strtol,strtoul
    表头文件
         #include <stdlib.h>
    定义函数
         double atof(const char *nptr);
    函数说明
         atof()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时('')才结束转换,并 将结果返回。参数nptr字符串可包含正负号、小数点或E(e)来表示指数部分,如123.456或123e-2。
    返回值
         返回转换后的浮点型数。
    附加说明
         atof()与使用strtod(nptr,(char**)NULL)结果相同。
    范例
         /* 将字符串a 与字符串b转换成数字后相加*/
    #include<stdlib.h>
    main()
    {
    char *a=”-100.23”;
    char *b=”200e-2”;
    float c;
    c=atof(a)+atof(b);
    printf(“c=%.2f ”,c);
    }
    执行
         c=-98.23


         
    atoi(将字符串转换成整型数)
    相关函数
         atof,atol,atrtod,strtol,strtoul
    表头文件
         #include<stdlib.h>
    定义函数
         int atoi(const char *nptr);
    函数说明
         atoi()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时('')才结束转换,并将结果返回。
    返回值
         返回转换后的整型数。
    附加说明
         atoi()与使用strtol(nptr,(char**)NULL,10);结果相同。
    范例
         /* 将字符串a 与字符串b转换成数字后相加*/
    #include<stdlib.h>
    mian()
    {
    char a[]=”-100”;
    char b[]=”456”;
    int c;
    c=atoi(a)+atoi(b);
    printf(c=%d ”,c);
    }
    执行
         c=356


         
    atol(将字符串转换成长整型数)
    相关函数
         atof,atoi,strtod,strtol,strtoul
    表头文件
         #include<stdlib.h>
    定义函数
         long atol(const char *nptr);
    函数说明
         atol()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时('')才结束转换,并将结果返回。
    返回值
         返回转换后的长整型数。
    附加说明
         atol()与使用strtol(nptr,(char**)NULL,10);结果相同。
    范例
         /*将字符串a与字符串b转换成数字后相加*/
    #include<stdlib.h>
    main()
    {
    char a[]=”1000000000”;
    char b[]=” 234567890”;
    long c;
    c=atol(a)+atol(b);
    printf(“c=%d ”,c);
    }
    执行
         c=1234567890


         
    gcvt(将浮点型数转换为字符串,取四舍五入)
    相关函数
         ecvt,fcvt,sprintf
    表头文件
         #include<stdlib.h>
    定义函数
         char *gcvt(double number,size_t ndigits,char *buf);
    函数说明
         gcvt()用来将参数number转换成ASCII码字符串,参数ndigits表示显示的位数。gcvt()与ecvt()和fcvt()不同的地方 在于,gcvt()所转换后的字符串包含小数点或正负符号。若转换成功,转换后的字符串会放在参数buf指针所指的空间。
    返回值
         返回一字符串指针,此地址即为buf指针。
    附加说明
         
    范例
         #include<stdlib.h>
    main()
    {
    double a=123.45;
    double b=-1234.56;
    char *ptr;
    int decpt,sign;
    gcvt(a,5,ptr);
    printf(“a value=%s ”,ptr);
    ptr=gcvt(b,6,ptr);
    printf(“b value=%s ”,ptr);
    }
    执行
         a value=123.45
    b value=-1234.56


         
    strtod(将字符串转换成浮点数)
    相关函数
         atoi,atol,strtod,strtol,strtoul
    表头文件
         #include<stdlib.h>
    定义函数
         double strtod(const char *nptr,char **endptr);
    函数说明
         strtod()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,到出现非数字或字符串结束时('')才结束转换, 并将结果返回。若endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr传回。参数nptr字符串可包含正负号、小数 点或E(e)来表示指数部分。如123.456或123e-2。
    返回值
         返回转换后的浮点型数。
    附加说明
         参考atof()。
    范例
         /*将字符串a,b,c 分别采用10,2,16 进制转换成数字*/
    #include<stdlib.h>
    mian()
    {
    char a[]=”1000000000”;
    char b[]=”1000000000”;
    char c[]=”ffff”;
    printf(“a=%d ”,strtod(a,NULL,10));
    printf(“b=%d ”,strtod(b,NULL,2));
    printf(“c=%d ”,strtod(c,NULL,16));
    }
    执行
         a=1000000000
    b=512
    c=65535


         
    strtol(将字符串转换成长整型数)
    相关函数
         atof,atoi,atol,strtod,strtoul
    表头文件
         #include<stdlib.h>
    定义函数
         long int strtol(const char *nptr,char **endptr,int base);
    函数说明
         strtol()会将参数nptr字符串根据参数base来转换成长整型数。参数base范围从2至36,或0。参数base代表采用的进制方式,如 base值为10则采用10进制,若base值为16则采用16进制等。当base值为0时则是采用10进制做转换,但遇到如'0x'前置字符则会使用 16进制做转换。一开始strtol()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,再遇到非数字或字符串结束 时('')结束转换,并将结果返回。若参数endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr返回。
    返回值
         返回转换后的长整型数,否则返回ERANGE并将错误代码存入errno中。
    附加说明
         ERANGE指定的转换字符串超出合法范围。
    范例
         /* 将字符串a,b,c 分别采用10,2,16进制转换成数字*/
    #include<stdlib.h>
    main()
    {
    char a[]=”1000000000”;
    char b[]=”1000000000”;
    char c[]=”ffff”;
    printf(“a=%d ”,strtol(a,NULL,10));
    printf(“b=%d ”,strtol(b,NULL,2));
    printf(“c=%d ”,strtol(c,NULL,16));
    }
    执行
         a=1000000000
    b=512
    c=65535


         
    strtoul(将字符串转换成无符号长整型数)
    相关函数
         atof,atoi,atol,strtod,strtol
    表头文件
         #include<stdlib.h>
    定义函数
         unsigned long int strtoul(const char *nptr,char **endptr,int base);
    函数说明
         strtoul()会将参数nptr字符串根据参数base来转换成无符号的长整型数。参数base范围从2至36,或0。参数base代表采用的进制方 式,如base值为10则采用10进制,若base值为16则采用16进制数等。当base值为0时则是采用10进制做转换,但遇到如'0x'前置字符则 会使用16进制做转换。一开始strtoul()会扫描参数nptr字符串,跳过前面的空格字符串,直到遇上数字或正负符号才开始做转换,再遇到非数字或 字符串结束时('')结束转换,并将结果返回。若参数endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr返回。
    返回值
         返回转换后的长整型数,否则返回ERANGE并将错误代码存入errno中。
    附加说明
         ERANGE指定的转换字符串超出合法范围。
    范例
         参考strtol()


         
    toascii(将整型数转换成合法的ASCII 码字符)
    相关函数
         isascii,toupper,tolower
    表头文件
         #include<ctype.h>
    定义函数
         int toascii(int c)
    函数说明
         toascii()会将参数c转换成7位的unsigned char值,第八位则会被清除,此字符即会被转成ASCII码字符。
    返回值
         将转换成功的ASCII码字符值返回。
    范例
         #include<stdlib.h>
    main()
    {
    int a=217;
    char b;
    printf(“before toascii () : a value =%d(%c) ”,a,a);
    b=toascii(a);
    printf(“after toascii() : a value =%d(%c) ”,b,b);
    }
    执行
         before toascii() : a value =217()
    after toascii() : a value =89(Y)


         
    tolower(将大写字母转换成小写字母)
    相关函数
         isalpha,toupper
    表头文件
         #include<stdlib.h>
    定义函数
         int tolower(int c);
    函数说明
         若参数c为大写字母则将该对应的小写字母返回。
    返回值
         返回转换后的小写字母,若不须转换则将参数c值返回。
    附加说明
         
    范例
         /* 将s字符串内的大写字母转换成小写字母*/
    #include<ctype.h>
    main()
    {
    char s[]=”aBcDeFgH12345;!#$”;
    int i;
    printf(“before tolower() : %s ”,s);
    for(i=0;I<sizeof(s);i++)
    s=tolower(s);
    printf(“after tolower() : %s ”,s);
    }
    执行
         before tolower() : aBcDeFgH12345;!#$
    after tolower() : abcdefgh12345;!#$


         
    toupper(将小写字母转换成大写字母)
    相关函数
         isalpha,tolower
    表头文件
         #include<ctype.h>
    定义函数
         int toupper(int c);
    函数说明
         若参数c为小写字母则将该对映的大写字母返回。
    返回值
         返回转换后的大写字母,若不须转换则将参数c值返回。
    附加说明
         
    范例
         /* 将s字符串内的小写字母转换成大写字母*/
    #include<ctype.h>
    main()
    {
    char s[]=”aBcDeFgH12345;!#$”;
    int i;
    printf(“before toupper() : %s ”,s);
    for(i=0;I<sizeof(s);i++)
    s=toupper(s);
    printf(“after toupper() : %s ”,s);
    }
    执行
         before toupper() : aBcDeFgH12345;!#$
    after toupper() : ABCDEFGH12345;!#$
  • vmware 导致vs2005打不开

    2009-01-14 17:42:42

        你是不是装过VMware和VS2005,然后打开VS2005时,就出现启动画面停在那,无响应??
        这是由于VMDebugger导致VS2005无法启动,就出现了VS2005启动时只显示到启动界面就定住的情况。发现了原因是VMware装上后在VS2005上添加了一个VMDebugger工具栏,VMware服务没有加载,就导致了VS2005无法启动。然后在工具->外接程序管理器中把启动选项去掉,但是下次启动VS2005依然加载VMDebugger。
        解决办法: regedit打开注册表,
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\AddIns\VMDebugger.Connect目录下把Loadbehavīor项改为0,问题解决。 

  • 311/212>

    数据统计

    • 访问量: 15538
    • 日志数: 32
    • 建立时间: 2008-09-07
    • 更新时间: 2010-12-16

    RSS订阅

    Open Toolbar