看到Silverlight 3 Beta发布了后,听说ASP.NET MVC 1.0发布了,就去ASP.NET MVC的官方站看了下,果然看到是1.0发布了,下面是下载地址:

http://go.microsoft.com/fwlink/?LinkId=144444

下面是ASP.NET MVC的在线文档:

http://go.microsoft.com/fwlink/?LinkId=145989 

看了下Release Notes(带目录才6页),基本和RC2没啥变化。

已知下面的Visual Studio add-ins 会和MVC的安装程序有冲突:

· PowerCommands

· Clone Detective

· Azure Tools

可以下载下面的Hotfix修复

https://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=16827&wa=wsignin1.0

 

经过5个preview和2个RC后,好不容易终于发了1.0,可喜可贺,哈哈…

下面是一些资源:

ASP.NET MVC 文章推荐

ASP.NET MVC 入门系列教程(基本是Preview5的,和正式版有一点点出入)

重典的教程:

Asp.net Mvc Framework 系列

Asp.net Mvc 入门视频教程(试水)

老赵的视频教程:http://www.microsoft.com/china/msdn/events/webcasts/shared/webcast/Series/MVC.aspx

从零开始学习ASP.NET MVC(未完) by ziqiu.zhang

如果你英文比较好的话,可以直接到ASP.NET MVC 官网上看,上面有入门教程.

 

一些ASP.NET MVC可用的模板引擎:

  • Brail
  • NHaml
  • NVelocity
  • XSLT

    上面的4个都包括在MVC Contrib中了

    还有我比较喜欢的一个:Spark 

    一个用于ASP.NET MVC的业务实体验证的开源项目:xVal,介绍见xVal - a validation framework for ASP.NET MVCxVal 0.8 (Beta) Now Released

     

  • posted @ 2009-06-03 20:04 咖啡色 阅读(17) | 评论 (2)编辑
    现在,微软推出了 Sql Server 2005 简体中文版,不过大家基本上都是使用的 Sql Server 2005 的 Express 版,不过这个版本的管理界面并不好,所以我删除了这个版本,开始安装 Developer Edition 版。

    不过,在安装开发版的时候,安装程序在检查系统需求的时候,出现了如下图的错误,信息:



    查看安装帮助后,发现有这一段话:

    1 在 Microsoft Windows 2003 或 Windows XP 桌面上,依次单击“开始”、“运行”,然后在“打开”中键入 regedit.exe,再单击“确定”。在 Windows 2000 中,使用 regedt32.exe 启动注册表编辑器。

    2 定位到以下注册表项:

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib]

    "Last Counter"=dword:00000ed4 (5276)

    "LastHelp"=dword:00000ed5 (5277)

    3 上一步的“Last Counter”值 (5276) 必须与以下注册表项中“Perflib\004”(不是009,004对应中文版操作系统,009对应英文版操作系统)的“Counter”项的最大值匹配,并且上一步的“Last Help”值 (5277) 必须与以下注册表项中“Perflib\004”的“Help”项的最大值匹配。

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009]

    http://www.knowsky.com

    注意 009 是英文中的一个示例。“Last Counter”和“Last Help”值是由 Windows 动态分配的;这两个值会因计算机的不同而不同。

    4 如有必要,可修改“\Perflib”项中的“Last Counter”和“Last Help”值的值:在右侧窗格中,右键单击“Last Counter”或“Last Help”,单击“修改”,再单击“Base = "Decimal"”,在“值数据”中设置值,再单击“确定”。如有必要,对另一个项重复以上过程,然后关闭注册表编辑器。

    5 再次运行 SQL Server 安装程序。

    我完全按照以上叙述的步骤修改了注册表,可是始终出现这个问题。后来经过分析,发现原因有两个,即注册表值中的数字与实际数字不符合,还有就是根本不应该修改009这个注册表项中的值。因为我们使用的大多数是简体中文系统。所以修改方式如下:

    首先打开 “控制面板” --> “管理工具” 后,双击 “性能”,显示出性能分析器界面,在右边的控制台节点中展开 “性能日志和警报”后,用鼠标选择 “性能计数器”。

    然后双击右边窗口的 “System Overview”,显示 System Overview 属性窗口。



    在窗口中的 “添加对象” 和 “添加计数器” 按钮分别单击一次,但单击后显示的窗口不要去动,直接选择取消。

    分别单击两个按钮后,单击缺定关闭 System Overview 属性窗口,然后关闭性能窗口。

    然后在 “开始” -->   “运行”中输入 regedit,开启注册表编辑器。

    定位到
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib
    处,在右边的树形目录下可以看到Perflib目录下有004和009两个子目录。

    在Sql Server 2005 的安装帮助文件中说的是需要查看009目录的注册表项,而我们大部分人使用的是简体中文的操作系统,所以不能按帮助中说的,而是需要注意004目录中的内容。

    打开004 目录中的内容,可以看到如下图:



    我们分别双击 “Counter” 项 和 “Help” 项,察看其中的最后的数字,如下图:

    Counter 项的内容:




    Helper 项内容



    这时候,我们知道,Counter 项的数字是5556,Helper项的内容是5557。

    然后,操作注册表编辑器的左边的目录树,定位到Perflib目录下,并注意检查右边窗口的 “Laster Counter”项和“Laster Help”项的值,并把Laster Counter的值改成刚才记录下的Counter值5556,把Laster Help 的值改成刚才记下的 Help的值5557。

    要注意的是,修改数字的时候,输入的时候必须选则基数是 “十进制”,否则数字将不匹配,Sql Server 2005 检查将再次失败。

    然后关闭注册表编辑器,开始安装 Sql Server 2005 ,绝对可以安装成功。


    注意:我上面说的5556和5557的数字,每个人每台计算机的数字都是不同的,所以必须要一步步来,检查出准确的数字填入,而不是直接看到5556/5557填写,这样还不行的。

    posted @ 2009-05-08 08:55 咖啡色 阅读(30) | 评论 (0)编辑

        偶然在网上看到Dasblog,发现竟然是aspx写的,而且速度还蛮快,然后在看过了azure的博客后,深为其美工所吸引,而是跑去官方网上下载,发现竟然还可以下载源代码,哈~

        这次终于找到学习的对象了,俺下载的版本:dasBlog 2.3 (2.3.9074.18820) 

    立刻下载了web文件和源代码。解压后先将web文件放到wwwroot下的dasblog文件夹,然后设置成虚拟目录就ok了。

    在浏览器敲入地址:http://localhost/dasblog,回车,哦也,出现了dasblog的首页。

       我开始以为dasblog是用数据库的,在webconfig中找了好久,楞是没找到配置路径,哈,后来在院子了搜了下,知道dasblog原来是用xml存储数据的,果然够特别。

       对了,默认密码为admin,admin,如果要修改的话到siteconfig目录找到secrityconfig中修改。记得将content,logs,siteconfig目录属性修改为everyone可以read&write。

    ok,下一篇再开始研究dasblog的源代码。^_^

     

     

    posted @ 2009-04-02 12:20 咖啡色 阅读(97) | 评论 (0)编辑
         摘要: 在LJ公司干了三年,合同到期,目前正在交接,估计2星期后哥们就没地去了,有招人的没?赏口饭吃,呵~附上本人简历(在51job根据提示填写的,凑合着看吧):简历基 本 信 息 姓 名: 余飞奇性 别: 男 出生日期:1985年1月4日居 住 地:上海市 工作年限:三年以上 户 口: 安徽 地 址: 上海市虹口区玉田新村42号104(200083)电子邮件:icelenix@163.com (1376... 阅读全文
    posted @ 2009-04-01 12:01 咖啡色 阅读(158) | 评论 (3)编辑

    Form.KeyDown, KeyUp, KeyPress在给程序添加快捷键时都不好使,因为某些控件会拦截按键信息,比如你在Form上放一个Button,把焦点移到它上面,随便按个什么键,Form.KeyXXX都是不会引发的。

    解决这个问题的正规方法是重写Form.ProcessCmdKey方法,它会在控件处理按键信息之前被调用。但是,假如要更方便,更灵活地处理快捷键--比如由另一个类处理,这个方法就不适用了。这时,最好的办法是:

    给窗体添加一个额外的菜单,设计好菜单项的Click事件处理器,然后给它设置ShortcutKeys属性,最后,设置菜单的Visible为false.

    需要注意的是,如果你一个窗体有多于一个菜单的话,并且不同的菜单里的菜单项的快捷键有重复的,这时最多只有一个菜单项的事件处理器会被调用,具体说就是按照菜单项被Add到父菜单项的DropDownItems里的顺序(无论父菜单项有没有被Add到另一个父控件中去!)查找,第一个Enable为真的菜单项,它的事件处理器会被调用。这个优先顺序与菜单的可见与否无关,但是与Enable有关。

    ps:在使用微软的Debug Symbols + Reflector的人肉调试过程中,发现几行代码,作用是如果一个WinForms进程附加了调试器,那么在这个程序里按Ctrl+Break会使用Debuger冻结程序。

    pps: .Net Framework的源代码是不是还不可用? 按Scott的截图设置了VS 2008 Express,结果只能得到Symbols,源码还是没有,一想转到微软的代码里就提示是否显示汇编:(

     

    来源:http://www.cnblogs.com/deerchao/archive/2008/01/07/1028298.html

    posted @ 2009-03-02 13:42 咖啡色 阅读(32) | 评论 (0)编辑
    这是最终确定的 JavaScript 基于消息传递编程风格的文章“OOP 诡异教程(上)”的下篇。原文地址:http://let-in.blogspot.com/2007/06/oop.html。原来的想法是以风格开头,谈到 JavaScript 的内部机制,但作者 lichray 迟迟没有动键盘,认为不如利用已有的风格做一套机制出来,这样可能更有意义。于是,就有了这个更加“诡异”的下篇。

    四. 扩展的实现
    上文最后给出了一个“看上去很美”的基于消息传递的编程风格,比如构造一个 People 类的代码类似:

    function People () {
      var money = 0
      function setMoney (dollars) {
        money = dollars
      }
      function pay (dollars) {
        money -= dollars
      }
      return (function (verb) {
        return eval(verb)
      })
    }

    有了这样的语法我们就可以描述不少句子了。但是存在一个问题:现实中的 Objects 之间是存在关系的——比如,forrest 是个 IQ 为 75 的傻子,傻子是 People 的一种。而我们仅仅是生搬硬套了一种语法而割裂了这种 "is-a" 关系。现在我们的工作,目的之一就是让这样一个“真切”的世界从我们已有的编程风格的地基上拔地而起。
    到底应该怎样做才能使 Fool 产生的对象都能响应 People 的消息呢?我们要给 Fool 产生的对象(也就是返回的那个匿名函数啦)都添加这样一种能力:如果在 Fool 中响应不了消息,那就反馈给 People 响应。

    function Fool (iq) {
      var IQ = iq || 0
      function init (iq) {
        IQ = iq
      }
      return (function (verb) {
        try {
          return eval(verb)
        } catch (e) {
          return People()(verb)
        }
      })
    }

    js> forrest = Fool()
    js> forrest('init')(75)
    js> forrest('IQ')
    75
    js> forrest('money')
    0

    五. 语法扩展和代码生成
    这下代码量增加了很多,强迫潜在的使用者们在创建每个类时都这样写那实在是令人抓狂。本来这篇文章应该不提此类问题的解决,但考虑到有益于读者理解“机制”这个抽象概念,这里给出一个可行的方案——把普通的类代码用 Function() 函数重编译为可用的 JavaScript 函数。也就是说,我们能给出类扩展的代码并指定被扩展的类来获取类似上文的代码:

    Fool = extend('People()', function (iq){
      var IQ = iq || 0
      function init (iq) {
        IQ = iq
      }
    })

    为了方便字符串操作,我们希望编译后的代码的参数部分(如 People())都集中出现在一个位置且尽可能便于定位。在函数头添加一句

    var origin = People()

    当然是可行的,这样还能使 Fool 内部显式引用到其超类。但这样还不够漂亮。我们修改编译后的样例代码为:

    function () {
      return (function (origin) {
        var IQ = 0
        function init (iq) {
          IQ = iq
        }
        return (function (verb) {
          try {
            return eval(verb)
          } catch (e) {
            return origin(verb)
          }
        })
      })(People())
    }

    这个利用参数传递变量的小技巧不值得学习,实际效率不高。但在这篇文章中,这样绑定特殊变量的技术是标准方案。
    那么,extend() 函数的实现为:

    function extend (originc, code) {
      function argsArea (code) {
        // 题外话,正则表达式也有不值得使用的时候
        return code.slice(code.indexOf('(')+1, code.indexOf(')'))
      }
      function bodyCode (code) {
        // 不用 trim() 了,没事儿找事儿
        return code.slice(code.indexOf('{')+1, code.lastIndexOf('}'))
      }
      function format (body) {
        var objc = bodyCode(function () {
          return (function (verb) {
            try {
              return eval(verb)
            } catch (e) {
            return origin(verb)
            }
          })
        }.toString())
        return 'return (function (origin) {'+body+objc+'})('+originc+')'
      }
      var $ = code.toString()
      return Function(argsArea($), format(bodyCode($)))
    }

    这样前文提到过的 extend 的实例代码就可以正常运行了,测试代码不再重复。

    六. 机制完备化
    这样,我们的基于消息传递编程风格的一套面向对象机制就确定下来了。机制是宪法,是语言的根本大法,有了它,我们就可以通过修改代码生成器,很快地给这套机制进行完备化。
    想法有很多,例子只举两个。
    第一个例子:类的定义中应该能直接引用到将产生的对象 self。答案只有一句话:把返回的那个作为对象的匿名函数命名为 self。
    第二个例子:既然是单继承模式,应当存在一个顶层类 AbsObj,使没有指定继承的类自动继承它。答案也只有一句话:在 extend 函数体第一行添加代码:

    if (arguments.length == 1) {
      code = originc
      originc = 'AbsObj()'
    }

    然后手工构造设计 AbsObj 类,为空也无所谓。不过当然了,一般都会给顶层类添加一些全局性质的消息绑定。由于是“底层操作”,基本上都需要修改 extend 函数。做了一个简单的:

    function AbsObj () {
      //检测是否能响应此 verb,要再用一次异常处理
      function canHandle(verb){
        try {
          // 别担心这里的 self 会传递不过去
          self(verb)
        } catch (e) {
          return false
        }
        return true
      }
      function toString() {} // 这个搞起来其实很麻烦~`
      var self = function (verb) {
        return eval(verb)
      }
      return self
    }

    js> Obj=extend(function(){x=5})
    js> o=Obj()
    js> o('canHandle')('x')
    true
    js> o('canHandle')('y')
    false

    文章写完了,小结一下。消息传递的编程不仅仅是一种代码风格,还可以成长为一种完备的机制。这种完备性远不只是这两篇加起来不到300行的文章所能覆盖的(例如非常彻底的“万物皆对象”,因为只要是能响应消息的函数,连接一下 AbsObj 就是合法对象了;类,函数都可以),大家可以试着玩一玩,顺便体会一下这个计算模型的透明和强大。
    另外,熟悉函数式编程的朋友可以帮忙思考一下:这样一个基于闭包变换的计算模型实质上是函数式的,再配合动态的函数式的对象级继承(用一个匿名类代换一下)就能在纯 FP 真正下实现 OOP 了。可惜的是每一次更新操作都要重新生成对象,性能代价大了点,不知道大家有什么好想法。
    posted @ 2008-10-16 09:45 咖啡色 阅读(31) | 评论 (0)编辑

    串口调试程序v1.0(模拟sscom设计)

     

    C#2.0开发。

    开发:apple
    时间:2006
    公司:陵嘉防伪技术(上海)有限公司
    联系:icelenix@163.com

    程序界面:

     程序下载:

    http://files.cnblogs.com/4kapple/LJ_myMScomm.rar

     

    posted @ 2008-09-23 14:47 咖啡色 阅读(437) | 评论 (2)编辑
         摘要: 最近也在接触SOCKET编程,在当今这样一个网络时代,很多技术都以网络为中心在诞生,至少我认为是这样的,而SOCKET套接字接口,在实现网络通讯上处于关键地位,所以不会SOCKET是不行的。首先,本文主要是针对那些刚接触SOCKET编程的朋友,如果是高手,就可以不看此文啦,可以去陪陪老婆,比如逛街或看电视...在开始之前,我们需要预习一些基础知识:什么是SOCKET套接字?SOCKET通常有那几种... 阅读全文
    posted @ 2008-09-22 12:26 咖啡色 阅读(161) | 评论 (1)编辑

    今天(确切的说,昨天晚上),我们的技术团队对所有服务器进行正常的停机维护,先在社区里面发了公告贴,然后大约在18点左右,在整站的顶部进行了通告,说“大众点评网将于2008年9月11日 23:00 至 2008年9月12日 8:00 进行系统维护,届时网站可能不能正常访问,敬请谅解!”。

    趁着23点没到,我还特地做了一个停机维护页面,上面调侃的写了一句话“大众点评网的服务器长期不堪重负,终于累趴下了。放心!工程师正在人工呼吸,开着小绵羊给我们送只强心针来吧!!”,然后在下面放了一个小绵羊赛车的游戏。算是有点创意吧??

    [04:59更新]开机了,停机维护页面原封不动放上来给大家看看,点击

    忙忙碌碌、紧紧张张的工作到4点左右,打开搜索引擎,出现几条很让人哭笑不得的新闻。在腾讯网的科技频道,互联网新闻里面,突然出现“大众点评今日突然无法打开 停机维护暂无期限”。而且还很白目的写道“目前尚无法确认该网站是何时开始停机维护,大众点评也未公布停机维护的时长,以及恢复的时间。”。很快,和讯就转载了这个新闻。

    亲爱的编辑同志,我们在页面顶端很清楚的写明了停机的开始时间截至时间,怎么就变成无法确认了呢。

    在这里劝站长们,不要轻易停机维护,就算要停机维护也不能出公告~~~会很冤枉的~~~我这就开机去~~~

    posted @ 2008-09-17 11:18 咖啡色 阅读(52) | 评论 (0)编辑
    Javascript libraries roundup  

    Prototype
    Prototype is a JavaScript framework that aims to ease development of dynamic web applications.

    Homepage: http://prototype.conio.net

    Documentation:
    http://blogs.ebusiness-apps.com/jordan/pages/Prototype%20Library%20Info.htm
    http://www.sergiopereira.com/articles/prototype.js.html
    http://wiki.script.aculo.us/scriptaculous/show/Prototype

    Rico
    Rico provides a very simple interface for registering Ajax request handlers as well as HTML elements or JavaScript objects as Ajax response objects. Multiple elements and/or objects may be updated as the result of one Ajax request.

    Homepage: http://openrico.org/rico/home.page
    Documentation: http://openrico.org/rico/docs.page
    Demos: http://openrico.org/rico/demos.page

    MochiKit
    MochiKit is a highly documented and well tested, suite of JavaScript libraries that will help you get things done, fast.

    Homepage: http://www.mochikit.com/
    Documentation: http://www.mochikit.com/doc/html/MochiKit/index.html
    Demos: http://www.mochikit.com/demos.html

    Dojo Toolkit
    Dojo is an Open Source toolkit that allows you to easily build dynamic capabilities into web pages and any other environment that supports JavaScript. Dojo provides components that let you make your sites more useable, responsive, and functional.

    Homepage: http://www.dojotoolkit.org/
    Documentation: http://dojotoolkit.org/docs/
    Demos: http://dojotoolkit.org/examples/

    Behaviour
    Separate Structure (xhtml) from Behavior (javascript)

    Homepage: http://bennolan.com/behaviour/

    Solvent
    The Solvent is a cross-browser AJAX application toolkit written in JavaScript. The Solvent is provided as modules or as an entire toolkit. The projects focus is to promote robust web applications and enable rapid web development.

    Homepage: http://sourceforge.net/projects/solvent/
    Documentation: http://sourceforge.net/docman/?group_id=144164

    Moo.FX
    moo.fx is a superlightweight, ultratiny, megasmall javascript effects library, written with prototype.js.

    Homepage: http://moofx.mad4milk.net/
    Demo: http://moofx.mad4milk.net/tests.html

    WZ_DradDrop
    A Cross-browser JavaScript DHTML Library which adds Drag Drop functionality to layers and to any desired image

    Homepage: http://www.walterzorn.com/dragdrop/dragdrop_e.htm

    WZ_jsGraphics
    High Performance JavaScript Vector Graphics Library.

    Homepage: http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm

    overLIB
    overLIB is a JavaScript library created to enhance websites with small popup information boxes (like tooltips) to help visitors around your website.

    Homepage: http://www.bosrup.com/web/overlib/
    Documentation: http://www.bosrup.com/web/overlib/?Documentation
    Command reference: http://www.bosrup.com/web/overlib/?Command_Reference

    Scriptaculous
    Javascript visual effects, togather with prototype.js

    Homepage: http://script.aculo.us/

    SACK
    Simple AJAX Code-Kit

    Homepage: http://twilightuniverse.com/projects/sack/
    Documentation: http://twilightuniverse.com/projects/sack/docs.php

    Sarissa
    Sarissa is an ECMAScript library acting as a cross-browser wrapper for native XML APIs. It offers various XML related goodies like Document instantiation, XML loading from URLs or strings, XSLT transformations, XPath queries etc and comes especially handy for people doing what is lately known as “AJAX” development.

    Homepage: http://sarissa.sourceforge.net/doc/

    Nifty Corners
    A small library for making rounded corners with Javascript.

    Homepage: http://pro.html.it/esempio/nifty/nifty1js.html

    dp.SyntaxHighlighter
    dp.SyntaxHighlighter is a free JavaScript library for source code syntax highlighting.

    Homepage: http://www.dreamprojections.com/SyntaxHighlighter/
    Documentation: http://www.dreamprojections.com/SyntaxHighlighter/Usage.aspx
    Demo: http://www.dreamprojections.com/SyntaxHighlighter/Examples.aspx

    AJAX.NET
    Michael Schwarz, a .NET developer in Germany has released the latest version of his Ajax .NET Wrapper. This class library simplifies the use of XMLHttp by providing .NET objects that generate the necessary Javascript code.

    Homepage: http://weblogs.asp.net/mschwarz/archive/2005/04/07/397504.aspx

    TOXIC
    Toxic is an AJAX toolkit, or framework, for creating rich web applications. It handles the tedious and repetetive tasks involved in integrating a client created using html and javascript with a server backend. It enables client side javascript to directly call class methods in PHP5 (or any other suitable language). It also enables the server side PHP to directly call client side javascript functions. Using Toxic you can get rid of much of the tedious work in form intensive rich web applications.

    Homepage: http://www.dotvoid.com/view.php?id=40

    Plex Toolkit
    Open source feature-complete DHTML GUI toolkit and AJAX framework based on a Javascript/DOM implementation of Macromedia’s Flex technology. Uses the almost identical markup language to Flex embedded in ordinary HTML documents for describing the UI. Binding is done with Javascript.

    Homepage: http://www.plextk.org/

    CPaint
    CPAINT (Cross-Platform Asynchronous INterface Toolkit) is a multi-language toolkit that helps web developers design and implement AJAX web applications with ease and flexibility.

    Homepage: http://cpaint.booleansystems.com/
    Docs: http://cpaint.booleansystems.com/doc/

    DOM-Drag
    DOM-Drag is a lightweight, easy to use, dragging API for modern DHTML browsers.

    Homepage: http://www.youngpup.net/2001/domdrag/
    Tutorials: http://www.youngpup.net/2001/domdrag/tutorial
    Demo: http://www.youngpup.net/2001/domdrag/examples

    Tibet
    Enterprise Class AJAX

    Homepage: http://www.technicalpursuit.com/ajax.htm

    Zimbra
    Rubust AJAX framework

    Homepage: http://www.zimbra.com
    Documentation: http://www.zimbra.com/products/documentation.html
    Demo: http://www.zimbra.com/products/hosted_demo.php

    qooxdoo
    qooxdoo is an advanced open-source javascript based toolkit. qooxdoo continues where simple HTML is not enough anymore. This way qooxdoo can help you to get your rich web application interface done - easier than ever before.

    Homepage: http://qooxdoo.oss.schlund.de
    Documentation: http://qooxdoo.oss.schlund.de/section/documentation
    Demo: http://qooxdoo.oss.schlund.de/counter/refer.php?id=5

    AJFORM
    AJFORM is a JavaScript toolkit which simply submits data from any given form in an HTML page, then sends the data to any specified JavaScript function. AJFORM degrades gracefully in every aspect. In other words, if the browser doesn’t support it, the data will be sent through the form as normal.

    Homepage: http://redredmusic.com/brendon/ajform/

    ThyApi
    ThyAPI is an api to allow the developement of better user interfaces for web applicaticions, Using javascript and Ajax, it allows a complete visual interface definition using CSS and encapsulates all objects data manipulateion.

    Homepage: http://sourceforge.net/projects/thyapi/

    Engine
    Engine for Web Applications is an application framework for client-side development and Web applications. It provides an environment in which to develop and run JavaScript components and applications.

    Homepage: http://www.imnmotion.com/projects/engine/
    Documentation: http://www.imnmotion.com/projects/engine/api/engine_api.html

    AJAXGear Toolkit
    It is a toolkit that allows you to take advantage of the client-side technique known as AJAX. AJAX is shorthand for Asynchronous JavaScript and XML. It uses the XMLHttpRequest object to allow a Web browser to make asynchronous call to the Web server without the need to refresh the whole page.

    Homepage: http://www.ajaxgear.com

    Interactive Website Framework
    A framework for creating highly interactive websites using javascript, css, xml, and html. Includes a custom xml parser for highly readable javascript. Essentially, all the plumbing for making AJAX-based websites, with js-based GUI toolkit.

    Homepage: http://sourceforge.net/projects/iwf/

    RSLite
    RSlite is an extremely lightweight implementation of remote scripting which uses cookies. It is very widely browser-compatible (Opera!) but limited to single calls and small amounts of data.

    Homepage: http://www.ashleyit.com/rs/main.htm
    Demo: http://www.ashleyit.com/rs/techniques.htm

    XHConn
    XMLHTTP is a technology with which a developer can access external resources over HTTP from a static web page without ever having to reload the page itself. This library is meant to simplify and unify the code necessary to successfully send and receive simple data via XMLHTTP.

    Homepage: http://xkr.us/code/javascript/XHConn/

    Taconite
    Taconite is a framework that simplifies the creation of Ajax enabled Web applications. It’s a very lightweight framework that automates the tedious tasks related to Ajax development, such as the creation and management of the XMLHttpRequest object and the creation of dynamic content.

    Homepage: http://taconite.sourceforge.net/
    Documentation: http://taconite.sourceforge.net/docs/jsdocs/index.html
    Demo: http://taconite.sourceforge.net/examples.html

    qForms
    Great Javascript API for interfacing forms.

    Homepage: http://pengoworks.com/index.cfm?action=get:qforms
    Documentation: http://pengoworks.com/qforms/docs/
    Demo: http://pengoworks.com/qforms/docs/examples/

    JSPkg
    jspkg is a package loader for Javascript, based on pluggable loaders for locating and loading scripts into a client-side Javascript application. It is designed to work best with unobtrusive Javascript libraries, but doesn’t impose any methodology or design on its users.

    Homepage: http://jspkg.sourceforge.net/

    Ajaxcaller
    AjaxCaller is a thin XMLHttpRequest wrapper used in all the AjaxPatterns demos. The focus is on ease-of-use and full HTTP method support.

    Homepage: http://ajaxify.com/run/testAjaxCaller/

    libXmlRequest
    The XmlRequest library contains a two public request functions, getXml and postXml, that may be used to send synchronous and asynchronous XML Http requests from Internet Explorer and Mozilla.

    Homepage: http://www.whitefrost.com/reference/2005/09/09/libXmlRequest.html

    SAJAX
    Sajax is an open source tool to make programming websites using the Ajax framework 鈥?also known as XMLHTTPRequest or remote scripting 鈥?as easy as possible. Capable of stubbing calls to numerous server-side platforms: ASP/ ColdFusion/ Io/ Lua/ Perl/ PHP/ Python/ Ruby

    Homepage: http://www.modernmethod.com/sajax/
    Demo: http://www.modernmethod.com/sajax/examples.phtml

    Sardalya
    A small library for making dynamic HTML programming easy and fun.

    Homepage: http://www.sarmal.com/sardalya/Default.aspx
    Demo: http://www.sarmal.com/sardalya/Samples.aspx

    X
    One of the best javascript libraries out there.

    Homepage: http://www.cross-browser.com/toys/

    AjaxRequest
    AjaxRequest is a layer over the XMLHttpRequest functionality which makes the communication between Javascript and the server easier for developers.

    Homepage: http://ajaxtoolbox.com/request/
    Documentation: http://ajaxtoolbox.com/request/documentation.php
    Demo: http://ajaxtoolbox.com/request/examples.php

    PHP based AJAX Frameworks

    AjaxAC
    AjaxAC is an open-source framework written in PHP, used to develop/create/generate AJAX applications. The fundamental idea behind AJAX (Asynchronous JavaScript And XML) is to use the XMLHttpRequest object to change a web page state using background HTTP sub-requests without reloading the entire page.

    Homepage: http://ajax.zervaas.com.au

    XOAD
    XOAD, formerly known as NAJAX, is a PHP based AJAX/XAP object oriented framework that allows you to create richer web applications.

    Homepage: http://www.xoad.org
    Documentation: http://www.xoad.org/documentation/source/
    Demo: http://www.xoad.org/examples/

    PAJAJ
    What is the PAJAJ framework, it stands for (PHP Asynchronous Javascript and JSON). It is a object oriented Ajax framework written in PHP5 for development of event driven PHP web applications.

    Homepage: http://sourceforge.net/projects/pajaj/
    Documentation: http://www.wassons.org/pajaj/public/docs/index.php

    Symfony
    A PHP 5 Development Framework inspired by Rails. It has integrated database abstraction and support for AJAX. Installation is fairly easy. Symfony is aimed at building robust applications in an enterprise context. This means that you have full control over the configuration: from the directory structure to the foreign libraries, almost everything can be customized. To match your enterprise’s development guidelines, symfony is bundled with additional tools helping you to test, debug and document your project.

    AjaxBlog: http://ajaxblog.com/arc…….application-in-php-in-minutes-with-symfony

    Homepage: www.symfony-project.com
    Documentation: http://www.symfony-project.com/content/documentation.html
    http://www.symfony-project.com/trac/wiki

    XAJAX
    xajax is an open source PHP class library that allows you to easily create powerful, web-based, Ajax applications using HTML, CSS, JavaScript, and PHP. Applications developed with xajax can asynchronously call server-side PHP functions and update content without reloading the page.

    Homepage: http://xajax.sourceforge.net/

    PEAR:: HTML_AJAX
    Provides PHP and JavaScript libraries for performing AJAX (Communication from JavaScript to your server without reloading the page)

    Homepage: http://pear.php.net/package/HTML_AJAX
    Documentation: http://pear.php.net/package/HTML_AJAX/docs

    Flexible AJAX
    Flexible Ajax is a handler to combine the remote scripting technology, also known as AJAX (Asynchronous Javascript and XML), with a php-based backend.

    Homepage: http://tripdown.de/flxajax/
    Demo: http://tripdown.de/flexible_ajax_example.php

    Javascript libs for Flash:

    FlashObject
    FlashObject is a small Javascript file used for embedding Macromedia Flash content.

    Homepage: http://blog.deconcept.com/flashobject/

    OSFlash - Flashjs
    The Flash JavaScript Integration Kit allows developers to get the best of the Flash and HTML worlds by enabling JavaScript to invoke ActionScript functions, and vice versa.

    Homepage: http://www.osflash.org/doku.php?id=flashjs
    Documentation: http://www.mustardlab.com/developer/flash/jscommunication/

    AFLAX
    A JavaScript Library for Macromedia’s Flash鈩?Platform. AFLAX is a method through which developers may use JavaScript and Flash together to create AJAX-type applications, but with a much richer set of vector drawing controls than are available in either Internet Explorer or FireFox. Developers using this library have access to the full range of Flash features, but without ever touching the Flash IDE.

    Homepage: http://www.aflax.org

    Java based AJAX Frameworks

    ZK
    ZK is an AJAX-based solution for developing Web applications in Java. ZK includes an event-driven engine to automate interactivity, and a rich set of XUL-based components.

    Homepage: http://zk1.sourceforge.net
    Demo: http://www.potix.com/zkdemo/userguide

    posted @ 2008-09-17 10:52 咖啡色 阅读(97) | 评论 (0)编辑
    WMI(Windows Management Instrumentation)是Windows下可以与系统信息(包括软硬件等)的一个管理框架,通过WMI可以很方便地对机器进行管理。现在以通过WMI来打开(或创建)一个记事本(notepad.exe)进程为例,看看VC++到.Net的变迁,一览.Net是如何让程序员从繁琐晦涩的程序中解放出来。

    1、预工作:
    VC++中需要在源代码中加入:
    #include <Wbemidl.h>
    #pragma comment(lib, "wbemuuid.lib")

    VC#中需要:
    在工程中添加引用:System.Management
    在代码中加入using System.Management;

    2、流程:
    VC++的代码,需要6步和查询返回值、最后释放资源:
    // Step 1: --------------------------------------------------
    // Initialize COM. ------------------------------------------
    hres =  CoInitializeEx(0, COINIT_MULTITHREADED); 

    // Step 2: --------------------------------------------------
    // Set general COM security levels --------------------------
    hres =  CoInitializeSecurity(
            NULL, 
            
    -1,                          // COM negotiates service
            NULL,                        // Authentication services
            NULL,                        // Reserved
            RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
            RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation  
            NULL,                        // Authentication info
            EOAC_NONE,                   // Additional capabilities 
            NULL                         // Reserved
            );

    // Step 3: ---------------------------------------------------
    // Obtain the initial locator to WMI -------------------------
    IWbemLocator *pLoc = NULL;
    hres 
    = CoCreateInstance(
            CLSID_WbemLocator,             
            
    0
            CLSCTX_INPROC_SERVER, 
            IID_IWbemLocator, (LPVOID 
    *&pLoc);

    // Step 4: ---------------------------------------------------
    // Connect to WMI through the IWbemLocator::ConnectServer method
    IWbemServices *pSvc = NULL;
    // Connect to the local root\cimv2 namespace
    // and obtain pointer pSvc to make IWbemServices calls.
    hres = pLoc->ConnectServer(
            _bstr_t(L
    "ROOT\\CIMV2"), 
            NULL,
            NULL, 
            
    0
            NULL, 
            
    0
            
    0
            
    &pSvc
        );

    // Step 5: --------------------------------------------------
    // Set security levels for the proxy ------------------------
    hres = CoSetProxyBlanket(
            pSvc,                        
    // Indicates the proxy to set
            RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx 
            RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx 
            NULL,                        // Server principal name 
            RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
            RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
            NULL,                        // client identity
            EOAC_NONE                    // proxy capabilities 
        );

    // Step 6: --------------------------------------------------
    // Use the IWbemServices pointer to make requests of WMI ----
    // set up to call the Win32_Process::Create method
    BSTR MethodName = SysAllocString(L"Create");
    BSTR ClassName 
    = SysAllocString(L"Win32_Process");

    IWbemClassObject
    * pClass = NULL;
    hres 
    = pSvc->GetObject(ClassName, 0, NULL, &pClass, NULL);

    IWbemClassObject
    * pInParamsDefinition = NULL;
    hres 
    = pClass->GetMethod(MethodName, 0
            
    &pInParamsDefinition, NULL);

    IWbemClassObject
    * pClassInstance = NULL;
    hres 
    = pInParamsDefinition->SpawnInstance(0&pClassInstance);

    // Create the values for the in parameters
    VARIANT varCommand;
    varCommand.vt 
    = VT_BSTR;
    varCommand.bstrVal 
    = L"notepad.exe";
    // Store the value for the in parameters
    hres = pClassInstance->Put(L"CommandLine"0,
            
    &varCommand, 0);
    wprintf(L
    "The command is: %s\n", V_BSTR(&varCommand));

    // Execute Method
    IWbemClassObject* pOutParams = NULL;
    hres 
    = pSvc->ExecMethod(ClassName, MethodName, 0, NULL, pClassInstance, &pOutParams, NULL);

    // Get return value
    VARIANT varReturnValue;
    hres 
    = pOutParams->Get(_bstr_t(L"ReturnValue"), 0&varReturnValue, NULL, 0);

    // Last: clean up
    VariantClear(&varCommand);
    VariantClear(
    &varReturnValue);
    SysFreeString(ClassName);
    SysFreeString(MethodName);
    pClass
    ->Release();
    pInParamsDefinition
    ->Release();
    pOutParams
    ->Release();
    pLoc
    ->Release();
    pSvc
    ->Release();
    CoUninitialize();

    VC#只需寥寥数行就可以实现(暂不考虑错误处理):
    跳过VC++中的Step1~5,直接从Step6开始。而且非常直观:
    ManagementClass mc = new  ManagementClass("Win32_Process");
    ManagementBaseObject obj 
    = mc.GetMethodParameters("Create");
    obj[
    "CommandLine"]="notepad.exe";
    mc.InvokeMethod(
    "Create", obj, null);
    mc.Dispose();

    .Net对WMI良好的封装,还有对字符串的更强支持、方便的垃圾回收机制,使程序既一目了然,又易于维护。其实类似的区别在VC vs VB年代已经出现了(特别是在COM组件编写和调用方面可以看出),从这点也可以看出.Net完全继承了VB易学易用的特性,又不失强大的功能。
    posted @ 2008-08-19 16:17 咖啡色 阅读(116) | 评论 (0)编辑
    在工业生产控制系统中,有许多需要定时完成的操作,如定时显示当前时间,定时刷新屏幕上的进度条,上位

    机定时向下位机发送命令和传送数据等。特别是在对控制性能要求较高的实时控制系统和数据采集系统中,就更需要精确定时操作。
    众所周知,Windows 是基于消息机制的系统,任何事件的执行都是通过发送和接收消息来完成的。 这样就带来了一些问题,如一旦计算机的CPU被某个进程占用,或系统资源紧张时,发送到消息队列 中的消息就暂时被挂起,得不到实时处理。因此,不能简单地通过Windows消息引发一个对定时要求 严格的事件。另外,由于在Windows中已经封装了计算机底层硬件的访问,所以,要想通过直接利用 访问硬件来完成精确定时,也比较困难。所以在实际应用时,应针对具体定时精度的要求,采取相适 应的定时方法。
    VC中提供了很多关于时间操作的函数,利用它们控制程序能够精确地完成定时和计时操作。本文详细介绍了 VC中基于Windows的精确定时的七种方式,如下图所示:


    图一 图像描述

    方式一:VC中的WM_TIMER消息映射能进行简单的时间控制。首先调用函数SetTimer()设置定时 间隔,如SetTimer(0,200,NULL)即为设置200ms的时间间隔。然后在应用程序中增加定时响应函数 OnTimer(),并在该函数中添加响应的处理语句,用来完成到达定时时间的操作。这种定时方法非常 简单,可以实现一定的定时功能,但其定时功能如同Sleep()函数的延时功能一样,精度非常低,最小 计时精度仅为30ms,CPU占用低,且定时器消息在多任务操作系统中的优先级很低,不能得到及时响 应,往往不能满足实时控制环境下的应用。只可以用来实现诸如位图的动态显示等对定时精度要求不高的情况。如示例工程中的Timer1。
    方式二:VC中使用sleep()函数实现延时,它的单位是ms,如延时2秒,用sleep(2000)。精度非常 低,最小计时精度仅为30ms,用sleep函数的不利处在于延时期间不能处理其他的消息,如果时间太 长,就好象死机一样,CPU占用率非常高,只能用于要求不高的延时程序中。如示例工程中的Timer2。
    方式三:利用COleDateTime类和COleDateTimeSpan类结合WINDOWS的消息处理过程来实现秒级延时。如示例工程中的Timer3和Timer3_1。以下是实现2秒的延时代码:

    COleDateTime      start_time = COleDateTime::GetCurrentTime();
    COleDateTimeSpan end_time= COleDateTime::GetCurrentTime()-start_time;
    while(end_time.GetTotalSeconds()< 2) //实现延时2秒
    {
    MSG msg;
    GetMessage(&msg,NULL,0,0);
    TranslateMessage(&msg);
    DispatchMessage(&msg);

    //以上四行是实现在延时或定时期间能处理其他的消息,
           //虽然这样可以降低CPU的占有率,
    //但降低了延时或定时精度,实际应用中可以去掉。
    end_time = COleDateTime::GetCurrentTime()-start_time;
    }//这样在延时的时候我们也能够处理其他的消息。
    方式四:在精度要求较高的情况下,VC中可以利用GetTickCount()函数,该函数的返回值是
    DWORD型,表示以ms为单位的计算机启动后经历的时间间隔。精度比WM_TIMER消息映射高,在较 短的定时中其计时误差为15ms,在较长的定时中其计时误差较低,如果定时时间太长,就好象死机一样,CPU占用率非常高,只能用于要求不高的延时程序中。如示例工程中的Timer4和Timer4_1。下列代码可以实现50ms的精确定时:
    DWORD dwStart = GetTickCount();
    DWORD dwEnd = dwStart;
    do
    {
    dwEnd = GetTickCount()-dwStart;
    }while(dwEnd <50);
    为使GetTickCount()函数在延时或定时期间能处理其他的消息,可以把代码改为:
    DWORD dwStart = GetTickCount();
    DWORD dwEnd = dwStart;
    do
    {
    MSG msg;
    GetMessage(&msg,NULL,0,0);
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    dwEnd = GetTickCount()-dwStart;
    }while(dwEnd <50);
    虽然这样可以降低CPU的占有率,并在延时或定时期间也能处理其他的消息,但降低了延时或定时精度。
    方式五:与GetTickCount()函数类似的多媒体定时器函数DWORD timeGetTime(void),该函数定时精 度为ms级,返回从Windows启动开始经过的毫秒数。微软公司在其多媒体Windows中提供了精确定时器的底 层API持,利用多媒体定时器可以很精确地读出系统的当前时间,并且能在非常精确的时间间隔内完成一 个事件、函数或过程的调用。不同之处在于调用DWORD timeGetTime(void) 函数之前必须将 Winmm.lib 和 Mmsystem.h 添加到工程中,否则在编译时提示DWORD timeGetTime(void)函数未定义。由于使用该 函数是通过查询的方式进行定时控制的,所以,应该建立定时循环来进行定时事件的控制。如示例工程中的Timer5和Timer5_1。
    方式六:使用多媒体定时器timeSetEvent()函数,该函数定时精度为ms级。利用该函数可以实现周期性的函数调用。如示例工程中的 Timer6和Timer6_1。函数的原型如下:
    MMRESULT timeSetEvent( UINT uDelay, 
    UINT uResolution,
    LPTIMECALLBACK lpTimeProc,
    WORD dwUser,
    UINT fuEvent )
    该函数设置一个定时回调事件,此事件可以是一个一次性事件或周期性事件。事件一旦被激活,便调用指定的回调函数, 成功后返回事件的标识符代码,否则返回NULL。函数的参数说明如下:
    uDelay:以毫秒指定事件的周期。
    Uresolution:以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms。
    LpTimeProc:指向一个回调函数。
    DwUser:存放用户提供的回调数据。
    FuEvent:指定定时器事件类型:
    TIME_ONESHOT:uDelay毫秒后只产生一次事件
    TIME_PERIODIC :每隔uDelay毫秒周期性地产生事件。
    具体应用时,可以通过调用timeSetEvent()函数,将需要周期性执行的任务定义在LpTimeProc回调函数 中(如:定时采样、控制等),从而完成所需处理的事件。需要注意的是,任务处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后, 应及时调用timeKillEvent()将之释放。
    方式七:对于精确度要求更高的定时操作,则应该使用QueryPerformanceFrequency()和 QueryPerformanceCounter()函数。这两个函数是VC提供的仅供Windows 95及其后续版本使用的精确时间函数,并要求计算机从硬件上支持精确定时器。如示例工程中的Timer7、Timer7_1、Timer7_2、 Timer7_3。
    QueryPerformanceFrequency()函数和QueryPerformanceCounter()函数的原型如下:
    BOOL  QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
    BOOL QueryPerformanceCounter(LARGE_INTEGER *lpCount);
    数据类型ARGE_INTEGER既可以是一个8字节长的整型数,也可以是两个4字节长的整型数的联合结构, 其具体用法根据编译器是否支持64位而定。该类型的定义如下:
    typedef union _LARGE_INTEGER
    {
    struct
    {
    DWORD LowPart ;// 4字节整型数
    LONG HighPart;// 4字节整型数
    };
    LONGLONG QuadPart ;// 8字节整型数

    }LARGE_INTEGER ;
    在进行定时之前,先调用QueryPerformanceFrequency()函数获得机器内部定时器的时钟频率, 然后在需要严格定时的事件发生之前和发生之后分别调用QueryPerformanceCounter()函数,利用两次获得的计数之差及时钟频率,计算出事件经 历的精确时间。下列代码实现1ms的精确定时:
    LARGE_INTEGER litmp; 
    LONGLONG QPart1,QPart2;
    double dfMinus, dfFreq, dfTim;
    QueryPerformanceFrequency(&litmp);
    dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
    QueryPerformanceCounter(&litmp);
    QPart1 = litmp.QuadPart;// 获得初始值
    do
    {
    QueryPerformanceCounter(&litmp);
    QPart2 = litmp.QuadPart;//获得中止值
    dfMinus = (double)(QPart2-QPart1);
    dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒
    }while(dfTim<0.001);
    其定时误差不超过1微秒,精度与CPU等机器配置有关。 下面的程序用来测试函数Sleep(100)的精确持续时间:
    LARGE_INTEGER litmp; 
    LONGLONG QPart1,QPart2;
    double dfMinus, dfFreq, dfTim;
    QueryPerformanceFrequency(&litmp);
    dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
    QueryPerformanceCounter(&litmp);
    QPart1 = litmp.QuadPart;// 获得初始值
    Sleep(100);
    QueryPerformanceCounter(&litmp);
    QPart2 = litmp.QuadPart;//获得中止值
    dfMinus = (double)(QPart2-QPart1);
    dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒
    由于Sleep()函数自身的误差,上述程序每次执行的结果都会有微小误差。下列代码实现1微秒的精确定时:
    LARGE_INTEGER litmp; 
    LONGLONG QPart1,QPart2;
    double dfMinus, dfFreq, dfTim;
    QueryPerformanceFrequency(&litmp);
    dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
    QueryPerformanceCounter(&litmp);
    QPart1 = litmp.QuadPart;// 获得初始值
    do
    {
    QueryPerformanceCounter(&litmp);
    QPart2 = litmp.QuadPart;//获得中止值
    dfMinus = (double)(QPart2-QPart1);
    dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒
    }while(dfTim<0.000001);
    其定时误差一般不超过0.5微秒,精度与CPU等机器配置有关。(完)
    posted @ 2008-07-10 12:40 咖啡色 阅读(158) | 评论 (0)编辑
         前些日子在工作中遇到一个在原子交易中用C#设置系统时间的问题,虽是一个小问题,却因为C#本身没有这种函数而耽误了一些时间,C#要设置系统时间必须要调用Win32的API,而其中相关的函数就是SetSystemTime(), GetSystemTimer(), SetLocalTime(), GetLocalTime(), 这似乎是用VC写的函数,在VC++中是可以直接调用的。MSDN上面对这几个函数解释得不是很详细,网上可以找到不少这样的程序,但我个人感觉对这些函数的功能和注意点说得也不够透彻,包括那个所谓经过测试的。这里把自己所用到的一些功能和体会给出来,至少要把SetSystemTIme()和SetLocalTime()这两个函数的区别搞清楚。

    对于这两个函数,其输入参数必须是一个下面这样的结构体,其成员变量类型必须是ushort,成员变量不能改变顺序。

        [StructLayout(LayoutKind.Sequential)]
     public struct SystemTime
     
    {
      
    public ushort wYear;
      
    public ushort wMonth;
      
    public ushort wDayOfWeek;
      
    public ushort wDay;
      
    public ushort wHour;
      
    public ushort wMinute;
      
    public ushort wSecond;
      
    public ushort wMiliseconds;
     }

    调用Win32的API,根据需要选用:

     

     public class Win32
     
    {
      [DllImport(
    "Kernel32.dll")]
      
    public static extern bool SetSystemTime(ref SystemTime sysTime );
      [DllImport(
    "Kernel32.dll")]
      
    public static extern bool SetLocalTime(ref SystemTime sysTime);
      [DllImport(
    "Kernel32.dll")]
      
    public static extern void GetSystemTime(ref SystemTime sysTime);
      [DllImport(
    "Kernel32.dll")]
      
    public static extern void GetLocalTime(ref SystemTime sysTime);
     }

    下面是SetLocalTime的调用,SetLocalTime的功能就是设置本地系统时间。因为我的工作中要用到的是根据XML文件中的节点内容字符串通过解析后来设置系统时间,所以我做了一个通过输入字符串参数设置本地系统时间的函数,其中调用了SetLocalTime()函数。

     

            public static bool SetLocalTimeByStr(string timestr)
            {
                
    bool flag=false;
                SystemTime sysTime 
    =new SystemTime();
                
                
    string SysTime=timestr.Trim();   //此步骤多余,为方便程序而用直接用timestr即可
                sysTime.wYear = Convert.ToUInt16(SysTime.Substring(0,4));
                sysTime.wMonth 
    = Convert.ToUInt16(SysTime.Substring(4,2));
                sysTime.wDay
    =Convert.ToUInt16(SysTime.Substring(6,2));
                sysTime.wHour
    =Convert.ToUInt16(SysTime.Substring(8,2));
                sysTime.wMinute 
    = Convert.ToUInt16(SysTime.Substring(10,2));
                sysTime.wSecond 
    = Convert.ToUInt16(SysTime.Substring(12,2));
               
    //注意:
                
    //结构体的wDayOfWeek属性一般不用赋值,函数会自动计算,写了如果不对应反而会出错
                
    //wMiliseconds属性默认值为一,可以赋值
                try
                {
                    flag
    =Win32.SetLocalTime(ref sysTime);
                }
                
    //由于不是C#本身的函数,很多异常无法捕获
               
    //函数执行成功则返回true,函数执行失败返回false
               
    //经常不返回异常,不提示错误,但是函数返回false,给查找错误带来了一定的困难
                catch(Exception ex1)
                {
                    Console.WriteLine(
    "SetLocalTime函数执行异常"+ex1.Message);
                }

                
    return flag;
            }

    如果不是以字符串来赋值,而以int甚至ushort类型数来赋值将会更加简单,不多说了。

    程序执行之后本地系统时间将会如期改变。

    那么SetLocalTime()和SetSystemTime()又有什么区别呢?大家可以把上述函数的“flag=Win32.SetLocalTime(ref sysTime);”部分换成“flag=Win32.SetSystemTime(ref sysTime);”试试,你将会发现这样一个结果:

    执行后系统时间也会改变,但总是比预期的有些偏差,中国的朋友估计都会多出8个小时来。

    这是时区的设置造成的,SetSystemTime()默认设置的为UTC时间,当系统设置时间的时候还会按照时区加上一个偏差。而我们的用的北京时间也就是东八区时间,刚好比UTC多了8个小时。这回了解二者的区别了吧。

            这个偏差是不是可以补回来呢?比如对于北京时间,设置完之后减去8个小时就可以了吗?是这么回事,但是具体做起来要有些麻烦,因为要考虑到天,月甚至年都有可能会造成改变,还要考虑到不同的月份。虽然有了SetLocalTime,再来考虑这个有些多此一举,或者也可以直接从时区的方法上入手,但是我偏偏不服,做了一个这样的方法,只是小试一下,没有自己审核,可能会有bug,给大家参考:

            //从字符串设置系统时间
            public bool SetSysTimeByStr(string timestr)
            
    {
                
    int temp=0;
                SystemTime sysTime 
    =new SystemTime();
                
    //给sysTIme初始赋值
                Win32.GetSystemTime(ref sysTime);
                
    string SysTime=timestr;
                sysTime.wYear 
    = Convert.ToUInt16(SysTime.Substring(0,4));
                sysTime.wMonth 
    = Convert.ToUInt16(SysTime.Substring(4,2));
                sysTime.wDay
    =Convert.ToUInt16(SysTime.Substring(6,2));
                sysTime.wHour
    =Convert.ToUInt16(SysTime.Substring(8,2));

                
    //为抵消北京时间+8而进行的操作
                temp=Convert.ToInt16(SysTime.Substring(8,2))-8;
                
    if(temp<0)
                
    {
                    sysTime.wHour 
    =Convert.ToUInt16(temp+24);//Convert.ToUInt16(SysTime.Substring(8,2))-Convert.ToInt16(8);
                    sysTime.wDay=Convert.ToUInt16(sysTime.wDay-1);
                    
    if(sysTime.wDay==0)
                    
    {
                        
    if(sysTime.wMonth==5|sysTime.wMonth==7|sysTime.wMonth==8|sysTime.wMonth==10|sysTime.wMonth==12)
                        
    {
                            sysTime.wMonth
    =Convert.ToUInt16(sysTime.wMonth-1);
                            sysTime.wDay
    =Convert.ToUInt16(30);
                        }

                        
    else if(sysTime.wMonth==1)
                        
    {
                            sysTime.wMonth
    =Convert.ToUInt16(12);
                            sysTime.wDay
    =Convert.ToUInt16(31);
                            sysTime.wYear
    =Convert.ToUInt16(sysTime.wYear-1);
                        }

                        
    else if(sysTime.wMonth==3)
                        
    {
                            sysTime.wMonth
    =Convert.ToUInt16(2);
                            
    if(sysTime.wYear%4==0&&sysTime.wYear%100!=0)
                                sysTime.wDay
    =Convert.ToUInt16(29);
                            
    else
                                sysTime.wDay
    =Convert.ToUInt16(28);
                        }

                        
    else
                        
    {
                            sysTime.wMonth
    =Convert.ToUInt16(sysTime.wMonth-1);
                            sysTime.wDay
    =Convert.ToUInt16(31);
                        }


                    }

                }

                
    else
                
    {
                    sysTime.wHour
    =Convert.ToUInt16(temp);
                }


                sysTime.wMinute 
    = Convert.ToUInt16(SysTime.Substring(10,2));
                sysTime.wSecond 
    = Convert.ToUInt16(SysTime.Substring(12,2));
                    bool flag=Win32.SetSystemTime(ref sysTime);

                    
    return flag;
            }

     

    而对于那两个Get的方法GetSystemTimer(),和GetLocalTime()的使用,相信不成什么问题,就不多说了。

    另外,在此过程中发现一个问题,就是Visual Studio.net 2003在调式这个程序的时候经常会遇到程序不执行的情况,也不报错误,我用单步调试也是毫无反应,而关掉重新开就一点毛病都没有了,在多台电脑上都出现过。可能是Visual Studio的一个bug吧。

    posted @ 2008-07-02 14:22 咖啡色 阅读(210) | 评论 (0)编辑
    一、Remoting的优缺点?
    优点:
    1、能让我们进行分布式开发
    2、Tcp通道的Remoting速度非常快
    3、虽然是远程的,但是非常接近于本地调用对象
    4、可以做到保持对象的状态
    5、没有应用程序限制,可以是控制台,winform,iis,windows服务承载远程对象
    缺点:
    1、非标准的应用因此有平台限制
    2、脱离iis的话需要有自己的安全机制

    二、Remoting和Web服务的区别?
        ASP.NET Web 服务基础结构通过将 SOAP 消息映射到方法调用,为 Web 服务提供了简单的 API。通过提供一种非常简单的编程模型(基于将 SOAP 消息交换映射到方法调用),它实现了此机制。ASP.NET Web 服务的客户端不需要了解用于创建它们的平台、对象模型或编程语言。而服务也不需要了解向它们发送消息的客户端。唯一的要求是:双方都要认可正在创建和使用的 SOAP 消息的格式,该格式是由使用 WSDL 和 XML 架构 (XSD) 表示的 Web 服务合约定义来定义的。 
        . NET Remoting 为分布式对象提供了一个基础结构。它使用既灵活又可扩展的管线向远程进程提供 .NET 的完全对象语义。ASP.NET Web 服务基于消息传递提供非常简单的编程模型,而 .NET Remoting 提供较为复杂的功能,包括支持通过值或引用传递对象、回调,以及多对象激活和生命周期管理策略等。要使用 .NET Remoting,客户端需要了解所有这些详细信息,简而言之,需要使用 .NET 建立客户端。.NET Remoting 管线还支持 SOAP 消息,但必须注意这并没有改变其对客户端的要求。如果 Remoting 端点提供 .NET 专用的对象语义,不管是否通过 SOAP,客户端必须理解它们。

    三、最简单的Remoting的例子
    1、远程对象:
    建立类库项目:RemoteObject

    using System;

    namespace RemoteObject
    {
        
    public class MyObject:MarshalByRefObject
        {
            
    public int Add(int a,int b)
            {
                
    return a+b;
            }
        }
    }

    2、服务端
    建立控制台项目:RemoteServer

    using System;
    using System.Runtime.Remoting;

    namespace RemoteServer
    {
        
    class MyServer
        {
            [STAThread]
            
    static void Main(string[] args)
            {
                RemotingConfiguration.Configure(
    "RemoteServer.exe.config");
                Console.ReadLine();
            }
        }
    }

    建立配置文件:app.config
    <configuration>
        
    <system.runtime.remoting>
            
    <application name="RemoteServer">
                
    <service>
                    
    <wellknown type="RemoteObject.MyObject,RemoteObject" objectUri="RemoteObject.MyObject"
                        mode
    ="Singleton" />
                
    </service>
                
    <channels>
                    
    <channel ref="tcp" port="9999"/>
                
    </channels>
            
    </application>
        
    </system.runtime.remoting>
    </configuration>

    3、客户端:
    建立控制台项目:RemoteClient

    using System;

    namespace RemoteClient
    {
        
    class MyClient
        {
            [STAThread]
            
    static void Main(string[] args)
            {
                RemoteObject.MyObject app 
    = (RemoteObject.MyObject)Activator.GetObject(typeof(RemoteObject.MyObject),System.Configuration.ConfigurationSettings.AppSettings["ServiceURL"]);
                Console.WriteLine(app.Add(
    1,2));
                Console.ReadLine();
            }
        }
    }

    建立配置文件:app.config

    <configuration>
     
    <appSettings>
     
    <add key="ServiceURL" value="tcp://localhost:9999/RemoteObject.MyObject"/>
     
    </appSettings>
    </configuration>

    4、测试
    在最后编译的时候会发现编译报错:
    1、找不到app.Add()
    2、找不到RemoteObject
    这是因为客户端RemoteClient没有添加RemoteObject的引用,编译器并不知道远程对象存在哪些成员所以报错,添加引用以后vs.net会在客户端也保存一个dll,可能大家会问这样如果对远程对象的修改是不是会很麻烦?其实不麻烦,对项目编译一次vs.net会重新复制dll。
    然后直接运行客户端会出现“目标主机拒绝”的异常,也说明了通道没有打开
    运行服务端再运行客户端出现“找不到程序集RemoteObject”!回头想想可以发现我们并在服务端对RemoteObject添加引用,编译的时候通过是因为这个时候并没有用到远程对象,大家可能不理解运行服务端的时候也通过?这是因为没有这个时候还没有激活远程对象。理所当然,对服务端要添加引用远程对象,毕竟我们的对象是要靠远程承载的。
    现在再先后运行服务端程序和客户端程序,客户端程序显示3,测试成功。

    四、结束语
    我们通过一个简单的例子实现了最简单的remoting,对其实质没有做任何介绍,我想通过例子入门才是最简单的。
    posted @ 2008-06-24 09:35 咖啡色 阅读(90) | 评论 (2)编辑
         摘要: 在编程控制防火墙前先要有个前提,就是你必须是管理员权限, 这样本例的程序才能用"Run as administrator"的方式运行,并成功修改. 如果你本身就是用Administrator这个用户登录的话,直接运行就行了. 建议最好在这个用户下来调试程序. 本程序只是个初始的例子,里面的功能只开发了一部分,各位有兴趣的话可以继续深入运用. 像Vista的防火墙就比较Bt,除了基本设定外,在"C... 阅读全文
    posted @ 2008-06-23 17:16 咖啡色 阅读(92) | 评论 (0)编辑

    1. 在ASP.NET中专用属性:
    获取服务器电脑名:Page.Server.ManchineName
    获取用户信息:Page.User
    获取客户端电脑名:Page.Request.UserHostName
    获取客户端电脑IP:Page.Request.UserHostAddress
    2. 在网络编程中的通用方法:
    获取当前电脑名:static System.Net.Dns.GetHostName()
    根据电脑名取出全部IP地址:static System.Net.Dns.Resolve(电脑名).AddressList
    也可根据IP地址取出电脑名:static System.Net.Dns.Resolve(IP地址).HostName

    3. 系统环境类的通用属性:
    当前电脑名:static System.Environment.MachineName
    当前电脑所属网域:static System.Environment.UserDomainName
    当前电脑用户:static System.Environment.UserName

    c#获取真实IP和代理IP

    获取真实IP

    public static string GetRealIP()
    {
    string ip;
    try
    {
    HttpRequest request = HttpContext.Current.Request;

    if (request.ServerVariables["HTTP_VIA"] != null)
    {
    ip = request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString().Split(',')[0].Trim();
    }
    else
    {
    ip = request.UserHostAddress;
    }
    }
    catch (Exception e)
    {
    throw e;
    }

    return ip;
    }


    获取代理IP

    public static string GetViaIP()
    {
    string viaIp = null;

    try
    {
    HttpRequest request = HttpContext.Current.Request;

    if (request.ServerVariables["HTTP_VIA"] != null)
    {
    viaIp = request.UserHostAddress;
    }

    }
    catch (Exception e)
    {

    throw e;
    }

    return viaIp;

    获取服务器的IP地址方法以DNS法较为简单实用,如下:
      using System.Net;

     

     private void ButtonIP_Click(object sender, System.EventArgs e)
      { 
         System.Net.IPAddress[] addressList = Dns.GetHostByName(Dns.GetHostName()).AddressList;
         if ( addressList.Length>1)
         {  
    TextLIP.Text = addressList[0].ToString();
              
    TextSIP.Text = addressList[1].ToString();
         }
         else
         {
              
    TextLIP.Text = addressList[0].ToString();
             
    TextSIP.Text = "没有可用的连接";
         }
      }

    获取服务器的IP地址与MAC地址另一方法如下:

       using System.Management;

     

       string stringMAC = "";
       string
    stringIP = "";
       ManagementClass 
    MC = new ManagementClass "Win32_NetworkAdapterConfiguration");
       ManagementObjectCollection
    MOC= MC.GetInstances();
      
       foreach(ManagementObject 
    MO in MOC)
       {
        if ((bool)
    MO["IPEnabled"] == true)
        {
         
    stringMAC += MO["MACAddress"].ToString();
         
    TextMAC.Text = stringMAC.ToString();
         string[] IPAddresses = (string[])
    MO["IPAddress"];
         if(IPAddresses.Length > 0)
         
    stringIP = IPAddresses[0];
         
    TextIP.Text = stringIP.ToString();
        }
       } 

    posted @ 2008-06-18 10:18 咖啡色 阅读(111) | 评论 (0)编辑
    二叉树结点类:
    public class Node
        {
            
    private Node left;
            
    private Node right;
            
    private int value;

            
    public Node(Node left, Node right, int value)
            {
                
    this.left = left;
                
    this.right = right;
                
    this.value = value;
            }

            
    public Node(int value)
            {
                
    this.value = value;
            }

            
    public Node getLeftNode() { return left; }
            
    public Node getRightNode() { return right; }
            
    public int getValue() { return value; }


            
    /// <summary>
            
    /// 查找某一节点
            
    /// </summary>
            
    /// <param name="node">根节点</param>
            
    /// <param name="value">要查找的值</param>
            
    /// <returns></returns>
            public Node findNode(Node root, int value)
            {
                
    if (root == null)
                {
                    
    return null;
                }

                
    if (root.getValue() == value)
                {
                    
    return root;
                }

                
    if (value < root.getValue())
                {
                    findNode(root.getLeftNode(), value);
                }
                
    else
                {
                    findNode(root.getRightNode(), value);
                }
                
    return null;
            }

            
    /// <summary>
            
    /// 先根遍历
            
    /// </summary>
            
    /// <param name="root"></param>
            public static void PreOrder(Node root)
            {
                
    if (root != null)
                {
                    Console.WriteLine(root.getValue());

                    PreOrder(root.getLeftNode());
                    PreOrder(root.getRightNode());
                }
            }

            
    /// <summary>
            
    /// 后根遍历
            
    /// </summary>
            
    /// <param name="root"></param>
            public static void AfterOrder(Node root)
            {
                
    if (root != null)
                {
                    AfterOrder(root.getLeftNode());
                    AfterOrder(root.getRightNode());
                    Console.WriteLine(root.getValue());
                }
            }

            
    /// <summary>
            
    /// 中序遍历二叉树
            
    /// </summary>
            
    /// <param name="root"></param>
            public static void MidOrder(Node root)
            {
                
    if (root != null)
                {
                    MidOrder(root.getLeftNode());
                    Console.WriteLine(root.getValue());
                    MidOrder(root.getRightNode());
                }
            }

            
    public static int leafNodeCount = 0;
            
    public static void count_leafNode(Node root)
            {
                
    if (root == null)
                {
                    
    return;
                }
                
    if (root.getLeftNode() == null && root.getRightNode() == null)
                {
                    Console.WriteLine(
    "叶子节点" +  leafNodeCount.ToString() + "的值为:" +   root.getValue());
                    leafNodeCount
    ++;
                }
                
    else
                {
                    count_leafNode(root.getLeftNode());
                    count_leafNode(root.getRightNode());
                }
            }
        }


    为了简便起见,将遍历等相关方法都写在了该节点中。
    以下是主程序调用:
    public void showMain()
            {
    //构建二叉树
                Node[] node=new Node[8];
                node[
    4= new Node(5);
                node[
    5= new Node(6);
                node[
    6= new Node(7);

                node[
    3= new Node(node[6], node[7], 3);
                node[
    2= new Node(node[4], node[5], 2);
                node[
    1= new Node(node[3], null2);
                node[
    0= new Node(node[1], node[2], 0);

                Console.WriteLine(
    "后根遍历:");
                Node.AfterOrder(node[
    0]);

                Console.WriteLine(
    "先根遍历:");
                Node.PreOrder(node[
    0]);

                Console.WriteLine(
    "中根遍历:");
                Node.MidOrder(node[
    0]);

                Console.WriteLine(
    "共有叶子节点个数为:");
                Node.count_leafNode(node[
    0]);
                Console.WriteLine(Node.leafNodeCount);

                Console.Read();

            }

    【转载;来源:http://www.cnblogs.com/hanxianlong】
    posted @ 2008-06-13 16:12 咖啡色 阅读(121) | 评论 (0)编辑

    作为一个完整的例子,你开发出来驱动还必须要能安装。所以下面我讲一下安装。

    如果前面的编译过程没有错误的话,现在我们应该已经得到了一个HelloWDM.sys文件,假设它是放在D:\HelloWDM\objfre\i386中。

    安装WDM驱动程序可以用两种方法,一种是利用注册表,还有一种是利用INF文件。我们一般是采用INF文件(这是微软推荐的)。INF文件可以在 WINNT\INF 目录中找到很多。为了顺利安装,我在这里先给出 HelloWDM 所需要的 HelloWDM.INF 文件:

    ;; The Win2K DDK documentation contains an excellent INF reference.

    ;--------- Version Section ---------------------------------------------------

    [Version]
    Signature="$CHICAGO$"
    Provider=LC_Device
    DriverVer=8/21/2002,3.0.0.3

    ; If device fits one of the standard classes, use the name and GUID here,
    ; otherwise create your own device class and GUID as this example shows.

    Class=Unknown
    ClassGUID={ff646f80-8def-11d2-9449-00105a075f6b}

    ;--------- SourceDiskNames and SourceDiskFiles Section -----------------------

    ; These sections identify source disks and files for installation. They are
    ; shown here as an example, but commented out.

    [SourceDisksNames]
    1 = "HelloWDM",Disk1,,

    [SourceDisksFiles]
    HelloWDM.sys = 1,objfre\i386,

    ;--------- ClassInstall/ClassInstall32 Section -------------------------------

    ; Not necessary if using a standard class

    ; 9X Style
    [ClassInstall]
    Addreg=Class_AddReg

    ; NT Style
    [ClassInstall32]
    Addreg=Class_AddReg

    [Class_AddReg]
    HKR,,,,%DeviceClassName%
    HKR,,Icon,,"-5"

    ;--------- DestinationDirs Section -------------------------------------------

    [DestinationDirs]
    YouMark_Files_Driver = 10,System32\Drivers

    ;--------- Manufacturer and Models Sections ----------------------------------

    [Manufacturer]
    %MfgName%=Mfg0

    [Mfg0]

    ; PCI hardware Ids use the form
    ; PCI\VEN_aaaa&DEV_bbbb&SUBSYS_cccccccc&REV_dd
    ;改成你自己的ID
    %DeviceDesc%=YouMark_DDI, PCI\VEN_9999&DEV_9999

    ;---------- DDInstall Sections -----------------------------------------------
    ; --------- Windows 9X -----------------

    ; Experimentation has shown that DDInstall root names greater than 19 characters
    ; cause problems in Windows 98

    [YouMark_DDI]
    CopyFiles=YouMark_Files_Driver
    AddReg=YouMark_9X_AddReg

    [YouMark_9X_AddReg]
    HKR,,DevLoader,,*ntkern
    HKR,,NTMPDriver,,HelloWDM.sys
    HKR, "Parameters", "BreakOnEntry", 0x00010001, 0

    ; --------- Windows NT -----------------

    [YouMark_DDI.NT]
    CopyFiles=YouMark_Files_Driver
    AddReg=YouMark_NT_AddReg

    [YouMark_DDI.NT.Services]
    Addservice = HelloWDM, 0x00000002, YouMark_AddService

    [YouMark_AddService]
    DisplayName = %SvcDesc%
    ServiceType = 1 ; SERVICE_KERNEL_DRIVER
    StartType = 3 ; SERVICE_DEMAND_START
    ErrorControl = 1 ; SERVICE_ERROR_NORMAL
    ServiceBinary = %10%\System32\Drivers\HelloWDM.sys

    [YouMark_NT_AddReg]
    HKLM, "System\CurrentControlSet\Services\HelloWDM\Parameters",\
    "BreakOnEntry", 0x00010001, 0


    ; --------- Files (common) -------------

    [YouMark_Files_Driver]
    HelloWDM.sys

    ;--------- Strings Section ---------------------------------------------------

    [Strings]
    ProviderName="Flying L Co.,Ltd."
    MfgName="LC Soft"
    DeviceDesc="Hello World WDM!"
    DeviceClassName="LC_Device"
    SvcDesc="???"



    注意它可以同时在Win98或者Win2000中使用(系统会通过这个INF文件里面的字段名称,自动选择适合当前系统的安装方法的)。关于INF文件的各个字段含义现在我也不知道,所以也没有办法说清楚,如果谁看到这篇文章,而又知道的话,不妨为我一份。

    准备好这个 HelloWDM.INF 文件后,让我们打开控制面板,双击“添加/删除硬件”,选择“添加/排除设备故障”->“添加新设备”->“否,我想从列表选择硬件”->“其它设备”->“从磁盘安装”,选择 HelloWDM.INF 所在的路径,然后安装。

    当安装完成后,系统就会添加上你写好的驱动程序了。(可以在“设备管理器”中查看到)。然后重启电脑,这个驱动程序就投入使用啦。

    关于安装,我也只知道这么多,到底安装驱动程序时,操作系统都作了些什么,我也不是很清楚,等我弄明白了我再贴上。

    posted @ 2007-11-14 17:06 咖啡色 阅读(364) | 评论 (3)编辑

    好啦,辛辛苦苦终于写完了程序,让我们编译运行吧!按下Ctrl+F5(嘿嘿,让我们先假设你习惯用VC来写程序),我等啊等……疑?怎么毫无动静的?再看看Output窗口,哇!有几百个错误啊!!不禁头大——这是怎么回事呢?

    原来,WDM程序编译出来的并不是我们常见的.exe,而是.sys文件,在未经设置编译环境之前,是不能直接用VC来编译的(这就是为什么会有几百个错误了)。这种类型的文件你可以在WINNT\System32\Drivers里面找到很多。其实驱动程序也是一种PE文件,它同样由DOS MZ header开头,也有完整的DOS stub和PE header,同样拥有Import table和Export table——……那跟普通的PE文件有什么不一样呢?那么就让我们先来做个小剖析,加深对.sys文件的认识吧


    首先祭出Delphi里附带的tdump.exe程序。让我们键入:
    C:\WINNT\System32\Drivers>tdump ccport.sys -em -ee
    参数-em是列出Import table,-ee是列出Export table。回车之后,屏幕列出一大堆东西:

    C:\WINNT\SYSTEM32\DRIVERS>tdump ccport.sys -em -ee
    Turbo Dump  Version 5.0.16.12 Copyright ? 1988, 2000 Inprise Corporation
                        Display of File CCPORT.SYS

    IMPORT:     NTOSKRNL.EXE={hint:011Fh}.’memcpy’
    IMPORT:     NTOSKRNL.EXE={hint:003Dh}.’IoDeleteDevice’
    IMPORT:     NTOSKRNL.EXE={hint:0030h}.’IoAttachDeviceToDeviceStack’
    IMPORT:     NTOSKRNL.EXE={hint:008Eh}.’KeSetEvent’
    IMPORT:     NTOSKRNL.EXE={hint:0068h}.’IofCallDriver’
    IMPORT:     NTOSKRNL.EXE={hint:0095h}.’KeWaitForSingleObject’
    IMPORT:     NTOSKRNL.EXE={hint:0074h}.’KeInitializeEvent’
    IMPORT:     NTOSKRNL.EXE={hint:003Fh}.’IoDetachDevice’
    IMPORT:     NTOSKRNL.EXE={hint:00D3h}.’RtlFreeUnicodeString’
    IMPORT:     NTOSKRNL.EXE={hint:0077h}.’KeInitializeSpinLock’
    IMPORT:     NTOSKRNL.EXE={hint:0129h}.’strcpy’
    IMPORT:     NTOSKRNL.EXE={hint:0121h}.’memset’
    IMPORT:     NTOSKRNL.EXE={hint:003Ch}.’IoCreateUnprotectedSymbolicLink’
    IMPORT:     NTOSKRNL.EXE={hint:0038h}.’IoCreateDevice’
    IMPORT:     NTOSKRNL.EXE={hint:00C2h}.’RtlAnsiStringToUnicodeString’
    IMPORT:     NTOSKRNL.EXE={hint:0069h}.’IofCompleteRequest’
    IMPORT:     NTOSKRNL.EXE={hint:0124h}.’sprintf’
    IMPORT:     NTOSKRNL.EXE={hint:003Eh}.’IoDeleteSymbolicLink’
    IMPORT:     NTOSKRNL.EXE={hint:0042h}.’IoFreeIrp’
    IMPORT:     NTOSKRNL.EXE={hint:004Dh}.’IoInitializeIrp’
    IMPORT:     NTOSKRNL.EXE={hint:002Dh}.’IoAllocateIrp’
    IMPORT:     NTOSKRNL.EXE={hint:0027h}.’InterlockedExchange’
    IMPORT:     NTOSKRNL.EXE={hint:0025h}.’InterlockedCompareExchange’
    IMPORT:     NTOSKRNL.EXE={hint:0035h}.’IoCancelIrp’
    IMPORT:     NTOSKRNL.EXE={hint:012Ah}.’strlen’
    IMPORT:     NTOSKRNL.EXE={hint:0126h}.’strcat’
    IMPORT:     NTOSKRNL.EXE={hint:0114h}.’atoi’
    IMPORT:     NTOSKRNL.EXE={hint:0128h}.’strcmp’
    IMPORT:     NTOSKRNL.EXE={hint:0034h}.’IoBuildSynchronousFsdRequest’
    IMPORT:     NTOSKRNL.EXE={hint:00D5h}.’RtlInitAnsiString’
    IMPORT:          HAL.DLL={hint:0006h}.’KfAcquireSpinLock’
    IMPORT:          HAL.DLL={hint:0009h}.’KfReleaseSpinLock’

    EXPORT ord:0001=’Vcomm_DriverControl’



    看到了吗?它主要调用了NTOSKRNL.EXE和HAL.DLL文件(实际上你会发现,几乎所有的WDM驱动程序都会调用NTOSKRNL.EXE文件,从它的名字你可以看出为什么了吧?),并且输出了一个函数“Vcomm_DriverControl”。这表明,其实.sys跟.exe文件一样,都是一种PE文件来的。不同的是,.sys文件Import的通常是NTOSKRNL.EXE,而.exe文件Import的通常是KERNEL32.DLL和USER32.DLL。

    知道了这些有什么用呢?实际上,由于.sys通常不调用KERNEL32.DLL和USER32.DLL,所以你是不能在设备驱动程序里面调用任何C、C++和Win32函数的,而且也不能用C++关键字new和delete等(可以用malloc和free来代替),而必须使用大量的内核函数。另外,你应该也能看到她调用了像IoDeleteDevice、IoAttachDeviceToDeviceStack等等函数,这些你以前可能没有见过的函数都是些内核函数。为了读者的方便,下面我列出一些常见的驱动程序可用的内核函数:

    Ex…        执行支持
    Hal…        硬件抽象层(仅NT/Windows 2000)
    Io…        I/O管理器(包括即插即用函数)
    Ke…        内核
    Ks…        内核流IRP管理函数
    Mm…        内存管理器
    Ob…        对象管理器
    Po…        电源管理
    Ps…        进程结构
    Rtl…        运行时库
    Se…        安全引用监视
    Zw…        其他函数


    最后让我们再来看看,写设备驱动程序时必须注意的一些问题:

    1、内核宏
    如果查看DDK头文件,会发现有几个内核函数是以宏的方式实现的。这种宏中有几个宏的定义是相当糟糕的。例如,我们看到RemoveHeadList的定义如下:

    #define RemoveHeadList(ListHead)
            (ListHead)->Flink;
            {RemoveEntryList((ListHead)->Flink)}


    如果以以下方式调用RemoveHeadList,则将编译错误的代码:

    if(SomethingInList)
            Entry = RemoveHeadList(list);


    使这个调用安全的唯一方法是使用花括号:

    if(SomethingInList)
        {
            Entry = RemoveHeadList(list);
        }


    所以我们切勿为了贪图一时的方便,而使用不太规范的写法,最好是在所有的if、for和while等语句中使用花括号。

    2、驱动程序函数名称
    跟C/C++的main()函数一样,设备驱动程序也有一个必须存在,而且只能以DriverEntry()为名称的入口函数。然而,除此之外,我们可以使用任何名字来给其他函数命名——只要你自己记得就行了,当然,最好符合某些特定的规范啦,例如匈牙利命名法……

    3、安装时的问题
    ·在Windows98中驱动程序可执行文件必须是8.3文件名。(别问我为什么,我也不知道,我只能建议你去问比尔该死)
    ·如果INF文件中含有非法节的详细资料,Windows将不使用这个INF文件。

    本节罗罗嗦嗦讲了一大堆,跟实际的编程却并没有太大的关系,前传嘛!就是这样的啦!

    posted @ 2007-11-14 17:04 咖啡色 阅读(313) | 评论 (0)编辑

    我在前面也讲过了一些关于编译环境及工具的。在这里结合本例子我再说一下:

    DDK分为98 DDK和2000 DDK两种,它们工作起来是大同小异的,不过有些驱动程序只能在2000 DDK中使用。由于Win98注定是一种即将被淘汰的操作系统了,所以我学习的时候也没有过多的关注,我用的是2000的DDK,所以以下的所有内容都是针对2000 DDK的。

    ·准备工作
    1、确定你已经安装了Visual C++
    2、安装2000 DDK
    3、安装2000 DDK成功后,在“开始”->“程序”里应该有“Development Kits”->“Windows 2000 DDK”的项目。
    注意一定要先安装好VC,然后才安装DDK,这个顺序决不能颠倒!!
    4、保证DDKROOT环境变量设置为Windows 2000 DDK的基目录,如果不是的话,请在控制面板“系统”属性的“高级”标签环境变量编辑器中设置好这个环境变量。


    ·编写必需的文件
    编译WDM程序的时候,有两个文件是必须要有的,它们是:
    1、makefile
    (这个是什么啊?你可能会问。)对于比较年轻的程序员来说,有可能没有见过这个文件吧。其实在VC这些IDE出现之前,我们都必须使用makefile来确定项目中哪些文件需要重新编译,现在的IDE都把这个工作自动做好了
    我们要做的工作很简单,就是提供这样一个文件,它的内容是:

    #
    # DO NOT EDIT THIS FILE!!!  Edit .\sources. If you want to add a new source
    # file to this component.  This file merely indirects to the real make file
    # that is shared by all the driver components of the Windows NT DDK
    #

    !INCLUDE $(NTMAKEENV)\makefile.def


    正如它所述,不要编辑这个文件。事实上每个WDM程序所需要的makefile的内容都是一样的,也就是说,我们只需要简单地copy一个makefile到新的项目中就可以了
    2、Sources

    TARGETNAME=HelloWDM //编译出来的驱动程序的名称
    TARGETTYPE=DRIVER      //编译的类型是驱动程序编译
    DRIVERTYPE=WDM           //驱动程序的类型是WDM驱动程序
    TARGETPATH=OBJ             //生成的文件存放在OBJ目录中

    INCLUDES=$(BASEDIR)\inc;\   //这是需要引入的头文件
             $(BASEDIR)\inc\ddk;\

    TARGETLIBS=$(BASEDIR)\lib\*\free\usbd.lib\  //这是需要引入的库文件

    SOURCES=HelloWDM.cpp\    //这是源码文件


    这个文件指定了驱动程序目标名是HelloWDM.sys,是一个WDM驱动程序,生成的文件存放在OBJ目录中。值得注意的是,“=”前后不能有空格,否则编译的时候会出错。


    ·开始编译
    娃哈哈,前面罗罗嗦嗦讲了一大堆,现在终于到重点了。WDM程序的编译过程比较特殊,它不是在VC里面按F7来编译的(尽管你可以通过设置来达到这一目的),而是通过一个DDK实用工具build来完成。下面我们来讲讲具体步骤:
    1、“Debug”版的生成
    首先,我们假设你的源代码放在D:\HelloWDM里面。请跟着以下步骤:

    “开始”->“程序”->“Development Kits”->“Windows 2000 DDK”->“Checked Build Environment”

    屏幕将显示:(有“回车”的那行是需要读者你亲自打进去的)

    New or updated MSVC detected.  Updating DDK environment….

    Setting environment for using Microsoft Visual C++ tools.
    Starting dirs creation…Completed.

    D:\NTDDK>cd\HelloWDM    (回车)

    D:\HelloWDM>build    (回车)


    如果源代码没有错误的话,生成的HelloWDM.sys将存放在objchk\i386目录中。

    2、“Release”版的生成
    请跟着以下步骤:

    “开始”->“程序”->“Development Kits”->“Windows 2000 DDK”->“Free Build Environment”

    随后的步骤跟“Debug”版相同,不同的是生成的HelloWDM.sys将存放在objfre\i386目录中。
    posted @ 2007-11-14 17:04 咖啡色 阅读(293) | 评论 (0)编辑

    看了好多天的书!特别到书店买了《Windows 200/xp wdm 设备驱动开发》这本书,在这里我不想怎么评论它!对于高手来说,我觉得她一定不能满足,但是对于像我这样想入门的人来说,仿佛看了半天,还是不知道从何下手。什么原理、模型、分层等等讲不讲,讲!绝对应该讲!但是你得快点告诉我怎么先弄一个像“Hello Word!”的什么简单来不能再简单的完整的例子给我呀!到网上找阿找啊!那些高手啊!也不为我们新手写点图文并茂的上手资料。没办法!结合自己的需要再参考一些别人的东东,算是自己的一点不成熟的想法吧!

    我觉得下面这个介绍非常不错!我能看懂,所以贴了出来。

    我道为什么找不到“Hello Word!”呢?原来在驱动开发的例子里是没有所谓的“Hello World”程序的。这主要还是因为网络上的WDM资料太少造成的。但是程序的入口点呢?c语言有Main(),用Vc的常看见的是WinMain(),Delphi开发的是Program里的Begin,但是驱动开发呢?那也是应该有程序的入口点啊。后来我才明白了,那就是DriverEntry()函数。还有一个问题让我怀疑了老半天,那就是驱动开发的源程序中需不需要include头文件呀?为什么会怀疑呢?那是因为我看了半天的书都没有看到一个完整的驱动程序结构。真的是郁闷。下面是我看到的一个完整的结构,我先放上来,让大家看看驱动开发的结构吧。

    /***************************************************************
    程序名称:Hello World for WDM
    文件名称:HelloWDM.cpp
    日期:2002-8-16
    ***************************************************************/

    //一定要的头文件,声明了函数模块和变量:

    #include "HelloWDM.h"

    /***************************************************************
    函数名称:DriverEntry()
    功能描述:WDM程序入口(原来的WinMain被换成了DriverEntry,也是驱动程序的大门)
    ***************************************************************/

    //extern "C"是必须的,表示“用C链接”。如果你的文件名是HelloWDM.c的话,这句可以省略。
    extern "C"
    NTSTATUS DriverEntry(    IN PDRIVER_OBJECT DriverObject, //IN 是一个关键字表示这是一个输 入参数,PDRIVER_OBJECT是一个数据结构的指针,就像PCHAR一样,这个数据结构是什么样子的,后面我会列出来。她描述了一个驱动设备对象。
                            IN PUNICODE_STRING RegistryPath)//参数RegistryPath指定了驱动程序注册表健的路径,因为驱动程序安装后总会在系统注册表里留下一点东西的。
    {
        //指定“添加设备”消息由函数“HelloWDMAddDevice()”来处理:
        DriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
        //指定“即插即用”消息由函数“HelloWDMPnp()”来处理:
        DriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;

        //返回一个NTSTATUS值STATUS_SUCCESS。几乎所有的驱动程序例程都必须返回一个NTSTATUS值,这些值在NTSTATUS.H DDK头文件中有详细的定义。
        return STATUS_SUCCESS;
    }

    //NTSTATUS也是一个数据类型,上面我所说的消息有点不准确的,准确地说是“I/O请求包”,不过如果像我们以前理解消息那样来理解也无不可,我觉得两者太想了。无非就是上层的应用程序通过它来告诉驱动程序,你要给我什么服务吧!IRP_MJ_PNP就是即插即用处理的请求。你发没发觉上面其实是在制造进入各个房间的“小门”


    /***************************************************************
    函数名称:HelloWDMAddDevice()
    功能描述:处理“添加设备”消息
    ***************************************************************/

    NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
                               IN PDEVICE_OBJECT PhysicalDeviceObject)
    {
        //定义一个NTSTATUS类型的返回值:
        NTSTATUS status;
        //定义一个功能设备对象(Functional Device Object):
        PDEVICE_OBJECT fdo;

        //创建我们的功能设备对象,并储存到fdo中:
        status = IoCreateDevice(
            DriverObject,                //驱动程序对象
            sizeof(DEVICE_EXTENSION),    //要求的设备扩展的大小
            NULL,                        //设备名称,这里为NULL
            FILE_DEVICE_UNKNOWN,        //设备的类型,在标准头文件WDM.H或NTDDK.H中列出的FILE_DEVICE_xxx值之一
            0,                            //各种常量用OR组合在一起,指示可删除介质、只读等。
            FALSE,                        //如果一次只有一个线程可以访问该设备,为TRUE,否则为FALSE
            &fdo);                        //返回的设备对象

        //NT_SUCCESS宏用于测试IoCreateDevice内核是否成功完成。不要忘记检查对内核的所有调用是否成功。NT_ERROR宏不等同于!NT_SUCCESS,最好使用!NT_SUCCESS,因为除了错误外,它还截获警告信息。
        if( !NT_SUCCESS(status))
            return status;

        //创建一个设备扩展对象dx,用于存储指向fdo的指针:
        PDEVICE_EXTENSION dx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
        dx->fdo = fdo;

        //用IoAttachDeviceToDeviceStack函数把HelloWDM设备挂接到设备栈:
        dx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);

        //设置fdo的flags。有两个“位”是必须改变的,一个是必须清除DO_DEVICE_INITIALIZING标志,如果在DriverEntry例程中调用IoCreateDevice(),就不需要清除这个标志位。还有一个是必须设置DO_BUFFER_IO标志位:
        fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
        fdo->Flags &= ~DO_DEVICE_INITIALIZING;

        //返回值:
        return STATUS_SUCCESS;
    }


    /***************************************************************
    函数名称:HelloWDMPnp()
    功能描述:处理“即插即用”消息
    ***************************************************************/

    NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,
                            IN PIRP Irp)
    {
        //创建一个设备扩展对象dx,用于存储指向fdo的指针:
        PDEVICE_EXTENSION dx=(PDEVICE_EXTENSION)fdo->DeviceExtension;

        //首先要通过函数IoGetCurrentIrpStackLocation()得到当前的IRP,并由此得到Minor Function:
        PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
        ULONG MinorFunction = IrpStack->MinorFunction;

        //然后把这个Minor Function传递给下一个设备栈:
        IoSkipCurrentIrpStackLocation(Irp);
        NTSTATUS status = IoCallDriver( dx->NextStackDevice, Irp);

        //处理“即插即用”次功能代码:
        //当Minor Function等于IRP_MN_REMOVE_DEVICE时,说明有设备被拔出或卸下,这时要取消资源分配并删除设备:
        if( MinorFunction==IRP_MN_REMOVE_DEVICE)
        {
            //取消设备接口:
            IoSetDeviceInterfaceState(&dx->ifSymLinkName, FALSE);
            RtlFreeUnicodeString(&dx->ifSymLinkName);
            
            //调用IoDetachDevice()把fdo从设备栈中脱开:
            if (dx->NextStackDevice)
                IoDetachDevice(dx->NextStackDevice);
            //删除fdo:
            IoDeleteDevice(fdo);
        }

        //返回值:
        return status;
    }



    /***************************************************************
    程序名称:Hello World for WDM
    文件名称:HelloWDM.h
    作者:罗聪
    日期:2002-8-16
    ***************************************************************/


    //头文件,只是声明一些函数和变量,比较简单就不多说了,请读者自行研究:

    #ifdef __cplusplus

    extern "C"
    {
    #endif

    #include "ntddk.h"

    #ifdef __cplusplus
    }
    #endif

    typedef struct _DEVICE_EXTENSION
    {
        PDEVICE_OBJECT    fdo;
        PDEVICE_OBJECT    NextStackDevice;
        UNICODE_STRING    ifSymLinkName;

    } DEVICE_EXTENSION, *PDEVICE_EXTENSION;

    NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
                               IN PDEVICE_OBJECT PhysicalDeviceObject);

    NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,
                            IN PIRP Irp);



    好了,第一个WDM版的“Hello World”就介绍到这里,虽然实际上它什么都没有做,但是由于它包含了完整的框架,所以对于向我这样的新手来说还是很有参考价值的。至于怎么编译及安装只有下次再说了

    posted @ 2007-11-14 17:03 咖啡色 阅读(545) | 评论 (0)编辑

    因为我学习的时候是在win2000下进行的,所以一切以我学习时的配置为准。

    第一:安装win2000操作系统,我安装是win2000高级服务器版本。

    第二:安装Vc++6.0,我装的是英文版。

    第三:安装win2000DDK;

    通常驱动程序的调试都是用ddk在cmd中完成的。这部分我暂时略过。下面先介绍如何设置vc++6.0在Visual Studio 6.0集成环境中开发设备驱动程序的方法。

    在Windows上,Windows DDK提供的开发环境是基于命令行的,操作起来极为不便,而Visual Studio 6.0给我们提供了非常友好易用的集成环境,让我们有如虎添翼之感。 
      那么,能否利用Visual Studio的集成环境来开发驱动程序呢?答案是可以的。通过对Visual Studio集成环境的简单设置,创建好自己的驱动开发集成环境就可以了。

    首先要求系统已安装DDK和Visual C++6.0(安装时选上所有工具),

    1、接下来需要改造ddk\bin\setenv.bat 把要求mstools的有关语句注释掉(若想在命令行环境开发驱动则还需加入call VC_DIR\VC98\Bin\Vcvars32. bat),以便能在命令行使用vc的相关工具;若只想在IDE环境开发就不必调用Vcvars32.bat,因为相关工具的路径信息可以在vc环境中设置.) 

    2、创建一个目录DriverEnv(目录名随意),作为你开发驱动的大本营 

    3、在该目录下创建一个批处理文件MakeDrvr.bat,内容如下:

    @echo off 
    if "%1"=="" goto usage 
    if "%3"=="" goto usage 
    if not exist %1\bin\setenv.bat goto usage 
    call %1\bin\setenv %1 %4 
    %2 

    cd %3 
    build -b -w %5 %6 %7 %8 %9 

    goto exit 

    :usage 
    echo usage MakeDrvr DDK_dir Driver_Drive Driver_Dir free/checked [build_options]echo eg MakeDrvr %%DDKROOT%% C: %%WDMBOOK%% free -cef 
    :exit 


    该批处理首先对传递的参数作一些检查,然后调用ddk的setenv命令设置环境变量,然后改变目录为源程序所在驱动器和目录,并最后调用build,-b保证显示完全的错误信息,-w保证在屏幕上输出警告,在vc ide里的output窗口中可以看到这些错误和警告。

    4.建立一个空白工程 
    选File的new菜单项,然后选project栏的makefile,然后输入路径,一路next下去即可,visual studio提供两种配置win32 debug和win32 release. 

    5.修改这两种配置 
      选project的settings菜单项win32 debug: 
      在Build Command Line一栏填入MakeDrvr DDK_DIR SOURCE_DRIVE SOURCE_DIR checked [build options] 
      在Rebuild all options一栏填入 -nmake /a 
      在output file一栏填入与sources文件中的TARGETNAME相同的文件名 
      在Browse info file name一栏填入obj\i386\checked\(与TARGETNAME相同的文件名,见下述).bsc 

      win32 release: 
      在Build Command Line一栏填入MakeDrvr DDK_DIR SOURCE_DRIVE SOURCE_DIR free [build options] 
      在Rebuild all options一栏填入 -nmake /a 
      在output file一栏填入与sources文件中的TARGETNAME相同的文件名 
      在Browse info file name一栏填入obj\i386\free\(与TARGETNAME相同的文件名).bsc 
      注:DDK_DIR一般可以写成%BASEDIR%,build options一般为-cef即已足够 

    6.添加源文件到工程 
      可以新建,也可以添加,这和普通的win32开发一样。 

    7.添加资源文件 
      选INSERT的RESOURCE菜单项即可 

    8.把文件makefile放入源程序目录,其内容总是 

    # DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source 
    # file to this component. This file merely indirects to the real make file 
    # that is shared by all the driver components of the Windows NT DDK 


    !INCLUDE $(NTMAKEENV)\makefile.def 

    9.把文件Sources放入源程序目录,内容为 
      TARGETNAME=RamDrive//这是要生成的驱动程序.sys文件的名字 
      TARGETPATH=obj //.sys文件所在目录的上层目录,(由于ddk的bug)应手工在obj目录下创建checked和free目录,以作为.sys的最终存放目录 
      TARGETTYPE=DRIVER //驱动程序的类型,一般不变 
      INCLUDES=$(BASEDIR)\inc //ddk包含文件路径,一般不变 
      SOURCES=RamDrive.cpp RamDrive.rc //源文件(不要头文件),资源文件 
      BROWSER_INFO = 1 //若想要浏览信息,则要有本行;否则可无 

    10.因为MakeDrvr.bat在DriverEnv目录,所以应把该目录添加到vc的Executable files里面 
      选tools的options菜单项,然后选directories页,在show directories for一栏选择Executable files,然后添加即可. 

      至此,环境设置完毕,你可以按F7, build你的驱动程序了。

    posted @ 2007-11-14 17:00 咖啡色 阅读(619) | 评论 (0)编辑

        这个系列的文章是我在学习驱动开发时在网络上找的资料,因为觉得写的不错就转在这了~

        很久没有网了,出了一段时间的差,近来,莫名的就有点郁闷!前不久在大富翁上发了一份帖子是关于delphi程序员的发展,大家的反应并不都是很好。于是开始觉得可以考虑换个方向。以前我是做MIS开发的。换哪个方向呢?人越多的方向,好像越是没有前途。想想当初上大学,那可是越多人考的学校,学费越贵啊!可现在的职业呢?越多人干的事,越是没有前途了。考虑来考虑去,决定学习一下驱动程序的开发吧!于是从网上查找了一些资料,看的懂的觉得蛮不错适合我这种小学生的就贴了出来,算是学习笔记吧!

    用户模式与内核模式

    从Intel80386开始,出于安全性和稳定性的考虑,该系列的CPU可以运行于ring0~ring3从高到低四个不同的权限级,对数据也提供相应的四个保护级别。运行于较低级别的代码不能随意调用高级别的代码和访问较高级别的数据,而且也只有运行在ring0层的代码可以直接对物理硬件进行访问。由于WindowsNT是一个支持多平台的操作系统,为了与其他平台兼容,它只利用了CPU的两个运行级别。一个被称为内核模式,对应80x86的ring0层,是操作系统的核心部分,设备驱动程序就是运行在该模式下;另一个被称为用户模式,对应80x86的ring3层,操作系统的用户接口部分(就是我们通常所说的win32 API)以及所有的用户应用程序都运行在该级别。操作系统对运行在内核模式下的代码是不设防的,所以不管是建设还是破坏内核模式下的编程都是值得去研究的。

    图1-WIN2000系统的分层结构

    在物理硬件与系统核心之间有一个硬件抽象层(HardwareAbstractionLayer),它屏蔽了不同平台硬件的差异,向操作系统的上层提供了一套统一的接口。从图中我们还可以看到,设备驱动程序(DeviceDriver)是被I/O管理器(I/OManager)包围起来的,即驱动程序与操作系统上层的通信全部都要通过I/O管理器。这给驱动程序的编写带来了很大的便利,因为很多诸如接收用户的请求、与用户程序交换数据、内存映射、挂接中断、同步等等麻烦的工作都由I/O管理器代劳了。

    驱动程序的分类

    驱动程序并不像所有人想的那样一定要和硬件打交道,我粗略的把他分为两类:硬驱动和软驱动。硬驱动就是对硬件直接编程进行控制,这类驱动通常必须遵守硬件的通信协议,直接对硬件进行端口访问、中断响应、DMA传输。它包括:串、并行口,键盘,文件系统,SCSI,网络等驱动程序;另外一种软驱动呢?不需要直接对硬件就行操作。我认为他可以理解为它是在硬驱动之上的一层更为高级的驱动。我想学习的主要是软驱动。

    一般来说,设备驱动程序的任务主要有两个:第一,接受来自用户程序的读写请求,把用户的数据传送给设备,或把从设备接收到的数据传送给用户;第二,轮询设备或处理来自设备的中断请求,完成数据传输。

    驱动程序的结构

    在这里,我主要介绍WDM的结构。WDM(Windows driver module)是什么东西呢?在Windows98\95下面,也许你听得最多的是VXD,我只知道VXD是一种驱动程序,和WDM差不多的东西。只是因为Windows2000是WindowsNT那条线过来的东西,要加上两个主要的新功能:即插即用(Plug and Play)和电源管理(Power Menage),又不能用Windows98\95那一套,所以就搞出一个叫WDM这么个东西,来支持PNP和PM.。其实想想,现在的技术名词还不是一般的多啊!总之wdm大家都叫它windows驱动程序模型

    Windows2000里有叫即插即用管理器和I\O(此I\O非彼I\O端口)管理器的两个东西。比如说我在机器上插了一张符合PCI规范的PCI卡。即插即用管理器会发现这张卡插在第N个插槽上,然后即插即用管理器会说它找到了这样一张卡,它就去找有没有现成的驱动程序,如果没有找到,它会告诉我们,我找到了这样一张卡,请你插入这张卡的驱动程序盘。好,我们就把驱动程序盘给它,即插即用管理器会去找驱动程序盘上的INF文件,找到后它会比较PCI卡上的标志和INF文件里的标志是否相同,如果相同,它就会依照INF文件里提供的路径去找驱动程序,找到之后就可以交给I\O管理器,I\O管理器会装载这个驱动程序。I\O管理器在做了一些接口的工作后,即插即用管理器会先分配好相关的资源给PCI卡,比如说I\O端口空间、内存空间和中断向量,然后告诉这张卡的驱动程序,我给你分配了这些资源,你看怎么的。如果你没有怎么的或不敢怎么的,那就赶快记下这些资源,以备后用。
        下面说I\O管理器这个东西。上面我们讲到I\O管理器装载这个驱动程序,驱动程序有一个大门,还有N多的小门。I\O管理器先从大门进去(因为I\O管理器只找得到大门,I\O管理器是不是很傻,NO,当然有它的道理,你别问我:I\O管理器怎么找到大门的?驱动程序无非就是一些文件,I\O管理器把这么些文件加载到系统中去),去找一样东西:进小门的地图。我们要在大门进去的房间里放这张地图(驱动程序都是我们造的,我们当然有驱动程序的地图啦)。I\O管理器找到了地图,就可以自由进出大小门了。———这些大小门说白了就是函数(不要问我函数是什么东东),小门的地图就是函数的地址。I\O管理器知道了这些函数的地址,当然就可以调用这些函数啦。还有一个叫IRP的东西,中文名叫I\O请求包。我们这样来理解它:在用户的应用程序这一端,要和驱动程序对话,它们之间不是简单的调用函数(至于为什么,我现在也不知道),应用程序和驱动程序之间有I\O管理器隔着,应用程序对驱动程序的操作,首先由I\O管理器处理成一个包,这个包里面有应用程序请求的操作内容、传送的数据等等一些东西,然后I\O管理器把这个包扔给驱动程序,驱动程序依照包里的请求,完成操作,把该回传的数据放进包里,再把包扔还给I\O管理器,I\O管理器再把数据返回给应用程序。——这里所说的包,就是IRP。
        这里说的只是WDM结构的一部分,但是有了这一部分知识,其它部分就不难懂了。通过上面的介绍你看见了什么。你可以想象得出驱动程序是什么样子的吗?我是这样想的。驱动程序好像就是一个函数库,只不过在大门的地方放了一张地图。而这个大门与地图我们见到过吗?好像有点像dll文件呢。在早些时候我学dll的时候,我就只会用dll来存放函数。

    posted @ 2007-11-14 16:59 咖啡色 阅读(612) | 评论 (1)编辑
    声明:偶第一次在博客园写点东西,且水平处于菜鸟阶段,有不对的地方大家就当笑话看看好了~

    也许富客户端本是针对基于浏览器的bs结构的程序而言的,我说的这样客户端也是需要安装的,也许和remoting差不多吧。不过我比较笨,remoting怎么也没弄明白,反到是对webservice很是喜欢,于是将自己写的程序加上webservice,由cs结构变成了bs结构了。
    光说这些也不清楚,我结合最近一个项目中的实例来说下吧。

    在公司做电梯项目的时候,因为需要在客户端发卡(类似于向公交卡中写入 信息的过程),且主程序是bs结构的,于是开始计划用activex控件来完成,但是偶对c++不感冒,且最近网络不安全,对控件(特别是浏览器控件)防范很多,于是只好另换别的方法,无意中看到了webservice,其实我以前也听说过webservice,但一直没亲自44,这次用过之后,才知道webservice是即容易学,有好用。捡到宝了,哈哈~

    我简单说下webservice的用法:
    1)在服务端用vs建立一个web服务项目,然后按照helloworld的样式写个你需要的函数,如名称为ConnectServer(),假定此函数的功能为测试服务器链接,正确返回2008,否则链接失败。
    2)然后将服务端发布到本地iis的虚拟目录中如myweb,
    3)建立一个客户端(应用程序),添加一个web应用,如:http://localhost/myweb/service1.asmx,名称:myservice这样就完成了webservce的引用了。
    4)在调用的时候:myservice.service1 myse = new myservice.service1();
                                  int result = myse.ConnectServer();
    5)记得在int result前加一句mse.Url = myserviceUrl,这个地址放在你的客户端配置文件中就好了。
    这样一个完整的webservice应用就完成了。

    然后具体在我项目中,利用webservice获取卡片的信息,然后存储到本地,再调用本地的程序将数据写入卡片中,这样即免去了控件的烦恼,又实现了客户端及时的与服务器交互,呵呵,何乐而不为呢?
    而且webservice是由iis维护的,可以为其设定特定的线程池,一下子就多线程及线程优化的任务推给了ms,哈哈~
    btw:如果webservice要部署到远程服务器,请在webconfig中的system.web中添加<webServices>
          <protocols>
            <add name="HttpSoap"/>
            <add name="HttpPost"/>
            <add name="HttpGet"/>
            <add name="Documentation"/>
          </protocols>
        </webServices>

    Over。就写这么多了,再多偶也写不出来了,嘻嘻!
    posted @ 2007-08-13 14:52 咖啡色 阅读(113) | 评论 (0)编辑
    从今天起,偶就在博客园安家了,发个帖子庆祝下先,哈哈~
    posted @ 2007-07-24 08:12 咖啡色 阅读(23) | 评论 (1)编辑