|
|
May 14 2008年5月12号下午一场突如其来的7.8级大地震(经国家地震局修正为8.0级),撼动了全国人民的心灵,在这里任何文字都显得那么苍白无力,为灾区人民祈福,希望更多的伤员获救,相信我们的人民解放军,相信我们的政府,相信我们的党! April 13
大学生活即将结束,由于学业的原因已经有很长时间没有更新自己的空间了,现如今的我也将走出校门步入社会了,最近我幸运的考取了省委组织部选调生,成为了一名公务员。这也是我多年的夙愿,能够为国家效力,为祖国的建设尽自己一份绵力。 当今世界唯有强者才可以生存,我中华要立于世界强国之林,我中华民族要立于世界优秀民族之林,需要我们几代人的努力才可能实现。中华民族曾经兴盛过,盛唐之时的中国在世界上甚至要比今日的美国还要强大,但是我们的国家也曾经衰败过,也曾经任由列强欺凌,割地赔款,可谓国家的尊严丧失殆尽,人民也生活于水深火热当中,是啊,国且不国而何况于人民乎。但是中华民族从来就不是弱者,他们曾经沉睡过,但是当他们醒来就会发愤图强,就会强大起来。伟大的孙中山先生就是我国革命者的先驱,他希望以三民主义救国强国,但是事实证明资本主义并不能救中国。如果要救中国那么就必须另寻他路,伟大的中华民族是不屈不挠的,我们终于找到了救国兴家之道——共产主义。最终中华民族在中国共产党的坚强领导下战胜了敌人,并且在一代代共产党人的不懈努力下使我们的祖国日渐昌盛、繁荣、富强起来。全国各族人民在科学发展观的统领下,共同构建和谐社会,全面建设小康社会的号角已经吹响。 我们又是一个热爱和平的民族,我们要和平崛起。我们的发展只会给世界人民带来共同的发展、共同的利益。“上善若水,水利万物而有静”传统的道家思想有力地证明了我们民族优秀的品格。这段时间“藏独”不法分子的打砸抢烧和破坏奥运的恶劣行径已经引起了全国各族人民和全世界各正义的国家和人民的强烈愤慨。我相信正义最终将战胜邪恶,中国人民将团结起来战胜邪恶,事实将证明“藏独”不法分子和外国敌对势力只是历史上的跳梁小丑为世界人民所唾弃。西藏问题是我们国家的内部问题,我们的国家有责任、有能力解决我们自己的内部问题,这是一个世界各国都应该清醒认识到的问题。同时我也相信我们的国家会成功的举办本届北京奥运会,在奥运史上留下光辉的一笔。 September 05
对酒当歌,人生几何? 譬如朝露,去日苦多。 慨当以慷,忧思难忘。 何以解忧?唯有杜康。 青青子衿,悠悠我心。 但为君故,沈吟至今。 呦呦鹿鸣,食野之苹。 我有嘉宾,鼓瑟吹笙。 明明如月,何时可掇? 忧从中来,不可断绝。 越陌度阡,枉用相存。 契阔谈宴,心念旧恩。 月明星稀,乌鹊南飞, 绕树三匝,何枝可依? 山不厌高,海不厌深。 周公吐哺,天下归心。
August 24 大家如果要买笔记本,千万不要买东芝的。因为东芝的风扇太烂了。
我的一款东芝A10笔记本,用了两年,风扇转起来就像一拖拉机,前一段时间干脆罢工了。于是跑去维修部,提示修不了,换新的,420块,一分不少,太坑人了。特生气,跑了几家专卖和笔记本维修部,结果一家一个家,最终以260元的价格在一笔记本维修部更换了新风扇。回到家,拆开旧风扇,一番折腾后竟然修好了。意想不到的是260的风扇用了没几天,坏了。太气人,简直是花钱买气,跑去科技市场,退了风扇。装上自己的风扇继续用,头两天里,一点声音都没了,还暗自庆幸,结果没几天,噪音又来了。最后彻底无奈了,只好等其再不转后拆下自己修了。
以后买笔记本,买国货就买联想的,国外的就买戴尔、惠普的,非要买日货就等到时候受罪吧,只要是日货卖到中国的就没有好货,用一段时间就容易出问题。一年多前我也加入了抵制日货的行列中来,不过日本的高科技确实厉害,我们也得自己争气才是,以后让鬼子们也用我们的高科技产品。
August 20 你好,命运……
转眼人生已过二十余载,回望短暂的这二十年,我说,你好,命运……
二十余载,似乎命运似乎由自己掌握,转眼翘望,却发现自己分不清是自己掌握着命运,还是命运掌握着自己,正茫然不知所以时,我看到命运就在不远处也正望着我。就像一面镜子,你离他远去,他也离你而去;你靠近他,他也向你走来。哈,这就是命运啊。
二十余载,对于历史的车轮,不及一辙,对于我却真的不能说是短暂,在这些年里,尤其是后半段,开心过、悲伤过、得意过、失落过、幸福过、痛苦过……一切的一切对我来说是那么的刻骨铭心。诚如梁漱溟先生所说,“因为人类生命是沿着动物生命下来的,沿着动物生命而来,则很近于一个动的机器,不用人摇而能自动的机器。机器是很可悲悯的,他完全不能自主。”是啊,我不是一样可悲悯的吗,这二十余载我有太多的不应该、不可以,太多的后悔,太多的失败。
二十余载,二十余载的人生,失去了,收获了,哭了,笑了——过去了。
二十余载,过去了。
新生的我,开始了!新生的命运,我也看到你了。
你好,命运! July 26 教务管理系统开发已经结束一段时间了,今天来总结一下,开发教务管理系统对我在软件开发中的一个转折点。通过这款软件的开发,让我自己对软件工程学对软件开发的重要作用有了更深一层次的认识,同时也看到了在没有好的软件工程学思想指导的情况下,升级和维护一个软件的困难。
做软件,系统分析要是做不好的话,后面的工作可以说是步步维艰啊。
这款软件代表了我的一个技术的转折点,所以这款软件至今也是存在不少的问题。
学不可以已矣。自己的水平还是有待提高,希望自己早日能成为一个真正的开发高手。 July 19 2006年 7月18日,我被任命为CSDN-VFP版主,应该是值得纪念的日子吧。
注册CSDN大约是在上高中的时候,2002年左右吧记不清了,那时候主要是看《程序员》和《程序春秋》杂志,于是跑去注册了一个帐号。结果把帐号密码给忘记了,刚上大学时(2003年10月18日)又注册了一个,才有了这个TERRYYRRET的帐号,就是TERRY和其镜像,呵呵。不过由于宿舍是没有电脑的所以仍然是不常去的。
2006年一月,专升本考试结束,回到家,开始了我的CSDN生活。2月到了专科母校专职从事学校教务处的MIS的开发,由于办公室可以上网,而编程工作比较轻松,所以整日里在论坛上回帖,给人解决问题,当然从中我也进步了不少。认识了十豆三、清风、随风、天马星空、Sunya、seaman、杨柳、IntoWindy、飘叶等等的好朋友。我真的很高兴。这是我在CSDN最大的收获。
9月就要开学了,要再上两年。不知到时候还有机会再在论坛吗,本科学校能装电脑,这样的话,我想在中午、下午、晚上的时间还是可以常去看看的。
最近正在学习VC++,争取在开学前要掌握了,不然开了学就没时间了,整日里啃课本了。
July 18
如何在程序的开始检测权限和根据权限操作
-
在一个程序的开始部分,如何根据不同的口令字来区分不同的登权限,各自的 口令字和权限设置又如何加以保密,以防止非法查看和修改? 我们辛辛苦苦编制的程序又如何根据每台机器的硬件来加以判断用户的合法 与非法,以防止非法拷贝? 针对以上问题,我编制了一个简单的程序实例供有兴趣的朋友参考。
该实例并非十分完美,也很简单,意在提供一种思路,大家可根据自己的情 况,加以完善。 实例共有一个数据表和四个模块组成,分别是: 一、KLK.DAT 这是经过vfpjmdbf.vcx加密后的数据表,解密后的表结构如下: ======================================================== 表文件名: KLK.DBF 数据记录数: 3 最近更新的时间: 10/17/1999 代码页: 936 字段 字段名 中文含义 类型 宽度 小数位 索引 排序 1 BH 编号 字符型 2 2 XM 姓名 字符型 8 3 PASSKL 口令字 字符型 12 4 QX 权限 数值型 1 5 VARJB 硬盘卷标 数值型 12 ** 总计 ** 36 =========================================================
二、Main.prg 系统初始化主程序,大家在调试时务必首先运行它,否则后面的模块无法运行。 它很简单: set stat off set scor off set date ansi set century on set hours to 24 set escape off set dele on set safe off SET STATUS BAR OFF SET SYSMENU TO SET SYSMENU AUTOMATIC on shutdown quit debug0=.t. &&为后面的模块提供检测菜单是否运行的条件 Dqml=sys(5)+sys(2003)+'\' &&取当前目录,调试时请您改为本程序所在目录 do (Dqml+'菜单1.mpr') &&打开菜单文件 do form (Dqml+'pass') &&打开效验口令字表单 read even on shutdown rele debug0 set sysmenu on set sysmenu to defa on error
三、pass.scx 表单。 功能:口令字效验、取出KLK中保存的硬盘卷标、判断当前硬盘卷标是否 合法、 取出KLK中的权限。 在表单及对象的方法或事件中,各自的代码是: 1.this.ini if type('debug0')='U' &&如果未通过Main.prg而调式,则: wait wind '请您从执行Main.PRG开始,进行调式......' this.release retu endi ******以下为解密klk.dat文件************************************** thisform.Vfpjmdbf1.pass_kl='12345678' &&设置加解密口令 thisform.Vfpjmdbf1.path_ml=dqml &&设定klk.dat文件所在目录 thisform.Vfpjmdbf1.jiEmi_file='klk.dat' &&存放密码的资料库文件名 thisform.Vfpjmdbf1.jiEmi &&解密klk.dat文件,并打开以'klk'为别名的工作区 thisform.Vfpjmdbf1.Visible=.f. &&隐藏 Vfpjmdbf1 Go top ******************以下为取磁盘卷标的WIN32API调用******************* Stor 0 to C_var,C_cd,C_qf C_disk='c:\' &&指定磁盘 DECLARE INTEGER GetVolumeInformation IN Win32API STRING @, STRING @,; INTEGER,; INTEGER @, INTEGER @, INTEGER @, STRING @, INTEGER xx=GetVolumeInformation(C_disk,"",20,@C_var,@C_cd,@C_qf,0,0) CLEAR DLLS * C_var=指定磁盘的卷标,是个十进制的数值。 if varjb=0 &&klk.varjb=0 表明是第一次运行本软件 repl varjb with C_var &&将磁盘卷标写入klk.varjb字段 thisform.Vfpjmdbf1.jiAmi_file='' thisform.Vfpjmdbf1.jiAmi &&先关闭,以保存修改 *要保存修改必须经Vfpjmdbf1.jiAmi来关闭库才行, thisform.Vfpjmdbf1.jiEmi_file='klk.dat' &&存放密码的资料库 thisform.Vfpjmdbf1.jiEmi &&再打开 else &&否则是已运行过本软件 if varjb<>C_var &&判断klk.varjb是否等于当前磁盘卷标(C_var) =Messagebox("对不起!您现在运行的软件未经过作者确认,不能运行... ",; 0+48+0,"警告") quit endi endi
2.this.unload if type('debug0')='U' do Case &&根据klk.qx字段决定菜单的屏蔽操作 case qx=1 set skip of bar 1 of 权限F .t. set skip of bar 2 of 权限F .t. case qx=2 set skip of bar 1 of 权限F .t. set skip of bar 2 of 权限F .t. set skip of bar 3 of 权限F .t. endc use &&关闭klk endi
3.thisform.text1.keypress if nKeyCode#13 retu endi loca for allt(this.value)==allt(Passkl) &&查找相符的口令字 if !foun() =Messagebox("对不起!您的口令有误,不能运行...... ",; 0+48+0,"警告") thisform.release quit &&退出系统,为了简单,口令效验我只设了一次。 endi thisform.release
四、查看和修改KLK的表单,这里就不具体写代码了。 五、菜单1.MPR 主菜单,这里就不具体写代码了。
实例中的权限密码是:0=全权 口令=aaaaaa 1=可修改自己的密码 口令=666666 2=不能作任何修改 口令=888888
需要以上程序源代码实例的,这里下载
从以上代码中可以看出,里面没有什么新的东西,所用的工具或资料,在我网上都 可找到,我只不过是把它们拼凑一下而已。您可把这个实例编译成EXE文件(VFP5.0的 朋友可能无法把共享版的Vfpjmdbf.vcx编进去),先在您的机器上运行一下,然后再 把它们直接拷贝到另外一台机器上一试,您看看还能运行否? 当然,别人只要用REFOX7.06把里面的Main.prg反编译出来,去掉: “ do form (Dqml+'pass')”这一句,还是可以非法拷贝的,那怎么办? 最好的办法是加密您的EXE文件,这样您的软件就比较保险了,最起码Refox等现成 的反编译工具是不可能反编译的。 在我网上有加密VFP&EXE的工具下载,您可下载一试。 需要说明的是:在我网上下载的vfpjmdbf.vcx和Vfp&Exe.exe都是共享版,功能和 使用次数都有一定的限制,如果您觉得还可以,请您花很少一点钱来注册这两个工具, 注册后,我将提供正式版和今后的免费升级。(最新Vfp&Exe.exe可以设置您自己的图表了)
如何把数字转换为汉字大写金额代码分析
-
[编程心得]又和您见面了,这次所写的是:怎样才能合理利 用 VFP本身所提供的命令和函数来组织代码、编制程序,以提高 运行效率。为了能说明问题,我以数字转换为大写为例,通过对 三组代码的对比来谈谈我个人对此问题的认识。 每种语言提供给我们的命令或函数,可以说最能体现这种语 言的本质,而每个程序员如何运用和组织这些命令或函数,则可 以直接反映这个程序员对这种语言的理解和掌握程度。现在虽然 更多的是强调如何学习和掌握面向对象的编程方法,以及如何灵 活运用这些对象中的事件和方法来组织代码,但我们切不可忽视 对这些最基本的语言命令或函数的学习、理解和灵活掌握。 下面我们以数字转换为大写为例来加以说明: 对于如何把数字转换为汉字大写金额的编程,我在网上看到 了许多种写法,代码最多的一种写法,其代码竟多达 100多行, 让人看了眼花缭乱,很难理解,而代码最少的却连10行都不到, 而其运行结果却是完全相同,可见理解和合理组织编程语言所提 供的命令和函数是何等的重要。 考虑到版面的原因,那近百行的代码我就不具体举例了,下 面的这些代码是我自行组织编写的,如有雷同,则纯属巧合: 代码一、 FUNCTION Rmbzh 1. PARA nDhsj 2. rmbxx=allt(str(nDhsj,12,2)) 3. lszs=allt(str(int(nDhsj))) 4. cd0=len(lszs) 5. dws0='元拾佰千万拾佰千亿' 6. sh0='壹贰叁肆伍陆柒捌玖零' 7. rmbdx='' 8. cd1=cd0 9. for I=1 to cd0 10. lspd=right(lszs,cd1) 11. if val(lspd)=0 12. rmbdx=rmbdx+iif(cd1>4,'万元','元') 13. exit 14. endi 15. ss=int(val(subs(lszs,I,1))) 16. if ss#0 17. rem0=SUBSTRC(sh0,ss,1)+SUBSTRC(dws0,cd1,1) 18. else 19. rem0=iif(I#cd0,'零','元') 20. endi 21. rmbdx=rmbdx+rem0 22. cd1=cd1-1 23. endf 24. do while atc('零零',rmbdx)>0 25. cc=10-atc('零零',rmbdx) 26. rmbdx=strtr(rmbdx,'零零',SUBSTRC(dws0,cc,1),1,1) 27. rmbdx=strtr(rmbdx,'零零','零',1,1) 28. rmbdx=strtr(rmbdx,'零零','',1,1) 29. endd 30. lsxs=allt(str(nDhsj,12,2)) 31. lsxs=right(lsxs,2) 32. if val(lsxs)#0 33. ss=int(val(subs(lsxs,1,1))) 34. rem0=SUBSTRC(sh0,ss,1)+'角' 35. rmbdx=rmbdx+rem0 36. ss=int(val(subs(lsxs,2,1))) 37. rem0=iif(ss=0,'整',SUBSTRC(sh0,ss,1)+'分') 38. rmbdx=rmbdx+rem0 39. else 40. rmbdx=rmbdx+'整' 41. endi 42. Retu rmbdx 这组代码是从纯数字的角度去考虑如何转换的,所以显得不 够简洁, 比如第3行和第31行,分别把整数部分和小数部分加以 分开,各自进行转换,就显的没有这个必要,但它也有可取之处, 比如: 1.充分利用了系统提供的函数,尤其是SUBSTRC()这个双字节 函数。 2.它运行的结果可以达到完全口语化的汉语金额,如: 12300.00元,其运行的结果是:壹万贰千叁佰元整,完全符合支 票类填写方式。但却不符合填充类方式。
代码二、 FUNCTION Rmbzh 1. PARA nDhsj, nDW &&nDW如果等于1,表示要金额单位 2. nDzs=strt(allt(str(nDhsj,15,2)),".","") &&把小数点去掉 3. hzdx="零壹贰叁肆伍陆柒捌玖" 4. cDW="分角元拾佰仟万拾佰仟亿拾佰仟" 5. rmbdx="" 6. nCd=len(nDzs) 7. for i=1 to len(nDzs) 8. NumS=substrc(hzdx,int(val(subs(nDzs,i,1))+1),1) 9. nDWs=iif(nDW=1,substrc(cDW,nCd,1),spac(2)) 10. rmbdx=rmbdx+NumS+nDwS 11. nCd=nCd-1 12. endfor 13. Retu rmbdx 这个代码就是在我网上提供下载的那个实例,可以看出它非常简洁, 编程线路清醒,第7行和第8行分别是数字转换和单位转换,不仅简单 明了,而且还可根据需要选择是否需要金额单位,但也有缺点,就是 不能象上例那样做到完全口语化,我之所以把它作为实例供下载,原 因就在于其清晰的思路和简洁的代码组织形式。 那么这组代码还可以“精简”(其实应该叫合并)吗?答案是: 当然可以。
代码三、 FUNCTION Rmbzh 1. PARA nDhsj, nDW &&nDW如果等于1,表示要金额单位 2. nDzs=strt(allt(str(nDhsj,15,2)),".","") 3. hzdx="零壹贰叁肆伍陆柒捌玖分角元拾佰仟万拾佰仟亿拾佰仟" 4. rmbdx="" 5. for i=len(nDzs) to 1 step -1 6. NumS=substrc(hzdx,2*val(subs(nDzs,len(nDzs)-i+1,1))+1,1) 7. nDws=iif(nDW=1,substrc(hzdx,i+10,1),space(2)) 8. rmbdx=rmbdx+NumS+nDwS 9. endfor 10. Retu rmbdx 可以看出这组代码又比上一组少了三行代码,而其最终运行的结果 是一样的,但它所带来的副作用是:代码不直观明了,不细细查看, 一时难以明白,尤其是第6行和第7行中substrc()中的参数需经过多 次运算方可得知,这对今后的维护带来了不便,我一般不采用这种写 法。如果硬是要再合并的话,那这组代码还可以继续合并,比如可以 把6-8行的代码合并为1行。
从上面的这几这几组代码我们不难看出,同一种运行目的,可以 有各种不同的代码组成和表现形式,也可以体现每个程序员各自不同 的编程风格,但不管如何,提高程序的运行效率是每个程序员所共同 追求的。 如果您有一定的汇编知识,可以把第二组代码和第三组代码编译 成可执行程序后,进行反汇编,这时您就会发现它们的汇编结果几乎 是完全相同的,也就是说它们在运行时仍然把您浓缩在一行中的源代 码分开,一条指令再一条指令地执行的,并不是按您源代码中的意愿, 一行代码就一条指令,因而这种“合并”,对提高运行速度和效率没 任何作用。既然如此,一味追求VFP源代码的“精简” 就显的毫无意 义了,反而给自己带来维护的不便。所以不管如何写代码,直观清晰 的编程思路和尽可能地提高运行效率才是我们所提倡的编程风格。 我谈了这么多所谓的体会,并不是说我写的代码就是高效率和完 全合理的,我只是想从另一个角度来谈谈如何在学习和掌握面向对象 编程的过程中,认识和重视这些最基本的知识,只有这样才不会浪费 VFP为我们提供的这些非常丰富的命令和函数。 自办了这个[编程心得],我收到了许多编程爱好者的来信,希望 我继续写下去,可能是由于我的水平有限,还真有些感到骑虎难下, 所以每期的主题显得很凌乱,还望各位理解。如果大家觉得这个栏目 对您的编程有所帮助的话,那我会继续努力的。
如何注册ActiveX控件
- 我曾在“编程心得(三)”中提到:“经常在网上看到因“XXX.OCX
没有注册,不能运行,怎么办?”这个问题,最近我也陆续开发了一些以 .OCX为后缀的ActiveX控件,这次我就简要地介绍一下什么是ActiveX控 件、如何注册ActiveX控件的问题。 如果我们直接讲OLE控件(Object Link and Enbed 译: 对象链接 与嵌入)恐怕大家就比较熟悉了,因为在VFP的帮助里常常会看到“OLE” 这三个字母,而OLE是指在Windows环境下应用程序间信息共享和传送的一 种技术,其包含的内容非常广泛,OLE控件(.OCX)只不过是OLE庞大技术 体系中的一种。在VFP中,我们可以简单和笼统地说:ActiveX控件(.OCX) 就是目前最高版本的OLE控件。 在这里我并不想详细介绍OLE或ActiveX,而是介绍如何注册ActiveX 控件和当系统出现“XXX.OCX没有注册”后怎么办的问题。 1.ActiveX控件一般存放在什么文件夹里: 要注册ActiveX控件,你首先必须知道ActiveX控件具体的文件名和它 所在的文件夹。ActiveX控件是以 .OCX为后缀的文件,Windows 95/98操 作系统一般都把ActiveX控件存放在:系统\System\文件夹中(这也是每个 应用程序查找ActiveX控件时默认的文件夹),当然根据需要您也可放在其 他文件夹中,如果是这样,一旦存放ActiveX控件的文件夹名重命名,那您 就必须重新对该ActiveX控件进行注册,否则就会出现“XXX.OCX没有注册” 或“找不到XXX.OCX文件”的提示。(这点您将会在下面的介绍中知道原因) 2.如何注册ActiveX控件: 所谓注册,就是在系统注册表文件System.dat中建立一个主键,在 Windows 9X 中,是建立在“HKEY_CLASSES_ROOT\CLSID\”键下的。 在VFP中注册ActiveX控件一般有以下三种简单的方法: A、通过VFP菜单的选择项进行注册: 进入VFP编辑状态,在VFP系统主 菜单的[工具]-[选项]-[控件]-[ActiveX 控件]-[按添加按钮],选择您要 注册ActiveX控件的文件夹和文件名,VFP就会自动向系统注册表进行注册, 注册完后您只需在控件列表框“ActiveX控件名”前的复选择框里打钩,就 可在表单设计器的[表单控件]-[ActiveX控件]中进行选择使用了。
B.直接运行Regsvr32.EXE系统文件进行注册, 可在VFP的程序中执行:Ren Regsvr32 [PATH]\xxx.ocx 进行注册。
C.利用Regedit.exe注册表编辑器,在编辑器的查找里直接输入 .OCX 文件名进行查找,找到: “HKEY_CLASSES_ROOT\CLSID\{xxxxxxxxxxxxxxxxxxxxxxxxxxx}”主键 后,再利用注册表编辑器菜单上-[注册表]-[导出注册表文件]-然后在文件 选择窗里输入导出的注册表文件名,并在下面的[导出范围]单选框中选[选 择的分支],最后按[保存],这样您就把该控件在注册表中的主键保存在一 个 .REG的注册表文件里了。在需要注册该控件的机器上,您只要直接在这 个 .REG的注册表文件上双击鼠标,系统就会询问您是否要将该.REG文件写 入本机的System.dat文件中,也就是进行注册。 如果选择“是”,那就进 行注册了。 那么我们保存的这个.REG注册表文件里有些什么呢? 下面是MP3PLAY.OCX在导出的.REG注册表文件中的内容: REGEDIT4 [HKEY_CLASSES_ROOT\CLSID\{3B00B10D-6EF0-11D1-A6AA-0020AFE4DE54}] @="Dialog-Medien Mp3Play Control" [HKEY_CLASSES_ROOT\CLSID\{3B00B10D-6EF0-11D1-A6AA-0020AFE4DE54}\ProgID] @="Mp3Play.Mp3PlayCtrl.1" [HKEY_CLASSES_ROOT\CLSID\{3B00B10D-6EF0-11D1-A6AA-0020AFE4DE54}\InprocServer32] @="C:\\MP3\\MP3PLAY.OCX" "ThreadingModel"="Apartment" [HKEY_CLASSES_ROOT\CLSID\{3B00B10D-6EF0-11D1-A6AA-0020AFE4DE54}\ToolboxBitmap32] @="C:\\MP3\\MP3PLAY.OCX, 1" [HKEY_CLASSES_ROOT\CLSID\{3B00B10D-6EF0-11D1-A6AA-0020AFE4DE54}\MiscStatus] @="0" [HKEY_CLASSES_ROOT\CLSID\{3B00B10D-6EF0-11D1-A6AA-0020AFE4DE54}\MiscStatus\1] @="131473" [HKEY_CLASSES_ROOT\CLSID\{3B00B10D-6EF0-11D1-A6AA-0020AFE4DE54}\Control] @="" [HKEY_CLASSES_ROOT\CLSID\{3B00B10D-6EF0-11D1-A6AA-0020AFE4DE54}\TypeLib] @="{3B00B10A-6EF0-11D1-A6AA-0020AFE4DE54}" [HKEY_CLASSES_ROOT\CLSID\{3B00B10D-6EF0-11D1-A6AA-0020AFE4DE54}\Version] @="1.0"
我之所以写这些,是为了让您理解ActiveX控件放在“系统\System\”文件 夹和放在其他文件夹中的区别,我的这个MP3PLAY.OCX是放在C:\MP3\文件夹里的, 所以上面的红色部分就明确标明了ActiveX控件所在的文件夹,也就是:如果这个 文件夹被重新命名后,您必须重新进行注册的原因所在。而如果是放在: “系统\System\”里,那这些红色的部分就是这样写的了: @="MP3PLAY.OCX" @="MP3PLAY.OCX, 1" 可以看出,这里已没有C:\MP3\文件夹名了。
上面的兰色部分就是一个ActiveX控件是不是在注册表文件里进行注册的标志, 也就是该控件在注册表文件里的主键名。
必须注意的是,上面B和C方法的注册,只是在注册表中进行了注册,如果您想 在VFP编辑时象其他控件那样在表单设计器的[表单控件]-[ActiveX控件]中看见它, 则您还必须进行方法A中的绿色部分。
- 希望通过上面这些简单的介绍,当您再遇到“XXX.OCX没有注册,...”时,您
可以找出原因,自行处理了。
利用DOS下的内部或外部命令来完成VFP无法达到的功能
- 这次“心得”继续谈谈编程中的一些小技巧,这些小技巧您可能已经知道,
但我还是希望这些技巧能对您有所帮助:
1.利用DOS下的内部或外部命令来完成VFP无法完成的功能:
A.利用DOS下的ATTRIB.EXE程序来改变文件的属性。我曾介绍过用VFP 的RENAME命令来更改文件属性的方法,其实用DOS下的ATTRIB.EXE更加方便。 ATTRIB.EXE程序是DOS操作系统的外部命令之一,在C:\WINDOWS\COMMAND\ 文件夹里, 用法: RUN /N ATTRIB [+或-] [R S H] 文件名 比如: RUN /N ATTRIB -R -S -H XXX.DAT 把XXX.DAT文件的只读、隐含和系统属性去掉,如果把“-”改为“+”,则作 用相反。
B.利用DOS的内部命令DIR来取得指定磁盘上所有的文件夹和文件名、 磁 盘格式化时建立的磁盘卷标序号、文件夹及文件的长文件名和短文件名、文件 夹和文件的个数等。 用法: RUN DIR [*或?] [/s /...] > 文件名 比如想取得C:盘下的所有文件夹和文件,则: RUN DIR C:\*.* /S > DISK_C.TXT 这样就会在当前目录下建立一个名为DISK_C.TXT的文本文件,文件里包含了上 面提到的所有数据,下面是其中的一部分:
Volume in drive C has no label Volume Serial Number is 3846-11DF Directory of C:\
COMMAND COM 94,282 06-19-98 20:01 COMMAND.COM WZT1 25 10-11-99 23:44 WZT1 WZT2 25 10-11-99 23:44 WZT2 WINDOWS < DIR > 05-04-99 20:56 WINDOWS ......
22 file(s) 234,496 bytes 40 dir(s) 1530,612,224 bytes free
具体的数据格式这里就不介绍了。
C.利用DOS的内部命令DATE来改变系统日期 VFP本身没有办法改变系统的日期或时间,用WIN32 API虽说可以改变, 但十分复杂(在我的[专题文章]栏目里有介绍),如果用 DOS的内部命令: DATE来改变系统日期就比较方便了,方法是: RUN DATE [MM/DD/YY]
比如把系统日期改为2000年10月1日,则 RUN DATE 10/01/2000
如果您需用变量来改日期,则: mDATE='10/01/2000' RUN DATE &mDATE
同理,如果想改变时间则用DOS的内部命令TIME来达到。 类似的命令还有一些,您可自己一试,需要说明的是,利用DOS下的命令, 会出现DOS窗口,您可以建立个快捷方式,将其设为最小化,使用时调用这个快 捷方式,可以以最小化窗口来执行DOS的命令。 也许您发现了,利用DOS的内部 命令时RUN命令后不能带“/N” 。
谈谈VFP中变量的问题
- 最近一直在忙于开发ActiveX控件,加上没有合适的心得可谈,许多
网友希望我能继续把它写下去,看来我还得继续努力。 这次我们谈谈有关内存变量和方法程序作用域的问题。我在网上发现 许多初学编程的网友对此概念比较模糊,或许这个问题对您来说太简单了, 但对初学者来说,不但难理解,而且又找不到能详细这方面知识的资料。 下面就谈谈我对此的理解,供大家参考: 一、变量 我们在有关VFP的书上常常看到的是局部(私有)和全局(共公)变量 的介绍,而对于学过VB的朋友来说除了这两种变量外,可能知道还有个叫窗 口级的变量,那在VFP中是否也有这种变量呢?“有”。在这里我们不妨就叫 “表单级变量”吧(其实就是:容器或对象的属性)。 1.局部(私有)变量: 局部变量,也可以称动态变量,即可以随时定义,其作用域仅限于定义 它的某个过程、方法和事件,对其他过程、方法和事件来讲它并不存在。定 义它的过程、方法和事件执行完毕,该变量自行释放,不占用内存空间,如 果您再次进入还得重新定义和赋值。 2.全局(共公)变量: 全局变量,也可以称静态变量,需用Public命令定义,往往在程序的开 始部分定义,系统会专门开辟一块空间用于全局变量,一旦定义,其作用域 是整个程序的全过程,即您可以在任何过程、方法和事件中对它读取和赋值, 且所赋的值在未被赋新值前一直有效。 它在整个程序未退出前一直占用着内存空间。要释放全局变量,必须用 RELEASE EXTENDED来一次性全部释放,在程序运行中执行了 RELEASE ALL、RELEASE ALL LIKE 或 RELEASE ALL EXCEPT 时并不释放全局 变量。如果用RELEASE XXX 指明具体要释放的全局变量名,可以在名义上释 放,但它所占用的内存空间并未被真正释放,只是您看不到罢了。 3.表单级变量(也就是:容器或对象的属性): 明白了局部和全局变量,再来了解表单级变量就不难理解了,表单级变 量的作用域为本表单本身及表单内所有对象的方法和事件,它随着表单的打 开而建立,表单的释放而释放。 那么什么样的变量是表单级变量呢?说白了就是:所有可以设置和取出 返回值的表单或对象的属性,都属于表单级变量,比如: Caption、Height、Enabled等,也可以说凡是在表单编辑器的属性窗口中能 看到的可读写的容器或对象的属性,就是表单级变量。也许有人会问,这些 属性都是系统定义的,我们自己可以定义吗? 可以,但我们只能定义表单 (FORM1)属性,不能给对象定义属性。 那么如何定义呢? 进入表单编辑器,选择主菜单的[表单]-[新建属性] -在[名称]里填写你要新建的属性名(表单级变量名),如果你用的是VFP3.0 或VFP6.0,你还可以在下面的说明里写上你新建属性 的说明,这样在 Form1 属性窗口中的下方,就可以看到你对该属性的说明。填写完了按添加按钮, 你就定义了一个表单级变量了。不信你到Form1 属性窗口的[其他]里去找, 看看有没有你刚定义的这个属性名,其初始值=.F.。 一旦定义了新的属性,就可以和其他表单属性一样读写了。如果按上面 的办法我们定义了一个名为Bdsx的属性,那你既可以在Form1属性窗口中直接 对它赋值,也可在本表单的任何方法和事件中对它赋值,只是在读写它时必 须加上THIS. 或 THISFORM. 如:
Thisform.Bdsx="hi 网友!您好" thisform.Label1.Caption=thisform.Bdsx
上面讲了这么多有关变量的概念,那么在具体应用中,究竟用哪种变量好 呢?这要看具体情况,但一般的原则是: 带有全局性质的参数,最好用全局变量,比如用于确定登入者身份的参数、 特定的操作目录等。在一个程序中全局变量越少,各过程、类、表单的独立性 和可移植性就越强。 凡是只在某个过程或事件中起中间过渡用的参数,就一律定义为局部变量, 到需要用时再定义。 凡是需在整个表单的各对象间进行读写的参数,就采用表单级变量,这样 你的这个表单今后如果想移植到其他程序中使用,就只需改变一下数据环境了, 无须再全部重新设计表单,也就是说这个表单相对于整个程序,它是个独立的 整体。 二、方法程序 VFP允许我们自定义函数和过程程序,但如果以.PRG文件名定义的函数和过 程程序,必须用 SET PROCEDURE TO ... 来打开和关闭,同时在这种过程程序 中,无法使用诸如THISFORM.LABEL1....等具体的对象名,那在表单中有一段几 个对象的CLICK的事件都需调用的代码,怎样在不重复写这些代码的情况下执行 这段代码呢?我们可以在表单中建立和调用自定义的方法程序吗?又如何调用 它们呢? 1.直接带对象名加事件名调用,比如在COMMAND1.CLICK的事件中,有如下 一段代码,这段代码在COMMAND2.CLICK和COMMAND3.CLICK中也需要执行,则在: COMMAND1.CLICK事件中的代码如果是: IF XXX=1 WAIT WIND '你好!' .... .... .... .... ELSE WAIT WIND '嘿!大家好!' ... ... ... ... ENDI ... ...
那在COMMAND2.CLICK和COMMAND3.CLICK的事件中只需写一句: THISFORM.COMMAND1.CLICK 就都可以调用这同一段代码了,无须重复写这段代码。
2.可以象表单级变量那样直接在表单上建立表单的方法程序: 进入表单编辑器,选择主菜单的[表单]-[新建方法程序]-在[名称]里填写你 要新建的方法程序名,如果你用的是VFP3.0或VFP6.0,你也可以在下面的说明里 写上你新建方法程序的说明,这样在Form1属性窗口中的下方,就可以看到你对 该方法程序的说明。填写完了按添加按钮,这样你就建立的一个新的方法程序, 你可以象FORM1.LOAD等方法那样在里面写入你的代码了,比如我们建立了一个名 为:BDFFCX的方法程序,在这个方法程序里我们把上面的这段代码写入BDFFCX: IF XXX=1 WAIT WIND '你好!' .... .... .... .... ELSE WAIT WIND '嘿!大家好!' ... ... ... ... ENDI ... ...
那你只要在COMMAND1.CLICK、COMMAND2.CLICK和COMMAND3.CLICK的事件 中写上如下一句就可执行这段代码了:
THISFORM.BDFFCX
如果您还想带参数调用,那首先必须在BDFFCX方法程序的第一句加上: Rarameters XXX1,XXX2
把THISFORM.BDFFCX这句改为:
THISFORM.BDFFCX(xxx1,xxx2)
就可以带参数调用BDFFCX了。
表单级方法程序和表单级变量一样,随着表单的打开而建立,表单的释放而 释放。
----关于报表打印
VFP所提供的报表设计器虽说功能强大,并能提供所见所得的报表预览, 但我总觉得没有DOS下直接用代码编制的打印程序来得方便和自由,虽然DOS 没有预览功能。也正因为如此,我很少谈及VFP 的打印问题,为了和大家交 流,这次也谈谈VFP的报表打印问题,希望以此和大家共同探讨。 一、部分与打印有关的系统变量 VFP本身为我们提供了几个与打印有直接关系的系统变量,它们是:(部分) _BOX 是否打印文字边框,.T.=打印 _GETNPD 指定或保存打印机接口驱动程序的文件名。 _PADVANCE 设定打印纸进纸方式,=FORMFEED(默认)整张进纸。 _PAGENO 设定或保存当前的打印页号。 _PBPAGE 设定或返回打印的起始页号。 _PEPAGE 设定或返回打印的终止页号。 _PCOLNO 设定或返回当前打印头的列。 _PLINENO 设定或返回当前打印头的行。 _PCOPIES 设定或返回打印份数。 _PLENGTH 设定或返回打印纸的页长,默认=66行长。 _PPITCH 设定打印机的打印密度。 _PQUALITY 设定打印机的打印质量。 ... ... 这些变量在设计报表程序时,有些是很有用的,故在此列出。
二、一些常用的打印技巧 1.怎样打印指定的页 REPORT FORM XXXX RANGE 2,5 TO PRINTER &&从第2起打至第5页止
2.如何计算总页数,以实现“第?页/总?页” 在打印前根据细节区所打印的记录条数,先进行计算,然后再打印,具体代码: PUBL mPAGE SELE XXX &&xxx=供打印的数据表 XX=10 &&XX=细节区所打印的记录条数 mPAGE=IIF(RECCOUNT()%XX=0,INT(RECCOUNT()/XX),INT(RECCOUNT()/XX)+1)
mPAGE就是总页数,这样在需要总页数的地方就可直接引用mPAGE变量了。
3.如何使报表打满一页 如果打印的记录不足一页,“页注脚”会自动上移,影响报表的美观,解决的 办法和上面的差不多,即补足一页中所缺少的记录(补足空白记录):
SELE XXX &&xxx=供打印的数据表 XX=10 &&XX=一页细节区所打印的记录条数 mI=RECCOUNT()%XX &&取得缺少的记录条数 FOR I=1 to mI APPEND BLANK ENDF
4.报表在设计时明明可以打印,可一安装到其他机器或重装系统后,就会出现 “XXX 带区太大不能放入页中”等提示,而且无法正常退出(尤其是对自定义纸张 大小的程序),这是为什么呢? 我们用报表设计器设计的打印程序,保存退出后,磁盘上就会出现 .frx和.FRT 文件,我们的所有设计均保存在这两个文件中。在VFP中 .frx相当于.DBF表,.FRT 相当于.FPT备注型文件,我们用USE XXX.frx 可以象打开.DBF文件一样打开.frx文 件,在.frx文件中有个Expr备注型字段名,在这个字段名中有如下内容:其中()内是 我所加的译文 ====================================================================== RIVER=winspool DEVICE=Epson LQ-1600K OUTPUT=LPT1: ORIENTATION=0 PAPERSIZE=256 (纸张大小) PAPERLENGTH=1000 (纸张长度) PAPERWIDTH=1600 (纸张宽度) DEFAULTSOURCE=8 (默认来源) PRINTQUALITY=180 (打印质量) COLOR=2 YRESOLUTION=180 TTOPTION=1 ======================================================================
-
从这个Expr备注型字段里可以看出:PAPERSIZE=256 这里的256表示是自定义纸张, 如果: PAPERSIZE=9 为A4、11为A5 具体数据见VFP帮助的Printfo()一节, 而: PAPERLENGTH=1000 (纸张长度) PAPERWIDTH=1600 (纸张宽度) 则分别代表自定义纸张的长度和宽度。 之所以会出现上面提到的问题,是因为系统重 新安装打印机后,WIN系统一般默认的是A4打印纸,与我们设计时保存在.frx文件里的 纸张不符,因而造成这种情况。 那么如何避免出现这个问题呢? 下面是一段检测纸张类型的代码,供您参考: 这段代码必须放在执行report form ... 命令前。
use xxx.frx in 0 ALIAS mPrint &&在空闲工作区以mPrint别名打开xxx.frx文件 x=atcline('PAPERSIZE',mPrint.Expr) &&取得PAPERSIZE在Expr字段中的行 sSIZE=subs(mline(mPrint.Expr,x),11) &&取得设计时保存的纸张类型 mSIZE=allt(str(Prtinfo(2))) &&取得当前打印机默认的纸张类型 x=atcline('PAPERLENGTH',mPrint.Expr) &&取得纸张长度在Expr字段中的行 sLEN=subs(mline(mPrint.Expr,x),13) &&取得纸张长度 x=atcline('PAPERWIDTH',mPrint.Expr) &&取得纸张宽度在Expr字段中的行 sWIDTH=subs(mline(mPrint.Expr,x),12) &&取得纸张宽度 use in 'mPrint' &&关闭xxx.frx文件 if sSIZE=mSIZE &&如果相符,则正常打印 report form xxx.frx to printer else Messagebox('请设定打印机纸张为自定义:长='+sLEN+',宽='+sWIDTH,0+48+0,'提示') report form xxx.frx to printer prompt &&打印前先打开打印机设置对话窗口 endi
5.不让打印的结果显示在屏幕上 report form xxx.frx to printer Noconsole
6.打印或打印预览时,如何使系统打印工具条不出现 系统提供的打印工具条,我们无法检测其各按钮的事件,不能掌握用户当时操 作的情况,那如何不让它出现呢? 首先您得做一个表单(最好是模式表单),用于代替系统的预览窗口(Preview),然后: do form dybd &&打开这个表单 report form xxx.frx windows dybd 这样系统提供的打印工具条就不会出现了。 当然如果自己再做个类似于打印工具条的类,既可掌握按钮事件又美观就更好了, 注:经查VFP3.0可能没有windows子句。
很惭愧,我平时很少用VFP的报表设计器来编制打印程序(用其他语言代替的), 所以能谈的体会很少,十分希望这方面的高手能介绍您的经验,谢谢!
July 12 豆三兄的
在VFP中我们可以用run来调用由VFP自身生成的.exe文件。命令格式: run c:\..\*.exe
当调用非VFP自身生成的.exe文件时,需要加上绝对路径和参数。 一般来说,用VFP运行非VFP自身生成的可执行文件时,会有一个黑色的窗口显示一下。可以通过加上/n来隐藏这个窗口。例如运行Windows下的扫雷游戏,命令格式如下: RUN/n C:\Windows\Winmine.exe 下面运行一个更复杂一点的程序。用word打开c:\下的一个名为mlx.doc的word文件,命令格式如下: run /n3 C:\Program Files\Microsoft Office\Office\Winword.exe C:\mlx.doc 参数:n 在运行时隐藏VFP的黑色窗口。 3 表示以最大化的方式运行程序。1正常方式、2最小化方式、3最大化方式
在这种情况下,虽然可以通过加上参数使程序程序能够流畅运行,但还是要指定绝对路径,而在实际开发时我们常常无法确定所需要启动的应用程序的文件名及其绝对路径。比如,在上例中,我们无法确定用户的word到底安装在什么地方,如果用户把word安装到了别的地方,上述命令便会出错。再如,一个.gif文件,有的用户喜欢用ACD See来打开,有的用户喜欢用IE来打开,有的用户喜欢用豪杰来打开,…… 为了解这种情况我们可以调用Windows的文件关连来打开文件。即所有的文件都用系统默认的程序来打开。这就要调用Windows的Win32 API函数。调用方式如下:
DECLARE INTEGER ShellExecute IN shell32.DLL INTEGER HWND,STRING lpszOP,STRING lpszFile,STRING lpszParams,STRING lpszDir,INTEGER fsshowcmd DECLARE INTEGER GetDesktopWindow IN win32api HWND = GetDesktopWindow() lpszOP = "open" * 指定要打开的文件名 lpszFile ="c:\mlx.doc" lpszParams = "" lpszDir = "c:\temp" fsshowcmd = 1 * 执行ShellExecute命令 LNRETURN = ShellExecute(HWND, lpszOP,lpszFile, lpszParams, lpszDir,fsshowcmd)
可以用任意的文件名来替换上文中的 c:\mlx.doc 。如果指定的文件名Windows无法找到相应的关连程序,这时程序将不会做出反应。有关的参数可以自行偿试进行修改,以达到最佳效果。
------------------------------------------------------------- API函数ShellExecute的使用
ShellExecute 说明 Shellexecute 函数用于对文件执行一个动词(verb). 它通常用于启动一个与特定文件类相关联的应用程序. 例如, 要启动 Word 来读一个 .doc 文件, 或启动 记事本 来编辑一个 .txt 文件. 用于第二个参数中的最常用的动词是 "Open", 但其它可用的动词是 "edit","print","explore" 和 "properties". 有趣的是, 使用 "mailto:" 或 "http://" 前缀, ShellExecute 函数也可用于以一个给定的邮件地址启动默认的邮件阅读器或给定的 URL 启动默认的浏览器.
缩主文件 Shell32.dll
在 VFP 中的定义 DECLARE INTEGER ShellExecute IN "Shell32.dll" INTEGER hwnd,STRING lpVerb,STRING lpFile,STRING lpParameters,STRING lpDirectory,LONG nShowCmd
Visual FoxPro 应用示例 * 打开 Word 来编辑文件 "c:\mywordfile.doc" =Shellexecute(0,"Open","c:\mywordfile.doc","","",1)
* 打开默认的浏览器并定位到天堂论坛 =Shellexecute(0,"Open","http://www.dbwin.net/bbs/index.asp?boardID=1&;page=1","","",1)
* 打开默认的邮件阅读器来发一封信给天堂版主 =Shellexecute(0,"Open","mailto:njjane@21cn.com","","",1)
* 打印文本文件 "c:\mytextfile.txt" =Shellexecute(0,"Print","c:\mytextfile.txt","","",1)
用过《网络蚂蚁》的朋友都知道,在帮助菜单的对话框里作者留下了他的电子邮件地址,单击该邮件地址我们便能给作者发送电子邮件。这种功能看起来有些神秘,实际上只要利用Windows API的ShellExecute函数,便可轻松地实现该功能。
ShellExecute是用来打开特定格式的文件(如WORD文件、EXCEL表格)的函数。该函数存放在Shell32.DLL动态链接库中,通过查阅MSDN,我们能够得到下述帮助:
HINSTANCE ShellExecute(HWND hwnd,LPCTSTR lpOperation,LPCTSTR lpFile,LPCTSTR lpParameters,LPCTSTR lpDirectory,INT nShowCmd);
其调用参数的含义如下:
hwnd:指明打开文件的窗口句柄。
lpOperation:指明操作类型,分别是"open"(打开)、"print"(打印)、"explore"(浏览)。
lpFile:欲打开文件的文件名。这里应该将文件的含义向更深层次理解。文件可以是本地文件,也可以是远程文件;文件的类型可以是文本文件,也可以是多媒体文件。
lpParameters:打开文件时所传递的参数。特别适合打开EXE文件。
lpDirectory:文件所在路径。
nShowCmd:打开文件时窗口的状态。0表示隐藏,1正常方式、2最小化方式、3最大化方式。
如果该函数能够成功打开文件,则该函数的返回值大于32。该函数的返回值能够为你调试程序提供很多有用的信息,具体情况见MSDN内帮助文件。
好,有了上述认识之后,我们便能在VFP的程序开发中利用ShellExecute函数来实现发送电子邮件的功能。新建一表单ABOUTME,在该表单上添加下列控件(见表1)。
需要说明的是,在Label1中的MouseIcon设为一手型光标,其所对应的图标文件在系统内可以随处找到(利用查找文件功能),本文用的是Windows自带h_move.cur文件。针对Label11的各类设置都是为了将该标签打扮得更象"超文本"。
添加完上述控件之后,接下来便是编写控件的CLICK事件代码了。对Label1和Label2的CLICK事件设定相同,其他代码为:
*进行声明操作:
DECLARE INTEGER ShellExecute IN shell32.DLL INTEGER HWND,STRING,STRING lpszFile,STRING,STRING,INTEGER
*向kingdom@126.com邮箱内发送一封主题为"你好"的电子邮件。
在Label1的CLICK事件里,添加以下代码:
ShellExecute(0,"open","mailto:kingdom@126.com?subject=你好",0,0,1)
在Label2的CLICK事件里,将上述语句改为:
ShellExecute(0,"open","http://NationalTax.home.Chinaren.com",0,0,1)
*以最大化窗口方式打开IE,进入作者主页。
确认按钮的CLICK事件代码很简单:thisform.release
运行上述表单,当鼠标移动到电子邮件或是作者主页区域时,鼠标便会变成一只手,点一下即可进行相应的操作:单击Label1,启动Outlook Express;单击Label2,启动IE,表单真有些像一个小型的IE!其实,在VFP的程序开发中,一些看似不可能的事件只要稍微用到一些Windows API,所有的事件一下子都变得简单多了。"山重水复疑无路,柳暗花明又一村",这可能就是编程的乐趣所在吧
Windows API简介:
Application Program Interface,即应用程序编程接口,它是Windows提供给程序员的一系列函数。这些API函数同一般的函数相似,同样具有输入输出参数,并编译到一个独立的文件中,该文件被称为动态链接库。运用API函数可以实现许多复杂和有趣的功能,如重新启动计算机、跟踪当前激活窗体、收发E-mail等。
---------------------------------------- VFP中操作多种文件 做为一种数据处理软件,VFP在使用上的简洁是人所共知的。在使用VFP进行应用程序开以时,有时会涉及到一些非数据处理方面的内容,比如在VFP中打开WORD文档、EXCEL图表、利用OE收发电子邮件,这些功能又恰恰是VFP的弱项。通常情况下,我们处理这些问题的方法是使用OLE(现称之为ActiveX)技术来完成。但是,在VFP中要熟练操作OLE对象,又是一件更麻烦的事情。拿用VFP接入互联网来说,你可以在VFP的表单中加入Microsoft Web浏览器控件,但是如果没有专业的技术文档,你要想顺利地使用浏览器控件,这基本上是不可能的。有没有一种更简单的办法来实现上述功能呢?有的。 VFP提供了在应用程序中调用Windows API函数的功能。如果我们要在VFP程序中使用API函数,只需要在使用前先进行申明,告诉VFP你想调用的函数的基本情况。申明语句的语法如下: DECLARE [cFunctionType] FunctionName IN LibraryName [AS AliasName] [cParamType1 [@] ParamName1, cParamType2 [@] ParamName2,……] 其中各参数的具体含义如下: cFunctionType:被调用函数的返值。它可以是SHORT、INTEGER、SINGLE、DOUBLE、LONG、STRING六个类型之一。学过VC编程的朋友对这些数据类型肯定不会陌生。 FunctionName :调用函数的名称。它是大小写敏感的。 LibraryName:调用函数的所在地。它一般是一DLL文件名。 cParameterType1 [@] ParamName1, cParameterType2 [@] ParamName2, :当对函数的调用属带参调用时,就应该申明参数的类型及名称。 其实,使用VFP操作其他类型的文件,你不妨把这项工作交给系统替你来完成。在SHELL32.DLL动态链接库中,有一ShellExecute函数,它负责打开文件的操作,查阅MSDN,我们能够得到下述帮助: HINSTANCE ShellExecute( HWND hwnd, LPCTSTR lpOperation, LPCTSTR lpFile,LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd); 其调用参数的含义如下: hwnd:指明打开文件的窗口句柄。 lpOperation:指明操作类型,分别是"open"(打开)、"print(打印)"、"explore(浏览)" lpFile:欲打开文件的文件名。这里应该将文件的含义向更深层次理解。文件可以是本地文件,也可以是远程文件。文件的类型可以要文本文件,也可能是多媒体文件。 LpParameters:打开文件时所传递的参数。特别适合对EXE文件的打开。 LpDirectory:文件所在路径。 NShowCmd:打开文件时窗口的状态。0表示隐藏,1表示最大化,2表示最小化。 如果该函数打开文件的操作的成功,则该函数的返回值大于32。该函数的返回值能够为你调试程序提供很多有用的信息,详情请见MSDN内帮助文件。 好,有了上面的认识,我们可以引入正题了。 一、 在VFP中浏览网页。 在VFP6.0提供了超级链接控件,使用该控件能够很简单地进入Internet。但是在VFP5中却没有此类控件,如果你想在VFP5中浏览互联网,只需在程序中添加如下代码: *进行申明操作: DECLARE INTEGER ShellExecute IN shell32.DLL INTEGER HWND,STRING, STRING lpszFile, STRING, STRING, INTEGER *以最大化窗口方式打开IE,并进入在线影院ShellExecute(0,"open","http://www.163film.com",0,0,1) 二、 打开、打印WORD文档 *若要打印文档,则将operate赋值为"print" operate="open" *打开hello.doc文档 ShellExecute(0,operate,"c:\my documents\hello.doc",0,0,1) 三、 带参调用EXE文件 *下列语句将运行记事本程序,并打config.sys进行编辑 ShellExecute(0,"open","notepad.exe","config.sys","c:\",1) 四、 调用资源管理器 *运行资源管理器查看C:盘内容 ShellExecute(0,"explore",0,"c:\",0,1) 五、在VFP中发送电子邮件 *发送一封主题为新年"进步"的信到abcd@efg.com ShellExecute(0,"open","mailto:abcd@efg.com?subject=新年进步 ",0,0,1) 以上便是ShellExecute常见的几种具体用途,怎么样,没想到在VFP中也能够这样容易地操作各种各样的文件吧!试试看,你肯定能够找到ShellExecute更加多的用途。
表单运行时用BINDEVENT()来绑定事件,属性或方法从VFP自己的对象到其它VFP对象
*--------------------------------
列子1:
在一个表单中有n个命令按钮,我想随便单击其中任何一个按钮,此表单关闭,进入另一个表单。 不想一个按钮一个按钮的添加代码,而想用动态方式将对象的CLICK事件与原有或新建的事件或方法绑定。
----------------------------------------------------------------------------------
可以为表单新建一个方法,比如MyMethod,然后写入诸如下面的代码: THISForm.Release DO FORM 表单名
在表单的Init事件中加入以下代码: FOR i = 1 TO THIS.ControlCount IF UPPER(THIS.Controls[i].BaseClass) == "COMMANDBUTTON" = BINDEVENT(THIS.Controls[i], "Click", THIS, "MyMethod") ENDIF ENDFOR
这样应该就可以了。但是,需要注意的是,只有VFP7以上版本才有BINDEVENT()函数。 也完全可以把按钮做成类,然后再向表单中添加若干基于该类的按钮。 不过如果如你所说,按钮是不可视的,那应该怎么都没用了。
列子2: ---------------------------------------------------------------------------------- 动态加载一个控件,如按钮,并给按钮的Click事件绑定事件:
1、建一个过程文件(如MyProc.PRG),内容如下 DEFINE CLASS myhandler AS Session PROCEDURE CmdClick MESSAGEBOX('ok',64,'') &&此处改为相应代码 RETURN ENDPROC ENDDEFINE
2、表单的INIT事件:
SET PROCEDURE TO MyProc.prg
3、表单上动态加载控件按钮的CLICK事件:
THISFORM.ADDOBJECT('Cmd_Test','CommandButton') THISFORM.Cmd_Test.CAPTION='Cmd_Test' THISFORM.Cmd_Test.TOP=100 THISFORM.Cmd_Test.LEFT=100 THISFORM.Cmd_Test.HEIGHT=25 PUBLIC oHandler oHandler=NEWOBJECT("myhandler") BINDEVENT(THISFORM.Cmd_Test,"Click",oHandler,"CmdClick") THISFORM.Cmd_Test.VISIBLE=.T.
----------------------------------------------------------
例子3: *BINDEVENT() 函数。下面是一个简单示例。
PUBLIC oform1
oform1=NEWOBJECT("form1") oform1.SHOW RETURN
DEFINE CLASS form1 AS FORM TOP = 24 LEFT = 158 DOCREATE = .T. CAPTION = "Bindevent 示例" NAME = "Form1"
PROCEDURE _click THISFORM.text1.VALUE=THISFORM.ACTIVECONTROL.NAME &&此处可改为表单文件名 ENDPROC
PROCEDURE INIT LOCAL i,j,cname THISFORM.LOCKSCREEN=.T. FOR i=1 TO 3 FOR j=1 TO 3 cname='cmd'+STR(i,1)+STR(j,1) THISFORM.ADDOBJECT(cname,'commandbutton') WITH THISFORM.&cname. .LEFT=j*100-60 .TOP=i*40 .CAPTION='按钮 &cname.' .HEIGHT=25 .WIDTH=80 .VISIBLE=.T. ENDWITH BINDEVENT(THISFORM.&cname.,'click',THISFORM,'_CLICK') ENDFOR ENDFOR THISFORM.ADDOBJECT('text1','textbox') WITH THISFORM.text1 .LEFT=80 .TOP=160 .HEIGHT=25 .WIDTH=200 .VISIBLE=.T. ENDWITH THISFORM.LOCKSCREEN=.F. ENDPROC
ENDDEFINE
*注意一点:Bindevent引用的代码中慎用“This”引用控件,它指向的还是原方法所在对象。如上例中,如果方法中出现“This”,则,它指的是表单,而不是按钮。
--------------------------------------------------------------- 例子4:
下列代码将Visual FoxPro主窗口的MouseMove事件绑定到自定义类MyHandler的MyMouseMove方法,绑定后,当在主窗口中移动鼠标时,将显示鼠标的坐标位置。
PUBLIC oHandler &&注意,请将保存对象的变量设置为全局变量
oHandler=NEWOBJECT("MyHandler")
*!* 将_SCREEN.MouseMove绑定到oHandler.MyMouseMove
=BINDEVENT(_SCREEN,"MouseMove",oHandler,"MyMouseMove")
DEFINE CLASS MyHandler AS Custom
PROCEDURE MyMouseMove
*!* 要保证该方法与_SCREEN.MouseMove有同样的参数设置
LPARAMETERS nButton, nShift, nXCoord, nYCoord
WAIT WINDOW "鼠标坐标:"+STR(nXCoord)+"|"+STR(nYCoord) NOWAIT
ENDPROC
ENDDEFINE July 10
java中的类是动态加载的,我们先看一下我们常用的类加载方式,先有一个感性的认识,才能进一步 深入讨论,类加载无非就是下面三种方式。 class A{} class B{} class C{} public class Loader{ public static void main(String[] args) throws Exception{ Class aa=A.class; Class bb=Class.forName("B"); Class cc=ClassLoader.getSystemClassLoader().loadClass("C"); } } 我们先看.class字面量方式,很多人可能不知道这种方式,因为这种用法不是一般java语法。 通过javap我们可以发现,这种方式的大致等价于定义了一个静态成员变量 static Class class$0;(后面的编号是增长的) 你可以试图再定义一个 static Class class$0,应该会收到一个编译错误(重复定义)。
Class aa=A.class; 就相当于 if(class$0==null){ try{ Class.forName("A"); } cacth(ClassNotFoundException e){ throw new NoClassDefFoundError(e); } } Class aa=class$0;
可以很清楚的看到,这种类的字面量定义其实不是加载类的方式,而是被编译器处理了,实质 上是使用了Class.forName方法,但是使用这种方式有一个很大的好处就是不用处理异常,因为 编译器处理的时候如果找不到类会抛出一个NoClassDefFoundError。也许你觉得需要处理 ClassNotFoundException这种异常,事实上99%的情况下我们可以把这种异常认为是一个错误。 所以大部分情况我们使用这种方式会更简洁。
最常用的方式就是Class.forName方式了,这也是一个通用的上层调用。这个方法有两个重载, 可能很多人都忽略了第二个方法。 public static Class forName(String name) throws ClassNotFoundException public static Class forName(String name, boolean initialize,ClassLoader loader) throws ClassNotFoundException
第二个方法后面多了两个参数,第二个参数表示是否初始化,第三个参数为指定的类加载器。 在上面的例子中: Class bb=Class.forName("B");等价于 Class bb=Class.forName("B",true,Loader.class.getClassLoader()); 这里要详细说一下这个类的初始化这个参数,如果这个参数为false的话, 类中的static成员不会被初始化,static语句块也不会被执行。 也就是类虽然被加载了,但是没有被初始化,不过在第一次使用时仍然会初始化。 所以我们有时候会看到Class.forName("XXX").newInstance()这样的语句,为什么这里要创建一个 不用的实例呢?不过是为了保证类被初始化(兼容以前的系统)。 其实第二个方法是比较难用的,需要指定类加载器,如果不指定而且又没有安装安全管理器的化, 是无法加载类的,只要看一下具体的实现就明白了。
最本质的方式当然是直接使用ClassLoader加载了,所有的类最终都是通过ClassLoader加载的, Class cc=ClassLoader.getSystemClassLoader().loadClass("C"); 这里通过使用系统类加载器来加载某个类,很直接的方式,但是很遗憾的是通过这种方式加载类, 类是没有被初始化的(也就是初始化被延迟到真正使用的时候).不过我们也可以借鉴上面的经验,加载 后实例化一个对象Class cc=ClassLoader.getSystemClassLoader().loadClass("C").newInstance()。 这里使用了系统类加载器,也是最常用的类加载器,从classpath中寻找要加载的类。 java中默认有三种类加载器:引导类加载器,扩展类加载器,系统类加载器。 java中的类加载有着规范的层次结构,如果我们要了解类加载的过程,需要明确知道哪个类被谁 加载,某个类加载器加载了哪些类等等,就需要深入理解ClassLoader的本质。 以上只是类加载的表面的东西,我们还将讨论深层次的东西。
java中的时间操作不外乎这四种情况:
1、获取当前时间
2、获取某个时间的某种格式
3、设置时间
4、时间的运算
好,下面就针对这三种情况,一个一个搞定。
一、获取当前时间
有两种方式可以获得,第一种,使用Date类。
j2SE的包里有两个Date类,一个是java.sql.Date,一个是java.util.Date
这里,要使用java.util.Date。获取当前时间的代码如下
Date date = new Date();
date.getTime();
还有一种方式,使用System.currentTimeMillis();
这两种方式获得的结果是一样的,都是得到一个当前的时间的long型的时间的毫秒值,这个值实际上是当前时间值与1970年一月一号零时零分零秒相差的毫秒数。
当前的时间得到了,但实际的应用中最后往往不是要用这个long型的东西,用户希望得到的往往是一个时间的字符串,比如“2006年6月18号”,或“2006-06-18”,老外可能希望得到的是“06-18-2006”,诸如此类等等。这就是下一个要解决的问题
二、获取某个时间的某种格式
获取时间的格式,需要用到一个专门用于时间格式的类java.text.SimpleDateFormat。
首先,定义一个SimpleDateFormat变量
SimpleDateFormat sdf = new SimpleDateFormat("",Locale.SIMPLIFIED_CHINESE);
这个构造函数的定义如下:
SimpleDateFormat(String pattern, Locale locale)
第一个参数pattern,我们后面再解释,这里我们使用一个"",第二个参数,是用来设置时区的,这里用到了java.util.Locale这个类,这个类了面定义了很多静态变量,直接拿过来用就OK,我们把时区设置为Locale.SIMPLIFIED_CHINESE,只看名字,这个静态变量的意义已经很清楚了。
接下来我们使用这个SimpleDateFormat把当前时间格式化为一个如下格式的时间字符串“XXXX年XX月XX日_XX时XX分XX秒”,代码:
sdf.applyPattern("yyyy年MM月dd日_HH时mm分ss秒");
String timeStr = sdf.format(new Date());
获取时间格式的函数是format,这个函数的参数是java.util.Date对象,这个没有什么花头。
要说明一下的是这个pattern,所谓的模式。这里,yyyy,MM,dd等,这就是模式。
我们可以在SimpleDateFormat的构造函数中指定模式,比如
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd",Locale.SIMPLIFIED_CHINESE);
也可以获取时间格式的时候使用applyPattern函数临时指定,上面的例子就是这样。
什么字符代表什么,这是j2se约定好的,设置模式的时候,我们可以使用约定好的字符加上任何我们想要的字符串。
j2se对字符所代表的模式的约定列表如下:
| Letter |
Date or Time Component |
Presentation |
| G |
Era designator |
Text |
| y |
Year |
Year |
| M |
Month in year |
Month |
| w |
Week in year |
Number |
| W |
Week in month |
Number |
| D |
Day in year |
Number |
| d |
Day in month |
Number |
| F |
Day of week in month |
Number |
| E |
Day in week |
Text |
| a |
Am/pm marker |
Text |
| H |
Hour in day (0-23) |
Number |
| k |
Hour in day (1-24) |
Number |
| K |
Hour in am/pm (0-11) |
Number |
| h |
Hour in am/pm (1-12) |
Number |
| m |
Minute in hour |
Number |
| s |
Second in minute |
Number |
| S |
Millisecond |
Number |
| z |
Time zone |
General time zone |
| Z |
Time zone |
RFC 822 time zone |
July 09 学生考勤系统已经开发完成了,从6月15号开始开发到7月2号结束并交付使用,历时17天,比自己计划的一周多用了10天,虽然期间有很多天去做其他的事情了,嘿嘿。
本次开发,无论是系统分析、系统设计和编码调试上都是我的一次很大的提高,将先前所学应用实践了。系统的BUG很少了,几乎没有,出现的几个也不影响使用。不过是使用VFP9,没有考虑到VFP6,一旦转到VFP6下编译会出现些版本的错误。 June 29
小结: 课程表管理、课程表预览、和授课地点管理模块已经完成,并且通过了单元测试。
一、考勤和平时成绩管理模块正在进行中,现在将考勤和平时成绩管理模块划分为四小块
1。考勤模块:用于管理学生出勤情况
2。作业记分模块:用于管理学生作业情况
3。平时测验记分模块:用于管理学生平时测验的分数情况
4。期末考试记分模块:用于录入和管理学生期末考试试卷成绩
二、将要进行:学生总评成绩查看和导出模块(最终) June 25 这段时间开发停顿了好几天,今天又接着做,短期的任务是完成[信息导入]模块的制作,
现在的进度在编写提取教务管理系统的数据。 June 22
熟悉SQL SERVER 2000的数据库管理员都知道,其DTS可以进行数据的导入导出,其实,我们也可以使用Transact-SQL语句进行导入导出操作。在Transact-SQL语句中,我们主要使用OpenDataSource函数、OPENROWSET 函数,关于函数的详细说明,请参考SQL联机帮助。利用下述方法,可以十分容易地实现SQL SERVER、ACCESS、EXCEL数据转换,详细说明如下:
一、 SQL SERVER 和ACCESS的数据导入导出
常规的数据导入导出:
使用DTS向导迁移你的Access数据到SQL Server,你可以使用这些步骤:
1在SQL SERVER企业管理器中的Tools(工具)菜单上,选择Data Transformation
2Services(数据转换服务),然后选择 czdImport Data(导入数据)。
3在Choose a Data Source(选择数据源)对话框中选择Microsoft Access as the Source,然后键入你的.mdb数据库(.mdb文件扩展名)的文件名或通过浏览寻找该文件。
4在Choose a Destination(选择目标)对话框中,选择Microsoft OLE DB Prov ider for SQL Server,选择数据库服务器,然后单击必要的验证方式。
5在Specify Table Copy(指定表格复制)或Query(查询)对话框中,单击Copy tables(复制表格)。
6在Select Source Tables(选择源表格)对话框中,单击Select All(全部选定)。下一步,完成。
Transact-SQL语句进行导入导出:
1. 在SQL SERVER里查询access数据:
-- ======================================================
SELECT *
FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="c:DB.mdb";User ID=Admin;Password=')...表名
-------------------------------------------------------------------------------------------------
2. 将access导入SQL server
-- ======================================================
在SQL SERVER 里运行:
SELECT *
INTO newtable
FROM OPENDATASOURCE ('Microsoft.Jet.OLEDB.4.0',
'Data Source="c:DB.mdb";User ID=Admin;Password=' )...表名
-------------------------------------------------------------------------------------------------
3. 将SQL SERVER表里的数据插入到Access表中
-- ======================================================
在SQL SERVER 里运行:
insert into OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source=" c:DB.mdb";User ID=Admin;Password=')...表名
(列名1,列名2)
select 列名1,列名2 from sql表
实例:
insert into OPENROWSET('Microsoft.Jet.OLEDB.4.0',
'C:db.mdb';'admin';', Test)
select id,name from Test
INSERT INTO OPENROWSET('Microsoft.Jet.OLEDB.4.0', 'c: rade.mdb'; 'admin'; ', 表名)
SELECT *
FROM sqltablename
-------------------------------------------------------------------------------------------------
二、 SQL SERVER 和EXCEL的数据导入导出
1、在SQL SERVER里查询Excel数据:
-- ======================================================
SELECT *
FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="c:book1.xls";User ID=Admin;Password=;Extended properties=Excel 5.0')...[Sheet1$]
下面是个查询的示例,它通过用于 Jet 的 OLE DB 提供程序查询 Excel 电子表格。 SELECT * FROM OpenDataSource ( 'Microsoft.Jet.OLEDB.4.0', 'Data Source="c:Financeaccount.xls";User ID=Admin;Password=;Extended properties=Excel 5.0')...xactions
-------------------------------------------------------------------------------------------------
2、将Excel的数据导入SQL server :
-- ======================================================
SELECT * into newtable
FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="c:book1.xls";User ID=Admin;Password=;Extended properties=Excel 5.0')...[Sheet1$]
实例:
SELECT * into newtable
FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="c:Financeaccount.xls";User ID=Admin;Password=;Extended properties=Excel 5.0')...xactions
-------------------------------------------------------------------------------------------------
3、将SQL SERVER中查询到的数据导成一个Excel文件
-- ======================================================
T-SQL代码:
EXEC master..xp_cmdshell 'bcp 库名.dbo.表名out c:Temp.xls -c -q -S"servername" -U"sa" -P""'
参数:S 是SQL服务器名;U是用户;P是密码
说明:还可以导出文本文件等多种格式
实例:EXEC master..xp_cmdshell 'bcp saletesttmp.dbo.CusAccount out c: emp1.xls -c -q -S"pmserver" -U"sa" -P"sa"'
EXEC master..xp_cmdshell 'bcp "SELECT au_fname, au_lname FROM pubs..authors ORDER BY au_lname" queryout C: authors.xls -c -Sservername -Usa -Ppassword'
在VB6中应用ADO导出EXCEL文件代码:
Dim cn As New ADODB.Connection
cn.open "Driver={SQL Server};Server=WEBSVR;DataBase=WebMis;UID=sa;WD=123;"
cn.execute "master..xp_cmdshell 'bcp "SELECT col1, col2 FROM 库名.dbo.表名" queryout E:DT.xls -c -Sservername -Usa -Ppassword'"
-------------------------------------------------------------------------------------------------
4、在SQL SERVER里往Excel插入数据:
-- ======================================================
insert into OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="c:Temp.xls";User ID=Admin;Password=;Extended properties=Excel 5.0')...table1 (A1,A2,A3) values (1,2,3)
T-SQL代码:
INSERT INTO
OPENDATASOURCE('Microsoft.JET.OLEDB.4.0',
'Extended Properties=Excel 8.0;Data source=C: raininginventur.xls')...[Filiale1$]
(bestand, produkt) VALUES (20, 'Test')
-------------------------------------------------------------------------------------------------
总结:利用以上语句,我们可以方便地将SQL SERVER、ACCESS和EXCEL电子表格软件中的数据进行转换,为我们提供了极大方便!
/***************************************************************************** * Name: MD5 * Description: MD5 *****************************************************************************/ CREATE FUNCTION dbo.MD5( @sOrigMess NVARCHAR(4000) ) RETURNS CHAR(32) WITH ENCRYPTION AS BEGIN --==================================== DECLARE @S11 TINYINT DECLARE @S12 TINYINT DECLARE @S13 TINYINT DECLARE @S14 TINYINT DECLARE @S21 TINYINT DECLARE @S22 TINYINT DECLARE @S23 TINYINT DECLARE @S24 TINYINT DECLARE @S31 TINYINT DECLARE @S32 TINYINT DECLARE @S33 TINYINT DECLARE @S34 TINYINT DECLARE @S41 TINYINT DECLARE @S42 TINYINT DECLARE @S43 TINYINT DECLARE @S44 TINYINT
SELECT @S11 = 7, @S12 = 12, @S13 = 17, @S14 = 22 SELECT @S21 = 5, @S22 = 9, @S23 = 14, @S24 = 20 SELECT @S31 = 4, @S32 = 11, @S33 = 16, @S34 = 23 SELECT @S41 = 6, @S42 = 10, @S43 = 15, @S44 = 21 --==================================== DECLARE @a INT DECLARE @b INT DECLARE @c INT DECLARE @d INT DECLARE @AA INT DECLARE @BB INT DECLARE @CC INT DECLARE @DD INT
SELECT @a = 0x67452301 ,@b = 0xEFCDAB89 ,@c = 0x98BADCFE ,@d = 0x10325476 --==================================== DECLARE @sRes VARCHAR(32) SET @sRes = '''''''' DECLARE @iWordArrayLen INT DECLARE @iWordArrayCount INT
DECLARE @tTmp TABLE([ID] INT, [Word] INT) INSERT INTO @tTmp SELECT * FROM dbo.MD5_ConvertToWordArray(@sOrigMess) SELECT @iWordArrayCount=0, @iWordArrayLen = COUNT(*) FROM @tTmp
WHILE(@iWordArrayCount < @iWordArrayLen) BEGIN SELECT @AA = @a, @BB = @b, @CC = @c, @DD = @d
SELECT @a = dbo.MD5_FF(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 0), @S11, 0xD76AA478) SELECT @d = dbo.MD5_FF(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 1), @S12, 0xE8C7B756) SELECT @c = dbo.MD5_FF(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 2), @S13, 0x242070DB) SELECT @b = dbo.MD5_FF(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 3), @S14, 0xC1BDCEEE) SELECT @a = dbo.MD5_FF(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 4), @S11, 0xF57C0FAF) SELECT @d = dbo.MD5_FF(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 5), @S12, 0x4787C62A) SELECT @c = dbo.MD5_FF(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 6), @S13, 0xA8304613) SELECT @b = dbo.MD5_FF(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 7), @S14, 0xFD469501) SELECT @a = dbo.MD5_FF(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 8), @S11, 0x698098D8) SELECT @d = dbo.MD5_FF(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 9), @S12, 0x8B44F7AF) SELECT @c = dbo.MD5_FF(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 10), @S13, 0xFFFF5BB1) SELECT @b = dbo.MD5_FF(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 11), @S14, 0x895CD7BE) SELECT @a = dbo.MD5_FF(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 12), @S11, 0x6B901122) SELECT @d = dbo.MD5_FF(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 13), @S12, 0xFD987193) SELECT @c = dbo.MD5_FF(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 14), @S13, 0xA679438E) SELECT @b = dbo.MD5_FF(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 15), @S14, 0x49B40821)
SELECT @a = dbo.MD5_GG(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 1), @S21, 0xF61E2562) SELECT @d = dbo.MD5_GG(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 6), @S22, 0xC040B340) SELECT @c = dbo.MD5_GG(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 11), @S23, 0x265E5A51) SELECT @b = dbo.MD5_GG(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 0), @S24, 0xE9B6C7AA) SELECT @a = dbo.MD5_GG(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 5), @S21, 0xD62F105D) SELECT @d = dbo.MD5_GG(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 10), @S22, 0x2441453) SELECT @c = dbo.MD5_GG(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 15), @S23, 0xD8A1E681) SELECT @b = dbo.MD5_GG(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 4), @S24, 0xE7D3FBC8) SELECT @a = dbo.MD5_GG(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 9), @S21, 0x21E1CDE6) SELECT @d = dbo.MD5_GG(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 14), @S22, 0xC33707D6) SELECT @c = dbo.MD5_GG(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 3), @S23, 0xF4D50D87) SELECT @b = dbo.MD5_GG(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 8), @S24, 0x455A14ED) SELECT @a = dbo.MD5_GG(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 13), @S21, 0xA9E3E905) SELECT @d = dbo.MD5_GG(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 2), @S22, 0xFCEFA3F8) SELECT @c = dbo.MD5_GG(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 7), @S23, 0x676F02D9) SELECT @b = dbo.MD5_GG(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 12), @S24, 0x8D2A4C8A)
SELECT @a = dbo.MD5_HH(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 5), @S31, 0xFFFA3942) SELECT @d = dbo.MD5_HH(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 8), @S32, 0x8771F681) SELECT @c = dbo.MD5_HH(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 11), @S33, 0x6D9D6122) SELECT @b = dbo.MD5_HH(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 14), @S34, 0xFDE5380C) SELECT @a = dbo.MD5_HH(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 1), @S31, 0xA4BEEA44) SELECT @d = dbo.MD5_HH(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 4), @S32, 0x4BDECFA9) SELECT @c = dbo.MD5_HH(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 7), @S33, 0xF6BB4B60) SELECT @b = dbo.MD5_HH(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 10), @S34, 0xBEBFBC70) SELECT @a = dbo.MD5_HH(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 13), @S31, 0x289B7EC6) SELECT @d = dbo.MD5_HH(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 0), @S32, 0xEAA127FA) SELECT @c = dbo.MD5_HH(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 3), @S33, 0xD4EF3085) SELECT @b = dbo.MD5_HH(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 6), @S34, 0x4881D05) SELECT @a = dbo.MD5_HH(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 9), @S31, 0xD9D4D039) SELECT @d = dbo.MD5_HH(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 12), @S32, 0xE6DB99E5) SELECT @c = dbo.MD5_HH(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 15), @S33, 0x1FA27CF8) SELECT @b = dbo.MD5_HH(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 2), @S34, 0xC4AC5665)
SELECT @a = dbo.MD5_II(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 0), @S41, 0xF4292244) SELECT @d = dbo.MD5_II(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 7), @S42, 0x432AFF97) SELECT @c = dbo.MD5_II(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 14), @S43, 0xAB9423A7) SELECT @b = dbo.MD5_II(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 5), @S44, 0xFC93A039) SELECT @a = dbo.MD5_II(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 12), @S41, 0x655B59C3) SELECT @d = dbo.MD5_II(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 3), @S42, 0x8F0CCC92) SELECT @c = dbo.MD5_II(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 10), @S43, 0xFFEFF47D) SELECT @b = dbo.MD5_II(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 1), @S44, 0x85845DD1) SELECT @a = dbo.MD5_II(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 8), @S41, 0x6FA87E4F) SELECT @d = dbo.MD5_II(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 15), @S42, 0xFE2CE6E0) SELECT @c = dbo.MD5_II(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 6), @S43, 0xA3014314) SELECT @b = dbo.MD5_II(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 13), @S44, 0x4E0811A1) SELECT @a = dbo.MD5_II(@a, @b, @c, @d, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 4), @S41, 0xF7537E82) SELECT @d = dbo.MD5_II(@d, @a, @b, @c, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 11), @S42, 0xBD3AF235) SELECT @c = dbo.MD5_II(@c, @d, @a, @b, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 2), @S43, 0x2AD7D2BB) SELECT @b = dbo.MD5_II(@b, @c, @d, @a, (SELECT [Word] FROM @tTmp WHERE [ID] = @iWordArrayCount + 9), @S44, 0xEB86D391)
SET @a = dbo.MD5_AddUnsigned(@a, @AA) SET @b = dbo.MD5_AddUnsigned(@b, @BB) SET @c = dbo.MD5_AddUnsigned(@c, @CC) SET @d = dbo.MD5_AddUnsigned(@d, @DD)
SET @iWordArrayCount = @iWordArrayCount + 16 END
SET @sRes = dbo.MD5_WordToHex(@a) + dbo.MD5_WordToHex(@b) + dbo.MD5_WordToHex(@c) + dbo.MD5_WordToHex(@d) SET @sRes = LOWER(@sRes) RETURN(@sRes) END GO
--Test set nocount on select dbo.MD5('''''''') as ''''MD5('''''''''''''''')'''' union select ''''d41d8cd98f00b204e9800998ecf8427e''''
select dbo.MD5(''''a'''') as ''''MD5(''''''''a'''''''')'''' union select ''''0cc175b9c0f1b6a831c399e269772661''''
select dbo.MD5(''''abc'''') as ''''MD5(''''''''abc'''''''')'''' union select ''''900150983cd24fb0d6963f7d28e17f72''''
select dbo.MD5(''''message digest'''') as ''''MD5(''''''''message digest'''''''')'''' union select ''''f96b697d7cb7938d525a2f31aaf161d0''''
select dbo.MD5(''''abcdefghijklmnopqrstuvwxyz'''') as ''''MD5(''''''''abcdefghijklmnopqrstuvwxyz'''''''')'''' union select ''''c3fcd3d76192e4007dfb496cca67e13b''''
select dbo.MD5(''''ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'''') as ''''MD5(''''''''ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'''''''')'''' union select ''''d174ab98d277d9f5a5611c2c9f419d9f''''
select dbo.MD5(''''12345678901234567890123456789012345678901234567890123456789012345678901234567890'''') as ''''MD5(''''''''12345678901234567890123456789012345678901234567890123456789012345678901234567890'''''''')'''' union select ''''57edf4a22be3c955ac49da2e2107b67a''''
select dbo.MD5(''''我'''') as ''''MD5(''''''''我'''''''')'''' union select ''''a31d0f25367ebe046897f8a939ca4a9f''''
| MD5算法实现 |
MD5算法实现 1、MD5算法是对输入的数据进行补位,使得如果数据位长度LEN对512求余的结果是448。 即数据扩展至K*512+448位。即K*64+56个字节,K为整数。 具体补位操作:补一个1,然后补0至满足上述要求 2、补数据长度: 用一个64位的数字表示数据的原始长度B,把B用两个32位数表示。这时,数据 就被填 补成长度为512位的倍数。 3. 初始化MD5参数 四个32位整数 (A,B,C,D) 用来计算信息摘要,初始化使用的是十六进制表示 的数字 A=0X01234567 B=0X89abcdef C=0Xfedcba98 D=0X76543210 4、处理位操作函数 X,Y,Z为32位整数。 F(X,Y,Z) = X&Y|NOT(X)&Z G(X,Y,Z) = X&Z|Y¬(Z) H(X,Y,Z) = X xor Y xor Z I(X,Y,Z) = Y xor (X|not(Z)) 5、主要变换过程: 使用常数组T[1 ... 64], T[i]为32位整数用16进制表示,数据用16个32位的 整 数数组M[]表示。 具体过程如下: /* 处理数据原文 */ For i = 0 to N/16-1 do /*每一次,把数据原文存放在16个元素的数组X中. */ For j = 0 to 15 do Set X[j] to M[i*16+j]. end /结束对J的循环 /* Save A as AA, B as BB, C as CC, and D as DD. */ AA = A BB = B CC = C DD = D /* 第1轮*/ /* 以 [abcd k s i]表示如下操作 a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ /* Do the following 16 operations. */ [ABCD 0 7 1] [DABC 1 12 2] [CDAB 2 17 3] [BCDA 3 22 4] [ABCD 4 7 5] [DABC 5 12 6] [CDAB 6 17 7] [BCDA 7 22 8] [ABCD 8 7 9] [DABC 9 12 10] [CDAB 10 17 11] [BCDA 11 22 12] [ABCD 12 7 13] [DABC 13 12 14] [CDAB 14 17 15] [BCDA 15 22 16] /* 第2轮* */ /* 以 [abcd k s i]表示如下操作 a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ /* Do the following 16 operations. */ [ABCD 1 5 17] [DABC 6 9 18] [CDAB 11 14 19] [BCDA 0 20 20] [ABCD 5 5 21] [DABC 10 9 22] [CDAB 15 14 23] [BCDA 4 20 24] [ABCD 9 5 25] [DABC 14 9 26] [CDAB 3 14 27] [BCDA 8 20 28] [ABCD 13 5 29] [DABC 2 9 30] [CDAB 7 14 31] [BCDA 12 20 32] /* 第3轮*/ /* 以 [abcd k s i]表示如下操作 a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ /* Do the following 16 operations. */ [ABCD 5 4 33] [DABC 8 11 34] [CDAB 11 16 35] [BCDA 14 23 36] [ABCD 1 4 37] [DABC 4 11 38] [CDAB 7 16 39] [BCDA 10 23 40] [ABCD 13 4 41] [DABC 0 11 42] [CDAB 3 16 43] [BCDA 6 23 44] [ABCD 9 4 45] [DABC 12 11 46] [CDAB 15 16 47] [BCDA 2 23 48] /* 第4轮*/ /* 以 [abcd k s i]表示如下操作 a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ /* Do the following 16 operations. */ [ABCD 0 6 49] [DABC 7 10 50] [CDAB 14 15 51] [BCDA 5 21 52] [ABCD 12 6 53] [DABC 3 10 54] [CDAB 10 15 55] [BCDA 1 21 56] [ABCD 8 6 57] [DABC 15 10 58] [CDAB 6 15 59] [BCDA 13 21 60] [ABCD 4 6 61] [DABC 11 10 62] [CDAB 2 15 63] [BCDA 9 21 64] /* 然后进行如下操作 */ A = A + AA B = B + BB C = C + CC D = D + DD end /* 结束对I的循环*/ 6、输出结果。
|
/***************************************************************************** * Name: T-SQL MD5算法实现 * Author: Rambo Qian * Create Date: 2003-04-10 * Last Modified by: Rambo Qian * Last Update Date: 2003-04-16
* Version: V1.0.00 *****************************************************************************/ GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_m_OnBits]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_m_OnBits] GO /***************************************************************************** * Name: MD5_m_OnBits * Description: 常数组 *****************************************************************************/ CREATE FUNCTION dbo.MD5_m_OnBits( @i TINYINT ) RETURNS INT WITH ENCRYPTION AS BEGIN DECLARE @iRes INT SELECT @iRes = CASE @i WHEN 0 THEN 1 -- 00000000000000000000000000000001 WHEN 1 THEN 3 -- 00000000000000000000000000000011 WHEN 2 THEN 7 -- 00000000000000000000000000000111 WHEN 3 THEN 15 -- 00000000000000000000000000001111 WHEN 4 THEN 31 -- 00000000000000000000000000011111 WHEN 5 THEN 63 -- 00000000000000000000000000111111 WHEN 6 THEN 127 -- 00000000000000000000000001111111 WHEN 7 THEN 255 -- 00000000000000000000000011111111 WHEN 8 THEN 511 -- 00000000000000000000000111111111 WHEN 9 THEN 1023 -- 00000000000000000000001111111111 WHEN 10 THEN 2047 -- 00000000000000000000011111111111 WHEN 11 THEN 4095 -- 00000000000000000000111111111111 WHEN 12 THEN 8191 -- 00000000000000000001111111111111 WHEN 13 THEN 16383 -- 00000000000000000011111111111111 WHEN 14 THEN 32767 -- 00000000000000000111111111111111 WHEN 15 THEN 65535 -- 00000000000000001111111111111111 WHEN 16 THEN 131071 -- 00000000000000011111111111111111 WHEN 17 THEN 262143 -- 00000000000000111111111111111111 WHEN 18 THEN 524287 -- 00000000000001111111111111111111 WHEN 19 THEN 1048575 -- 00000000000011111111111111111111 WHEN 20 THEN 2097151 -- 00000000000111111111111111111111 WHEN 21 THEN 4194303 -- 00000000001111111111111111111111 WHEN 22 THEN 8388607 -- 00000000011111111111111111111111 WHEN 23 THEN 16777215 -- 00000000111111111111111111111111 WHEN 24 THEN 33554431 -- 00000001111111111111111111111111 WHEN 25 THEN 67108863 -- 00000011111111111111111111111111 WHEN 26 THEN 134217727 -- 00000111111111111111111111111111 WHEN 27 THEN 268435455 -- 00001111111111111111111111111111 WHEN 28 THEN 536870911 -- 00011111111111111111111111111111 WHEN 29 THEN 1073741823 -- 00111111111111111111111111111111 WHEN 30 THEN 2147483647 -- 01111111111111111111111111111111 ELSE 0 END RETURN(@iRes) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_m_2Power]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_m_2Power] GO /*****************************************************************************
* Name: MD5_m_2Power * Description: 常数组 *****************************************************************************/ CREATE FUNCTION dbo.MD5_m_2Power( @i TINYINT ) RETURNS INT WITH ENCRYPTION AS BEGIN DECLARE @iRes INT SELECT @iRes = CASE @i WHEN 0 THEN 1 -- 00000000000000000000000000000001 WHEN 1 THEN 2 -- 00000000000000000000000000000010 WHEN 2 THEN 4 -- 00000000000000000000000000000100 WHEN 3 THEN 8 -- 00000000000000000000000000001000 WHEN 4 THEN 16 -- 00000000000000000000000000010000 WHEN 5 THEN 32 -- 00000000000000000000000000100000 WHEN 6 THEN 64 -- 00000000000000000000000001000000 WHEN 7 THEN 128 -- 00000000000000000000000010000000 WHEN 8 THEN 256 -- 00000000000000000000000100000000 WHEN 9 THEN 512 -- 00000000000000000000001000000000 WHEN 10 THEN 1024 -- 00000000000000000000010000000000 WHEN 11 THEN 2048 -- 00000000000000000000100000000000 WHEN 12 THEN 4096 -- 00000000000000000001000000000000 WHEN 13 THEN 8192 -- 00000000000000000010000000000000 WHEN 14 THEN 16384 -- 00000000000000000100000000000000 WHEN 15 THEN 32768 -- 00000000000000001000000000000000 WHEN 16 THEN 65536 -- 00000000000000010000000000000000 WHEN 17 THEN 131072 -- 00000000000000100000000000000000 WHEN 18 THEN 262144 -- 00000000000001000000000000000000 WHEN 19 THEN 524288 -- 00000000000010000000000000000000 WHEN 20 THEN 1048576 -- 00000000000100000000000000000000 WHEN 21 THEN 2097152 -- 00000000001000000000000000000000 WHEN 22 THEN 4194304 -- 00000000010000000000000000000000 WHEN 23 THEN 8388608 -- 00000000100000000000000000000000 WHEN 24 THEN 16777216 -- 00000001000000000000000000000000 WHEN 25 THEN 33554432 -- 00000010000000000000000000000000 WHEN 26 THEN 67108864 -- 00000100000000000000000000000000 WHEN 27 THEN 134217728 -- 00001000000000000000000000000000 WHEN 28 THEN 268435456 -- 00010000000000000000000000000000 WHEN 29 THEN 536870912 -- 00100000000000000000000000000000 WHEN 30 THEN 1073741824 -- 01000000000000000000000000000000 ELSE 0 END RETURN(@iRes) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_LShift]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_LShift] GO /***************************************************************************** * Name: MD5_LShift * Description: MD5_LShift *****************************************************************************/ CREATE FUNCTION dbo.MD5_LShift( @iValue INT ,@iShiftBits TINYINT ) RETURNS INT WITH ENCRYPTION AS BEGIN DECLARE @iRes BIGINT SET @iRes = CAST(@iValue AS BINARY(8)) SET @iRes = @iRes * dbo.MD5_m_2Power(@iShiftBits) RETURN(CAST(@iRes & 0x00000000FFFFFFFF AS BINARY(4))) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_RShift]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_RShift] GO /***************************************************************************** * Name: MD5_RShift * Description: MD5_RShift *****************************************************************************/ CREATE FUNCTION dbo.MD5_RShift( @iValue INT ,@iShiftBits TINYINT ) RETURNS INT WITH ENCRYPTION AS BEGIN DECLARE @iRes BIGINT SET @iRes = CAST(@iValue AS BINARY(8)) SET @iRes = @iRes / dbo.MD5_m_2Power(@iShiftBits) RETURN(CAST(@iRes & 0x00000000FFFFFFFF AS BINARY(4))) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_RotateLeft]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_RotateLeft] GO /***************************************************************************** * Name: MD5_RotateLeft * Description: MD5_RotateLeft *****************************************************************************/ CREATE FUNCTION dbo.MD5_RotateLeft( @iValue INT ,@iShiftBits TINYINT ) RETURNS INT WITH ENCRYPTION AS BEGIN RETURN(dbo.MD5_LShift(@iValue, @iShiftBits) | dbo.MD5_RShift(@iValue, (32 - @iShiftBits))) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_AddUnsigned]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_AddUnsigned] GO /***************************************************************************** * Name: MD5_AddUnsigned * Description: MD5_AddUnsigned *****************************************************************************/ CREATE FUNCTION dbo.MD5_AddUnsigned( @iX INT ,@iY INT ) RETURNS INT WITH ENCRYPTION AS BEGIN DECLARE @iRes BIGINT SET @iRes = CAST(CAST(@iX AS BINARY(8)) AS BIGINT) + CAST(CAST(@iY AS BINARY(8)) AS BIGINT) RETURN(CAST(@iRes & 0x00000000FFFFFFFF AS BINARY(4))) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_F]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_F] GO /***************************************************************************** * Name: MD5_F * Description: MD5_F *****************************************************************************/ CREATE FUNCTION dbo.MD5_F( @x INT ,@y INT ,@z INT ) RETURNS INT WITH ENCRYPTION AS BEGIN RETURN((@x & @y) | ((~@x) & @z)) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_G]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_G] GO /***************************************************************************** * Name: MD5_G * Description: MD5_G *****************************************************************************/ CREATE FUNCTION dbo.MD5_G( @x INT ,@y INT ,@z INT ) RETURNS INT WITH ENCRYPTION AS BEGIN RETURN((@x & @z) | (@y & (~@z))) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_H]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_H] GO /***************************************************************************** * Name: MD5_H * Description: MD5_H *****************************************************************************/ CREATE FUNCTION dbo.MD5_H( @x INT ,@y INT ,@z INT ) RETURNS INT WITH ENCRYPTION AS BEGIN RETURN(@x ^ @y ^ @z) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_I]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_I] GO /***************************************************************************** * Name: MD5_I * Description: MD5_I *****************************************************************************/ CREATE FUNCTION dbo.MD5_I( @x INT ,@y INT ,@z INT ) RETURNS INT WITH ENCRYPTION AS BEGIN RETURN(@y ^ (@x | (~@z))) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_FF]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_FF] GO /***************************************************************************** * Name: MD5_FF * Description: MD5_FF *****************************************************************************/ CREATE FUNCTION dbo.MD5_FF( @a INT ,@b INT ,@c INT ,@d INT ,@x INT ,@s INT ,@ac INT ) RETURNS INT WITH ENCRYPTION AS BEGIN SET @a = dbo.MD5_AddUnsigned(@a, dbo.MD5_AddUnsigned(dbo.MD5_AddUnsigned(dbo.MD5_F(@b, @c, @d), @x), @ac)) SET @a = dbo.MD5_RotateLeft(@a, @s) SET @a = dbo.MD5_AddUnsigned(@a, @b) RETURN(@a) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_GG]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_GG] GO /***************************************************************************** * Name: MD5_GG * Description: MD5_GG *****************************************************************************/ CREATE FUNCTION dbo.MD5_GG( @a INT ,@b INT ,@c INT ,@d INT ,@x INT ,@s INT ,@ac INT ) RETURNS INT WITH ENCRYPTION AS BEGIN SET @a = dbo.MD5_AddUnsigned(@a, dbo.MD5_AddUnsigned(dbo.MD5_AddUnsigned(dbo.MD5_G(@b, @c, @d), @x), @ac)) SET @a = dbo.MD5_RotateLeft(@a, @s) SET @a = dbo.MD5_AddUnsigned(@a, @b) RETURN(@a) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_HH]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_HH] GO /***************************************************************************** * Name: MD5_HH * Description: MD5_HH *****************************************************************************/ CREATE FUNCTION dbo.MD5_HH( @a INT ,@b INT ,@c INT ,@d INT ,@x INT ,@s INT ,@ac INT ) RETURNS INT WITH ENCRYPTION AS BEGIN SET @a = dbo.MD5_AddUnsigned(@a, dbo.MD5_AddUnsigned(dbo.MD5_AddUnsigned(dbo.MD5_H(@b, @c, @d), @x), @ac)) SET @a = dbo.MD5_RotateLeft(@a, @s) SET @a = dbo.MD5_AddUnsigned(@a, @b) RETURN(@a) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_II]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_II] GO /***************************************************************************** * Name: MD5_II * Description: MD5_II *****************************************************************************/ CREATE FUNCTION dbo.MD5_II( @a INT ,@b INT ,@c INT ,@d INT ,@x INT ,@s INT ,@ac INT ) RETURNS INT WITH ENCRYPTION AS BEGIN SET @a = dbo.MD5_AddUnsigned(@a, dbo.MD5_AddUnsigned(dbo.MD5_AddUnsigned(dbo.MD5_I(@b, @c, @d), @x), @ac)) SET @a = dbo.MD5_RotateLeft(@a, @s) SET @a = dbo.MD5_AddUnsigned(@a, @b) RETURN(@a) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_ConvertToWordArray]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_ConvertToWordArray] GO /***************************************************************************** * Name: MD5_ConvertToWordArray * Description: MD5_ConvertToWordArray *****************************************************************************/ CREATE FUNCTION dbo.MD5_ConvertToWordArray( @sOrigMess VARCHAR(8000) = '''''''' ) RETURNS @tWordArray TABLE([ID] INT IDENTITY(0,1),[Word] INT) WITH ENCRYPTION AS BEGIN IF @sOrigMess IS NULL SET @sOrigMess = ''''''''
DECLARE @iLenOfMess INT DECLARE @iWordArrayLen INT DECLARE @iPosOfWord INT DECLARE @iPosOfMess INT DECLARE @iCountOfWord INT
SET @iLenOfMess = LEN(@sOrigMess) SET @iWordArrayLen = ((@iLenOfMess + 8)/64 + 1) * 16 SET @iCountOfWord = 0 WHILE(@iCountOfWord<@iWordArrayLen) BEGIN INSERT INTO @tWordArray([Word]) VALUES(0) SET @iCountOfWord = @iCountOfWord + 1 END
SELECT @iPosOfMess = 0, @iPosOfWord = 0, @iCountOfWord = 0 WHILE(@iPosOfMess < @iLenOfMess) BEGIN SELECT @iCountOfWord = @iPosOfMess / 4, @iPosOfWord = @iPosOfMess % 4 UPDATE @tWordArray SET [Word] = [Word] | dbo.MD5_LShift(UNICODE(SUBSTRING(@sOrigMess,@iPosOfMess+1,1)),@iPosOfWord*8) WHERE [ID] = @iCountOfWord SET @iPosOfMess = @iPosOfMess + 1 END SELECT @iCountOfWord = @iPosOfMess / 4, @iPosOfWord = @iPosOfMess % 4 UPDATE @tWordArray SET [Word] = [Word] | dbo.MD5_LShift(0x80,@iPosOfWord*8) WHERE [ID] = @iCountOfWord
UPDATE @tWordArray SET [Word] = [Word] | dbo.MD5_LShift(@iLenOfMess,3) WHERE [ID] = @iWordArrayLen - 2 UPDATE @tWordArray SET [Word] = [Word] | dbo.MD5_RShift(@iLenOfMess,29) WHERE [ID] = @iWordArrayLen - 1 RETURN END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5_WordToHex]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5_WordToHex] GO /***************************************************************************** * Name: MD5_WordToHex * Description: MD5_WordToHex *****************************************************************************/ CREATE FUNCTION dbo.MD5_WordToHex( @iValue INT ) RETURNS CHAR(8) WITH ENCRYPTION AS BEGIN DECLARE @sRes VARCHAR(8) DECLARE @iTmp INT DECLARE @iCount TINYINT
SELECT @sRes = '''''''', @iCount = 0 WHILE(@iCount<4) BEGIN SET @iTmp = dbo.MD5_RShift(@iValue,@iCount*8) & 0x000000FF SET @sRes = @sRes + CASE @iTmp / 16 WHEN 0 THEN ''''0'''' WHEN 1 THEN ''''1'''' WHEN 2 THEN ''''2'''' WHEN 3 THEN ''''3'''' WHEN 4 THEN ''''4'''' WHEN 5 THEN ''''5'''' WHEN 6 THEN ''''6'''' WHEN 7 THEN ''''7'''' WHEN 8 THEN ''''8'''' WHEN 9 THEN ''''9'''' WHEN 10 THEN ''''A'''' WHEN 11 THEN ''''B'''' WHEN 12 THEN ''''C'''' WHEN 13 THEN ''''D'''' WHEN 14 THEN ''''E'''' WHEN 15 THEN ''''F'''' ELSE '''''''' END + CASE @iTmp % 16 WHEN 0 THEN ''''0'''' WHEN 1 THEN ''''1'''' WHEN 2 THEN ''''2'''' WHEN 3 THEN ''''3'''' WHEN 4 THEN ''''4'''' WHEN 5 THEN ''''5'''' WHEN 6 THEN ''''6'''' WHEN 7 THEN ''''7'''' WHEN 8 THEN ''''8'''' WHEN 9 THEN ''''9'''' WHEN 10 THEN ''''A'''' WHEN 11 THEN ''''B'''' WHEN 12 THEN ''''C'''' WHEN 13 THEN ''''D'''' WHEN 14 THEN ''''E'''' WHEN 15 THEN ''''F'''' ELSE '''''''' END SET @iCount = @iCount + 1 END RETURN(@sRes) END GO
IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''''[dbo].[MD5]'''') AND xtype IN(N''''FN'''', N''''IF'''', N''''TF'''')) DROP FUNCTION [dbo].[MD5] GO /***************************************************************************** * Name: MD5 * Description: MD5 *****************************************************************************/ CREATE FUNCTION dbo.MD5( @sOrigMess NVARCHAR(4000) ) RETURNS CHAR(32) WITH ENCRYPTION AS BEGIN --==================================== DECLARE @S11 TINYINT DECLARE @S12 TINYINT DECLARE @S13 TINYINT DECLARE @S14 TINYINT DECLARE @S21 TINYINT DECLARE @S22 TINYINT DECLARE @S23 TINYINT DECLARE @S24 TINYINT DECLARE @S31 TINYINT DECLARE @S32 TINYINT DECLARE @S33 TINYINT DECLARE @S34 TINYINT DECLARE @S41 TINYINT DECLARE @S42 TINYINT DECLARE @S43 TINYINT DECLARE @S44 TINYINT
SELECT @S11 = 7, @S12 = 12, @S13 = 17, @S14 = 22 SELECT @S21 = 5, @S22 = 9, @S23 = 14, @S24 = 20 SELECT @S31 = 4, @S32 = 11, @S33 = 16, @S34 = 23 SELECT @S41 = 6, @S42 = 10, @S43 = 15, @S44 = 21 --==================================== DECLARE @a INT DECLARE @b INT DECLARE @c INT DECLARE @d INT DECLARE @AA INT DECLARE @BB INT DECLARE @CC INT DECLARE @DD INT
SELECT @a = 0x67452301 ,@b = 0xEFCDAB89 ,@c = 0x98BADCFE ,@d = 0x10325476 --====================================
数据库是电子商务、金融以及ERP系统的基础,通常都保存着重要的商业伙伴和客户信息。大多数企业、组织以及政府部门的电子数据都保存在各种数据库中,他们用这些数据库保存一些个人资料,比如员工薪水、个人资料等等。数据库服务器还掌握着敏感的金融数据。包括交易记录、商业事务和帐号数据,战略上的或者专业的信息,比如专利和工程数据,甚至市场计划等等应该保护起来防止竞争者和其他非法者获取的资料。数据完整性和合法存取会受到很多方面的安全威胁,包括密码策略、系统后门、数据库操作以及本身的安全方案。但是数据库通常没有象操作系统和网络这样在安全性上受到重视。
微软的SQL Server是一种广泛使用的数据库,很多电子商务网站、企业内部信息化平台等都是基于SQL Server上的,但是数据库的安全性还没有被人们更系统的安全性等同起来,多数管理员认为只要把网络和操作系统的安全搞好了,那么所有的应用程序也就安全了。大多数系统管理员对数据库不熟悉而数据库管理员有对安全问题关心太少,而且一些安全公司也忽略数据库安全,这就使数据库的安全问题更加严峻了。数据库系统中存在的安全漏洞和不当的配置通常会造成严重的后果,而且都难以发现。数据库应用程序通常同操作系统的最高管理员密切相关。广泛SQL Server数据库又是属于“端口”型的数据库,这就表示任何人都能够用分析工具试图连接到数据库上,从而绕过操作系统的安全机制,进而闯入系统、破坏和窃取数据资料,甚至破坏整个系统。
这里,我们主要谈论有关SQL Server2000数据库的安全配置以及一些相关的安全和使用上的问题。
在进行SQL Server 2000数据库的安全配置之前,首先你必须对操作系统进行安全配置,保证你的操作系统处于安全状态。然后对你要使用的操作数据库软件(程序)进行必要的安全审核,比如对ASP、PHP等脚本,这是很多基于数据库的WEB应用常出现的安全隐患,对于脚本主要是一个过滤问题,需要过滤一些类似 , ‘ ; @ / 等字符,防止破坏者构造恶意的SQL语句。接着,安装SQL Server2000后请打上补丁sp1以及最新的sp2。
在做完上面三步基础之后,我们再来讨论SQL Server的安全配置。
1、使用安全的密码策略
我们把密码策略摆在所有安全配置的第一步,请注意,很多数据库帐号的密码过于简单,这跟系统密码过于简单是一个道理。对于sa更应该注意,同时不要让sa帐号的密码写于应用程序或者脚本中。健壮的密码是安全的第一步!
SQL Server2000安装的时候,如果是使用混合模式,那么就需要输入sa的密码,除非你确认必须使用空密码。这比以前的版本有所改进。
同时养成定期修改密码的好习惯。数据库管理员应该定期查看是否有不符合密码要求的帐号。比如使用下面的SQL语句:
Use master
Select name,Password from syslogins where password is null
2、使用安全的帐号策略。
由于SQL Server不能更改sa用户名称,也不能删除这个超级用户,所以,我们必须对这个帐号进行最强的保护,当然,包括使用一个非常强壮的密码,最好不要在数据库应用中使用sa帐号,只有当没有其它方法登录到 SQL Server 实例(例如,当其它系统管理员不可用或忘记了密码)时才使用 sa。建议数据库管理员新建立一个拥有与sa一样权限的超级用户来管理数据库。安全的帐号策略还包括不要让管理员权限的帐号泛滥。 SQL Server的认证模式有Windows身份认证和混合身份认证两种。如果数据库管理员不希望操作系统管理员来通过操作系统登陆来接触数据库的话,可以在帐号管理中把系统帐号“BUILTINAdministrators”删除。不过这样做的结果是一旦sa帐号忘记密码的话,就没有办法来恢复了。
很多主机使用数据库应用只是用来做查询、修改等简单功能的,请根据实际需要分配帐号,并赋予仅仅能够满足应用要求和需要的权限。比如,只要查询功能的,那么就使用一个简单的public帐号能够select就可以了。
3、加强数据库日志的记录。
审核数据库登录事件的“失败和成功”,在实例属性中选择“安全性”,将其中的审核级别选定为全部,这样在数据库系统和操作系统日志里面,就详细记录了所有帐号的登录事件。
请定期查看SQL Server日志检查是否有可疑的登录事件发生,或者使用DOS命令。
findstr /C:"登录" d:Microsoft SQL ServerMSSQLLOG*.*
4、管理扩展存储过程
对存储过程进行大手术,并且对帐号调用扩展存储过程的权限要慎重。其实在多数应用中根本用不到多少系统的存储过程,而SQL Server的这么多系统存储过程只是用来适应广大用户需求的,所以请删除不必要的存储过程,因为有些系统的存储过程能很容易地被人利用起来提升权限或进行破坏。
如果你不需要扩展存储过程xp_cmdshell请把它去掉。使用这个SQL语句:
use master
sp_dropextendedproc 'xp_cmdshell'
xp_cmdshell是进入操作系统的最佳捷径,是数据库留给操作系统的一个大后门。如果你需要这个存储过程,请用这个语句也可以恢复过来。
sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll'
如果你不需要请丢弃OLE自动存储过程(会造成管理器中的某些特征不能使用),这些过程包括如下:
Sp_OACreate Sp_OADestroy Sp_OAGetErrorInfo Sp_OAGetProperty
Sp_OAMethod Sp_OASetProperty Sp_OAStop
去掉不需要的注册表访问的存储过程,注册表存储过程甚至能够读出操作系统管理员的密码来,如下:
Xp_regaddmultistring Xp_regdeletekey Xp_regdeletevalue Xp_regenumvalues
Xp_regread Xp_regremovemultistring Xp_regwrite
还有一些其他的扩展存储过程,你也最好检查检查。
使用系统帐户登陆查询分析器 运行以下脚本 use master exec sp_dropextendedproc 'xp_cmdshell' exec sp_dropextendedproc 'xp_dirtree' exec sp_dropextendedproc 'xp_enumgroups' exec sp_dropextendedproc 'xp_fixeddrives' exec sp_dropextendedproc 'xp_loginconfig' exec sp_dropextendedproc 'xp_enumerrorlogs' exec sp_dropextendedproc 'xp_getfiledetails' exec sp_dropextendedproc 'Sp_OACreate' exec sp_dropextendedproc 'Sp_OADestroy' exec sp_dropextendedproc 'Sp_OAGetErrorInfo' exec sp_dropextendedproc 'Sp_OAGetProperty' exec sp_dropextendedproc 'Sp_OAMethod' exec sp_dropextendedproc 'Sp_OASetProperty' exec sp_dropextendedproc 'Sp_OAStop' exec sp_dropextendedproc 'Xp_regaddmultistring' exec sp_dropextendedproc 'Xp_regdeletekey' exec sp_dropextendedproc 'Xp_regdeletevalue' exec sp_dropextendedproc 'Xp_regenumvalues' exec sp_dropextendedproc 'Xp_regread' exec sp_dropextendedproc 'Xp_regremovemultistring' exec sp_dropextendedproc 'Xp_regwrite' drop procedure sp_makewebtask go 删除所有危险的扩展.
在处理存储过程的时候,请确认一下,避免造成对数据库或应用程序的伤害。
5、使用协议加密
SQL Server 2000使用的Tabular Data Stream协议来进行网络数据交换,如果不加密的话,所有的网络传输都是明文的,包括密码、数据库内容等等,这是一个很大的安全威胁。能被人在网络中截获到他们需要的东西,包括数据库帐号和密码。所以,在条件容许情况下,最好使用SSL来加密协议,当然,你需要一个证书来支持。
6、不要让人随便探测到你的TCP/IP端口
默认情况下,SQL Server使用1433端口监听,很多人都说SQL Server配置的时候要把这个端口改变,这样别人就不能很容易地知道使用的什么端口了。可惜,通过微软未公开的1434端口的UDP探测可以很容易知道SQL Server使用的什么TCP/IP端口了(请参考《深入探索SQL Server网络连接的安全问题》)。 不过微软还是考虑到了这个问题,毕竟公开而且开放的端口会引起不必要的麻烦。在实例属性中选择TCP/IP协议的属性。选择隐藏 SQL Server 实例。如果隐藏了 SQL Server 实例,则将禁止对试图枚举网络上现有的 SQL Server 实例的客户端所发出的广播作出响应。这样,别人就不能用1434来探测你的TCP/IP端口了(除非用Port Scan)。
7、修改TCP/IP使用的端口
请在上一步配置的基础上,更改原默认的1433端口。在实例属性中选择网络配置中的TCP/IP协议的属性,将TCP/IP使用的默认端口变为其他端口。
8、拒绝来自1434端口的探测
由于1434端口探测没有限制,能够被别人探测到一些数据库信息,而且还可能遭到DOS攻击让数据库服务器的CPU负荷增大,所以对Windows 2000操作系统来说,在IPSec过滤拒绝掉1434端口的UDP通讯,可以尽可能地隐藏你的SQL Server。
9、对网络连接进行IP限制
SQL Server 2000数据库系统本身没有提供网络连接的安全解决办法,但是Windows 2000提供了这样的安全机制。使用操作系统自己的IPSec可以实现IP数据包的安全性。请对IP连接进行限制,只保证自己的IP能够访问,也拒绝其他IP进行的端口连接,把来自网络上的安全威胁进行有效的控制。
上面主要介绍的一些SQL Server的安全配置,经过以上的配置,可以让SQL Server本身具备足够的安全防范能力。当然,更主要的还是要加强内部的安全控制和管理员的安全培训,而且安全性问题是一个长期的解决过程,还需要以后进行更多的安全维护。
|