蛙蛙推荐:c#编写网络电话

蛙蛙推荐:c#编写网络电话
摘要:语音通话已经是IM的基本功能了,qq,MSN甚至连刚出来的百度HI都自带语音聊天的功能,大家可能觉得很炫,其实大家都是用的windows平台上的API,懂了原理之后自己也可以做,再说了微软也提供了DirectSound的托管互操作程序集,使.net开发人员也很容易的介入到这个领域,甚至你还可以写一个能跑在window mobile上的语音电话,现在好多手机都支持wifi,这样一个简单的wifi电话就由你的手里诞生了。本帖来和大家一起看看如何来做网络电话。

思路:要想做一个网络电话,基本遵循以下步骤
1、一方实时的录音,把模拟信号转换成数字信号;
2、把声音实时压缩;
3、通过网络协议把压缩后的数据传输给接收方;
4、接收方解压缩接受到的音频数据;
5、实时的把接收到的数字信号转换成模拟信号并播放出来。

下面我们来看看每一步面临的挑战及其解决方案。
1、第一步,实时录音,DirectxSound有录音方面的API,托管的类分别是Microsoft.DirectX.DirectSound.CaptureDevicesCollection,Microsoft.DirectX.DirectSound.Capture和Microsoft.DirectX.DirectSound.CaptureBuffer,CaptureDevicesCollection用来枚举本机的可用的录音设备,Capture则表示一个录音设备,CaptureBuffer是用来存放录音数据的缓冲区,我们开始录音后,音频数据会不断的写入到环形的流式缓冲区,然后我们定期从缓冲区中把录音数据取出来返回给上层应用层就可以了。关于环形的流式缓冲区,可以看参考链接部分。
2、声音的压缩是一个很难抉择的步骤,默认的DirectSound只能播放和录制PCM格式(WAV)的音频数据,但这种声音格式特别大。常用的声音压缩格式有h.7231,gsm,amr,h.711等等,各种压缩算法都有自己的码率和适用范围。因为我们做的是互联网的语音电话,不考虑慢速网络和无线连接下的情况,也不用考虑终端设备的CPU能不能支持我们选用的压缩算法,我们做的语音电话双方都是PC机,应该什么解压缩算法都不会引起什么性能上的问题,所以只要网络快一些,选择哪个压缩算法都无所谓了,网上有h.711的压缩算法,我打算就采用这个,他的码率是64Kbps,比PCM的1.544Mbps和2.048Mbps要小的多。然后我们进行了音频数据压缩后,还可以对字节流进行GZIP或者7ZIP压缩,前者用SharpZip,后者7zip的官方有c#的使用代码,大家可以测试一下这两个算法的性能后做出适合自己的决定。关于各种压缩格式的特性可以参考我做的PPT及提供的参考链接。
3、网络电话注重实时性,而把声音从网络上传输就要走IP网络,而IP网络不是一个等时系统,所以我们就要尽量的去模拟实时的语音传输,提到实时,肯定UDP比TCP要实时,因为TCP要保证传输的可靠性,有序性等,而专门用于实时传输有一个应用层协议是RTP协议,这个协议一般就是建立在UDP基础上的,它在每个包头提供了一些序列号、时间戳等信息,但UDP本身并不会使用这些信息,这时候就有一个RTCP协议来用这些信息进行流量控制和拥塞控制,比如说RTCP检测到网络拥挤,会告诉发送方变换一种低码率的语音压缩算法来传输数据。这些大多都需要自己去实现,本文的源码没有去实现这些,关于RTP和RTCP可以参考相关资料或者我做的PPT。
4、每个压缩算法都有相应的解压缩算法,呵呵。
5、播放声音肯定也需要用到DS,也需要用到StreamBuffer,大致流程如下
1)创建一个声音设备Microsoft.DirectX.DirectSound.Device dev = new Microsoft.DirectX.DirectSound.Device();
2)设置协调级别dev.SetCooperativeLevel(this, Microsoft.DirectX.DirectSound.CooperativeLevel.Normal);
3)创建声音格式、缓冲区描述、及辅助缓冲区;
4)给辅助缓冲区设定通知;
5)用声音数据填满缓冲区;
6)播放缓冲区的声音数据,播放到一定的通知点,通知填充线程,填充新的声音数据;
7)循环第6步,直到没有新的声音数据填充到缓冲区。

具体的过程参考PPT或者具体代码。


版权声明:
附件源代码里的CaptureSound,SoundPlayer和CircularBuffer类反编译自随意桌面的代码(注释是我加的),版权归作者所有。
PPT里的图片和一些文字选自一个叫做ch11-DxSound&Input2.ppt的文件,源链接已丢失,还有一些选择一个叫做“SIP之 穿越NAT.ppt”的文件,网上可以搜索到,版权均归原作者所有,源作者要是再引用别人的东西,我就不知道了。

下面看一些具体的代码

用户创建声音格式

 

public class DirectSoundManager
{
    
public static WaveFormat CreateWaveFormat(int hz, short bits, short channels)
    
{
        WaveFormat format 
= new WaveFormat();
        
//声音的格式,通常使用WAVE_FORMAT_PCM来设定,
        
//因为PCM是比较常用的声音格式。
        format.FormatTag = WaveFormatTag.Pcm;
        
//采样率(单位:赫兹)典型值:11025、22050、44100Hz
        format.SamplesPerSecond = hz;
        
//每个采样点数;8-bit或16-bit;
        format.BitsPerSample = bits;
        
//声道的设置,当其值为1时是单声道,为2时是双声道;
        format.Channels = channels;
        
//每个采样点字节数
        format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8));
        
//平均传输率,每秒的数据流量
        format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond;
        
return format;
    }


    
属性
}


用于播放流式声音
   

 

public class SoundPlayer : IDisposable
{
    
私有成员

    
构造函数

    
公开属性

    
IDisposable Members

    
私有方法

    
公开方法
}


 

用户录制声音

 

public class CaptureSound
{

    
私有成员

    
构造函数

    
公开属性

    
公开事件

    
私有方法

    
公开方法
}


 

程序下载地址如下(自己反射看源码,因为程序只是用于演示,所以考虑很不周全,不足以效仿)
http://files.cnblogs.com/onlytiancai/wawaim.zip
PPT下载地址如下
http://files.cnblogs.com/onlytiancai/p2p语音.zip

参考链接如下:
ch11-DxSound&Input2.ppt:建立DirectSound 声音的播放与控制 使用3D音效
SIP之 穿越NAT.ppt
DirectX技术实现视频会议中的音频通信
http://www.ctiforum.com/forum/2008/03/forum08_0357.htm
C#中使用DirectSound录音
http://blog.donews.com/uplook/archive/2005/12/14/657145.aspx
在C#下利用DirectSound实现声音播放
http://www.cnblogs.com/yangbeibei/archive/2006/08/30/490270.html
隨意桌面,數位溝通
http://cuteofdragon.blogspot.com/2007/05/blog-post_9694.html
用DirectX Audio和DirectShow播放声音和音乐
http://www.cppblog.com/lovedday/archive/2007/09/24/32815.html


 

蛙蛙推荐:蛙蛙牌儿IIS备份器

05-18

蛙蛙推荐:蛙蛙牌儿IIS备份器rn把下面所有代码保存成一个.hta文件,然后双击就可以了rnrnrn New Document rn rn rnrnrn备份名称 rnrnrnrnrnrn rn rn 问:这个小软件有什么用?rn 答: 这个小软件用来手工备份和还原IIS配置,如果IIS元数据库严重受损,IIS 将无法启动,这个小软件可以做的小帮手,帮助你在IIS出错的时候选择合适的备份来恢复IIS配置.rn 问:重装机器后如何恢复IIS配置?rn 答: 做虚拟主机的时候如果重装了系统,一般需要手工一个一个的添加网站,网站很多的话,非常费时费力,而象IIS备份精灵,IISExport都需要注册才能无限制使用.其实在Internet 信息服务管理器控制台(也称为 IIS 插件)中所设置的属性和值默认情况都储存在 C:\winnt\system32\inetsrv\metabase.bin (如果是windows 2003+iis6是C:\WINDOWS\system32\inetsrv\MetaBase.xml)文件中。可以简便地使用 xcopy、scopy 或任何其他复制程序来复制这个 文件。但这时最好先停止 Internet 服务(在cmd命令模式下里键入 net stop "IIS Admin Service"来停止Internet服务,net start "IIS Admin Service"来启动IIS服务,或者在服务管理器在界面模式下操作,),以保证元数据库是最新的并且不在使用状态中。 重装系统的时候先停止internet服务,然后覆盖那个目录,IIS配置就恢复了.rn rn rn rn 问:请提供一些关于IIS管理和编程的文章rn rn 答: rn 1.Internet Information Services SDK rn http://msdn.microsoft.com/library/default.asp?url=/library/en-us/iissdk/html/b1073d67-0cfd-42cb-b62b-97e670a4eafb.asp rn 2.Backing Up and Restoring the IIS Metabase rn http://www.windowsitpro.com/Web/Article/ArticleID/9159/Web_9159.htmlrn 3.有关 Internet 信息服务的 5 个热点问题及其解答 rn http://www.microsoft.com/china/technet/community/columns/insider/iisi0602.mspxrn 4.IIS 内幕 rn http://www.microsoft.com/china/technet/community/columns/insider/default.mspxrn 5.Internet Information Services rn http://msdn.microsoft.com/library/default.asp?url=/library/en-us/iissdk/html/cd7a8a8d-dc9d-45be-b5f8-b7d548053b3f.asprn rn rn rnrnrnrn

蛙蛙推荐:蛙蛙Delphi学习经验(第一版)

01-21

蛙蛙推荐:蛙蛙Delphi学习经验(第一版)rn  一、使用ModelMaker设计类,并生成设计文档。rn  ModelMaker是Delphi7自带的一个UML建模软件,支持和Delphi IDE的双向工程,没用过Rose For Delphi的插件。我直接用的MM6,感觉MM就挺好用的。rn  你可以在Classes视图里添加类,并添加类的方法、字段、属性、字段,这些操作都很简单,在Classes视图的上边类部分右键点击添加类,然后选中这个类,点击下面的各个添加成员按钮,按向导就可以给类添加成员了。rn  要想生成代码,请点击Units视图,在Units视图的空白处右键点击添加新的单元,在弹出的Edit Unit对话框里,在Relative Unit file name后面点击Browse按钮选择单元保存的路径,其它参数你看着选择,然后点击OK。然后在Units视图里把刚刚添加的类从Classes not assignet to units拖到新建的单元下面。选中刚刚添加的单元,确保最上面按钮条的开着的小锁按钮处于按下状态(代码生成解锁,如果不选中,则代码处于保护状态,无法生成代码。)点击Units视图上的闪电按钮生成代码。点击菜单Delphi-locate in delphi,就可以在delphi里打开刚刚生成的代码了,你可以在实现部分写你要写的代码,因为mm只给你生成了一个类的架子,我们在代码的接口部分再添加一些字段和方法,以及注释,然后点击菜单ModelMaker-jump to modelmaker,又切换到mm界面了。在Units视图里点击刷新按钮(和浏览器的刷新按钮类似的那个按钮),你会发现刚刚在delphi里添加的字段和方法已经同步到了mm里,然后你再在MM里修改类,再生成代码,你会发现其它部分都生成的很好,就是接口部分你给成员写的注释给没了,这就不爽了,好歹也是你手工写的注释代码,为啥MM给你弄没了捏,一会儿再给大家介绍解决办法。rn  模型有了,我们还要生成模型的文档,以便和别人交流。在左边点击到单元视图,右边点击到Documentation视图。在左边选中单元,在右边的文档视图的下拉菜单里选择Unit,然后在下面大的文本编辑区域里写关于单元的注释文本。在左边点击类,右边下拉列表里选择Class,添加类的注释,在左边选择类的成员,右边下拉列表里选择Class Member,添加类成员的注释。OK,把右边的下拉列表选到unit,点击下拉列表左边的问号按钮,在弹出的对话框里,选择文档的保存路径,和是否生成私有成员的注释,然后点击OK就生成文档了。打开刚刚生成文档的目录,你会发现一个rtf文件和一个hpj文件,打开rtf文件一看,原来不支持中文,这可咋办,这可咋好,这可咋整,别着急,从网上下载个Microsoft Help WorkShop装上,双击生成的.hpj项目,打开后点击存盘并编译按钮,就生成一个hlp文件了,这不就是你要的文档吗?多好呀,和MSDN似的。.net的ndoc也不过如此嘛。rn  生成文档有个缺点,我给大家说说哦,你在文档视图里给类添加的描述(One Line)是不是想生成到代码里作为注释以提高代码的可读性呀,我也想呀,可是找了半天帮助也不直到怎么生成。还有,你是不是想在代码里写一定的注释,让mm同步的时候直接同步到文档视图里显示呀,这样你就可以在delphi里写注释,用mm来生成专业文档了,这个应该也能搞成,但我还是没搞成,你写的注释要符合MM的格式,大括号的那种注释。在MM的Project option对话框的Source Doc Generation标签里有生成注释和同步注释的一些设置,然后在帮助里有关于类注释、成员注释的一些语法,你可以看看,没准儿能鼓捣成呢,我主要是没时间琢磨了。其实这两个不算是MM的缺点,只能怪我没研究出来了。还有一个缺点,就是MM在生成文档的时候只能生成一个单元的,我晕,这个问题困扰了我好久,最终在它的网站上下载了一个插件装到MM上才搞定,这插件免费的哦。http://www.modelmakertools.com/modelmaker/plugins.html。下载那个HelpExpert插件,解压后把Expert目录下的OCHelpExpertMM6.dll拷贝到mm6安装目录的Expert目录下,把bin目录下的rtf文件拷贝到mm6安装目录的bin目录下。点击MM的tools菜单的Expert Manager子菜单,在打开的对话框里看看有没有多了一个OCHelpExpertMM6.dll,打上勾,发现tools里多了一个子菜单Geneater help form all units,点击就可以生成所有单元的文档了,cool吧。rn  MM的基本功能咱就挖掘到这儿吧,另一个常用的功能就是它的UML图视图,在这里你可以把你设计好的类,拖上去,以表示它们之间的关系给你的开发团队成员看。你刚拖上去的类也只显示一个类名,没有成员,你在这个类上点击右键,选择dragram properties,在打开的对话框里,把Class symbol member filter组合框的Project member type filter复选框的勾去掉,然后把custom member type filter下面的勾打上就可以显示成员了,这下你就可以画UML图来表达你的设计了,我想最常用的也是类图和组件图吧,其它的图我没用过。MM的功能还很强大,可以直接根据设计模式生成代码的架构,还有用宏自定义生成代码等功能,等你觉得以上我介绍的功能不够用的时候你可以学习学习,网上mm的教程不多,但也有几个比较好的。比如《ModelMake初探.rar》,比如《MM(ModelMaker)两小时上手指南》,还有大富翁整理的关于MM应用设计模式的一个教程。rn  总的来说mm对delphi的开发还是很有帮助的,和delphi结合紧密,比visio和vs结合还好,建议delphi的新手学学。老手一般不用这个,直接手工写就可以了,但如果想节省时间的话也可以用用。rn  二、使用OutputDebugString进行跟踪输出rn  无论哪一种语言和编辑工具,使用好它的调试功能都是非常必要的。在Delphi里可以使用OutputDebugString API函数输出调试信息,然后在调试运行的时候可以打开view-debug windows-event log菜单,查看你跟踪输出的信息。如果软件编译成二进制执行代码后,可以去微软下载个你debugview(下载地址自己搜索一下吧,微软绝对免费下载),用它去查看调试信息。在用Delphi调试的时候用debugview看不到信息的,因为被delphi的Event log给截获了。DebugView里可以使用命令行来启动,并把获取的信息输出到一个文本文件,还可以用命令行自动运行,每天生成一个日志,具体命令行参数可以看帮助。另外DebugView非常适合监控分布式程序,它有远程监控功能,你可以在你的机器上监控到运行在远程机器上的软件的调试信息,前提是你在那个服务器上用命令行启动一个debugview服务器,这个我没有试过哦,人家帮助里说的,现在DebugView刚出新版本,支持64位系统。有了它,你就可以简化你的日志方案了,你可以轻轻松松了解你的软件的运行状态,根本不用自己鼓捣一套。另外你也可以自己开发一个截获OutputDebugString输出消息的小软件,网上有资料,这样你就可以自定义显示你的调试信息和跟踪信息了。rn  三、使用断言,给断言处理事件挂接一个处理错误的回调函数。rn  我们.net程序员都知道,像c#,java这样的语言编译后不是二进制代码,而是可以解释执行的一种东西,这样在我们调试的时候,如果出错了,报的错可以显示错误所在的堆栈,方法,行号(当然,你release编译或者把pdb调试文件去了就没行号了),而Delphi就不行了。如果你在异常处理里想把异常出现的堆栈,方法和行号输出出来简直是不可能的。但有个你变通的方法,就是使用断言,断言在调试状态下可以指出错误行号(其实我编译生成exe后好像也可以输出源文件的行号,我不知道为什么)。我们要先为断言失败事件挂接一个默认执行方法,如下。rnprocedure AssertErrorHandler(const Message, Filename: string; LineNumber: Integer; ErrorAddr: Pointer);rnvarrn S: String;rnbeginrn S := Format('%s (%s, line %d, address $%x)',rn [Message, Filename, LineNumber, Pred(Integer(ErrorAddr))]);rn OutputDebugString(PChar(S));rnend;rnrninitializationrn AssertErrorProc := @AssertErrorHandler rn然后你就可以使用断言了, Assert(pam1 <> nil,'参数为空');比如你的方法要传入一个pam1的参数,如果传进来的参数未定义,这个断言就会失败,从而触发断言失败事件,然后你就可以用debugview看到断言失败的行号了。rn  使用这个方法的不足就是,你只能获取可预期异常的信息,对未知异常无法使用了。rn

蛙蛙推荐:置疑纯ORM方案

07-23

蛙蛙推荐:置疑纯ORM方案 rn纯orm方案的缺点:rn处理复杂对象查询时有难度。数据模型中的所有表格及关系很复杂,包括关联、引用(也就是主从表)和继承三种关系,甚至包括嵌套的复杂关系,在做or mapping的时候非常复杂。rn对应于表中的连接查询,如果直接写sql语句违背了map 规则,不写,灵活度不够。虽然在一些ORM方案中也可以把数据库表之间的关系也映射到对象层里,但是这样在数据库和数据访问层之间增加了一个映射层,再说了映射用的元数据都是XML格式的,访问这个XML文件的时候还得做额外操作,性能肯定要降低了(当然可以在应用程序启动的时候把元数据都缓存起来)。而且那个什么Opath语言,我看不出来和t-sql相比,他有什么优势,Xpath是查询XML用的,它用来查询Object,但是它的Opath查询,最后还是得转换成SQL来查询数据库呀,而且很可能一条Opath语句查下去可能得生成好几条SQL语句,而且它生成的SQL语句有你自己写的语句可靠吗,有你自己写的sql语句性能好吗?我看呀,以后这人们就都不用学sql了,直接学这个Opath就行了,到时候只有高手才会写SQL语句,就象初学者只会调用.NET封装好的类一样,而不知道这些类是怎么封装了COM和API的。我不是说这不好哦,这样做入门是简单了,可是人们被迷惑了似的,多年以后肯定只有微软自己的人才知道这些类的实现原理,而人们想接触一下底层的东西就很难了,我担心有一天我们的精力不够了,从懂事就开始学编程,一直学到40岁了才算入门,还啥都用不上,因为知识太多了,要想实现一个简单程序就得学10几年的知识,这不是悲哀吗,呵呵,撤远了哦。rnrn下面说说我的想法哦:rn我感觉将关系数据映射到业务实体,还是手工或者半手工做的比较好,因为你完全靠工具来生成OSD,RSD,MSD这些文件肯定不能完全覆盖你在领域分析后得出的问题域,工具哪儿有人智能呀,人有时候还分析不出来呢?rnrn而数据库层面的的业务逻辑实现(细粒度的)还是用存储过程来做比较好,而靠Opath这类的语言生成的sql语句肯定不能达到最优化(你能相信DataAdapter自己生成的数据更新sql语句吗?还是使用自定义命令呀?),而且在集合处理方面还存在问题(你说在内存里建立1000个对象的实例和建立一个有1000行的DataTable哪个开销大?),orm是建立在ADO.NET之上的,以后人们是不是连ADO.NET也不用学了呀,直接把对象组成的ArrayList绑定到DataGrid上呀?rnrn现在这人是越来越懒了,我感觉写一个程序最重要的工作还是业务建模和写业务层的代码,这个层的代码估计除了MDA没什么工具可以生成的吧(个人感觉MDA也不是很合适的软件开发方法)?当然了,向orm这些东西属于次要复杂性,本应该是由工具和语言来完成,而不应该耗费程序员的精力(谁也不会否定面向对象的软件开发比结构化编程有优势,谁也不会否定递归算法很巧妙,尽管它耗费资源),但是我感觉还没成熟到那种全自动的程度吧,在全自动生成和全手工编写之间我们得找一个平衡点儿。rnrn我们的目标是在数据库从sqlserver换成oracle,一个表里增加了两个字段的时候,客户要求再给某个对象增加一个方法的时候我们只要改动数据访问层和业务实体层就可以了,而尽量不改动业务层和表示层的代码,也就是维护方便。我们想利用ORM来达到这种效果,我们认为数据实体层和数据访问层只改动元数据就可以完成维护工作了,可是这些东西用了大量的反射(性能降低了),而业务层代码和UI不用改了吗,这怎么可能呢?你数据表里增加个字段,你的业务层的方法签名不需要改吗(就算你用业务实体(业务实体根据xml确定)做传入参数,但是方法里面处理的时候你不得变呀)?还有你的界面显示的时候不还得变呀,你不做客户端的数据验证代码了吗?这问题多着呢,哪儿能那样一劳永逸呀?现在还除了好多代码生成的工具,甚至界面也能靠XML生成了,人都考虑不过来的东西,让工具去考虑,真是的。rnrn我想我们一定是厌倦了在c#里写那么多重复繁琐的sql处理代码了,我们是想把处理sql或者存储过程的这些代码自动化,我们不想为每个存储过程的调用手工创建一大堆参数,然后给参数赋值,然后再调用DAAB执行,还得考虑参数缓存什么的,我们肯定讨厌这些重复工作了,我们想存储过程修改(无论是存储过程本身改变还是从sql的存储过程改变成oracle的存储过程)后我们的数据访问层能自动适应,自动创建参数,自动赋值。如果想这样,就得给存储过程建立元数据,而不是给表建立元数据,表对应的是Object(实际物体,订单啦,产品啦),而存储过程对应的是Action(例如增加或者取消一个订单)或者Service(例如根据某个条件来生成一个DataView)的数据层实现,当然有人说了“随着在存储过程中实现的业务逻辑的增多,存储过程可以简化维护带来的优势会逐渐减弱”,可是那也比纯用Opath这类更高层次的语言来实现数据访问有优势吧,再说了存储过程只封装细粒度的业务逻辑,返回简单处理后的数据,然后由数据层经过复杂处理才提供给业务层的。我估计光Opath也不能完成粗粒度的业务逻辑吧,也得把结构提供给业务层吧?rnrn我设想了一个架构(抄的,不过感觉很灵活):rnMonitorServices:负责监控和跟踪。rnMonitorServicesLogging:负责日志记录和错误处理。rnConfigurationServices:负责配置服务,从.config里读取配置数据。rnCMPServices:提供CMP处理rn这几个项目提供底层服务,微软发布了7中企业开发模块,里面几乎都包含了上面我提到的这些东西(除了最后一个),我感觉我提出的这几个是必要的,比较通用,加密解密和缓存是可选的,有的项目里用不到吧,看项目需要再做添加。rnrn别的好说,微软的企业开发模块里都包括了,在设计模式和实际操作上都给了一些指导,微软好像有视频下载,下载下来看看,理解一下,再参考MSDN的类库开发人员指南,写出自己的类库就行了。rn着中看一下CMPServices类库,在这里执行操作存储过程,首先肯定得有个类负责读取存储过程的元数据,元数据里包括了存储过程的名字,参数的信息,以及对应的业务实体类(业务实体类可以用代码生成器生成再改改),这些元数据放在.config里,如果这些东西改了,调用存储过程的代码自动就改了。而不象petshop里那样为oracle和sqlserver写两个dal层,然后用通过接口和工厂类来确定使用哪个类库,这还算好的,普通的方案的BLL层里调用DAL层的方法的时候用的是RunSqlserverSP(strSP)或者RunOracleSP(strSP)这样的方法,DAL层和BLL层是紧密耦合,这些都属于托管组件持久性(Component Managed Persistence),而我想用的是托管容器持久性(Container Managed Persistence)。当然这个类库里还要设计好几个类来提供业务实体的持久性,简单介绍一下。rnCMPConfigurationHandler:读取.config里的XML数据生成容器映射和命令映射类,并缓存起来。rnCommandMapping:命令映射类,存储过程的名称,参数等,对应一个命令。 rnCommandParameter :存储过程的参数,包括参数的类型,长度,方向等rnContainerMapping :容器映射类。rnContainerMappingSet :一组容器映射类。rnPersistableObject :持久类,和业务实体类差不多。rnPersistableObjectSet 一组业务实体类,如果一个select语句返回1000个记录,与其生成1000个PersistableObject的实例,不如保存在Dataset里。rnStdPersistenceContainer:这是个容器基类,定义实现容器映射的成员,每个容器可以执行CRUD四个命令。rnSqlPersistenceContainer :sqlserver的容器实现类,这里利用反射和XML元数据来建立存储过程元数据和持久类之间的关系。rnrn写业务逻辑层的时候可以实力一个容器,然后调用容器的CRUD命令来执行操作,实际上这就是去执行存储过程了,而存储过程里你再去写更细的逻辑,给CRUD命令传递参数的时候可以把这个容器对应的持久类的实例传进去,然后容器自己就会处理持久类实例的属性和存储过程参数的对应关系。这样就相当于把存储过程包装起来了,也在持久对象和数据库间建立了一个层,但这个层比纯ORM的方案灵活吧,你也不用opath这类晦涩的语言执行关系数据库查询,存储过程你爱怎么写怎么写,只要能灵活应用这种架构,就能适应无穷无尽的业务逻辑。如果数据库变了,至少在数据访问层可以做很小修改就能完成维护工作,改元数据呗,至于由于数据库变化导致的业务层和其它中间层,显示层的改动可以通过其它技巧减少一些,肯定不可能一劳永逸的,我们差完全自动生成代码还远呢,你的基础架构越强大,灵活性就越差我感觉,因为动些越多,耦合性越强,到时候架构功能太多,我想去一项服务也不行,本身就难以维护了,是吧,就象.NET框架,部署的时候还得把它部署进去,20多M的东西,我写一愕几十K的程序得部署一个20多M的框架,我能不生气吗?rnrn别人置疑.NET社区,偶就来置疑一下纯ORM吧,呱呱。rn

蛙蛙推荐:保护你的程序

05-08

蛙蛙推荐:保护你的程序rn我们知道windowsXP现在使用了在线激活的功能来实现保护正版的功能,它是通过你的硬件的一些标识生成一个原始数据(通常是一个字符串),然后把这个原始字符串通过inernet发送到ms网站的一个程序里,而这个程序接受到这个原始字符串后经过一些不为人知的算法处理后再发送给你的机器,我想或多或少是在你的机器上做了一些手脚,我们假设在你的硬盘生成一段密文。然后你的机器启动的时候就读取你的硬件标识并还生成那个原始数据,在本地还是经过那个不为人知的算法后得到一段密文,然后用刚刚生成的密文和在线激活的时候生成到你硬盘上的那段密文比较,如果相同,那你就能进入桌面,如果不同,那你就进不了桌面,而如果你机器更改了硬件配置,生成密文的原始数据就变了,所以密文就不同了,你就需要重新算号了。rn当然了,我是把问题描述简单化了,winxp是把激活文件放在一个特殊文件里了,要不我们破解winxp和window 2003的时候也不替换那几个文件了,另外我还有意忽略了一些在线激活的细节。rn我们可以把这个激活技术推广到我们自己的软件中去,毕竟我们程序员最不愿意看到的就是我们费了很久的时间,投入了很大的精力开发出的软件在被人们疯狂的拷贝使用,并且没有给你一点好处,尽管这是事实,我们还是要做一些措施的。rn微软的用以确定硬件杂凑编码的10项硬件设备特征分别是:显示适配器、SCSI适配器、IDE适配器、网络适配器MAC地址、RAM容量范围(例如0至64兆或64至128兆)、处理器类型、处理器序列号、硬盘驱动器设备、硬盘驱动器卷序列号、CD-ROM/CD-RW/DVD-ROM。我们就不要这么复杂了,我们只要其中3个就行了。rnrn一.获取硬件杂凑编码rn把下面的代码复制到剪贴版里,并保存成一个.vbs文件,这个脚本文件用来获取你的机器的CPUID,硬盘的PNP DeviceID,和网卡MAC号,得到的这3个字符串都能代表你的机器特征,而3个字符串组合起来几乎是就是表示你的机器的一个全球唯一标识。按理说,无论你重装系统,还是格式化硬盘,得到的这个组合字符串是唯一不变的,我们可以用它作为我们生成软件安装序列号的原始数据,当然这个组合字符串是很长的,你可以把这个字符串进行摘要算法处理后作为原始数据,比说把把这个字符串进行MD5加密。rnOn Error Resume NextrnrnstrComputer = "."rnstrOut = ""rnSet objWMIService = GetObject("winmgmts:" _rn & "impersonationLevel=impersonate!\\" & strComputer & "\root\cimv2")rnrn'获取网卡的MAC地址,因为一个机器可能有多个网络适配器,所以我们只获取AdapterType值为"Ethernet 802.3"的适配器MAC号,也就是AdapterTypeID=0的网络适配器,网卡号其实不是很保险,有的小网卡厂商生产的网卡可能会有重复的.rnSet colItems = objWMIService.ExecQuery("Select * from Win32_NetworkAdapter")rnFor Each objItem in colItems rn If objItem.AdapterTypeID = 0 Then strOut = strOut & "MAC Address: " & objItem.MACAddress &vbcrlfrnNextrn'获取CPU的序列号,这个号可不是全球唯一哦,有可能一批CPU使用一个序列号rnSet colItems = objWMIService.ExecQuery("Select * from Win32_Processor")rnFor Each objItem in colItemsrn strOut = strOut & "Processor ID: " & objItem.ProcessorId&vbcrlfrnNextrn'获取硬盘的PNP DeviceID,这个号和传说中的磁盘序列号差不多,应该是也是唯一的,大家都把这个值给偶发上来偶看看.rnSet colDiskDrives = objWMIService.ExecQuery _ rn ("Select * from Win32_DiskDrive")rnFor each objDiskDrive in colDiskDrivesrn strOut = strOut & "DiskDrives PNP DeviceID: " & vbTab & objDiskDrive.PNPDeviceID&vbcrlfrnNextrn'建立一个IE对象,并利用它的功能巧妙的把输出字符串复制到剪贴版里.rnSet objIE = CreateObject("InternetExplorer.Application")rnobjIE.Navigate("about:blank")rnobjIE.document.parentwindow.clipboardData.SetData "text", strOutrnobjIE.QuitrnWscript.Echo strOutrn以上是通过WSH脚本来获取硬件字符串的,我们让用户复制这个字符串,并通过你提供的在线算号的web程序里算号,当然你可以在算号前要求对方做一些事,比如说点击一下广告啦,照顾你一些money啦什么的。rn

蛙蛙推荐:asp提高首页性能的一个技巧

03-25

蛙蛙推荐:asp提高首页性能的一个技巧rnrn简单介绍:一般一个网站的首页访问量是最大的,如果您的网站的首页打开的非常缓慢,您的客户将会陆续离开你的网站.通常我们把需要经过复杂运算或者查询数据库得出的数据缓存起来或者生成静态网页来提高web应用的性能,这次我们直接把首页的输出缓存成一个字符串,然后定时更新,即照顾了性能,又不影响首页的时效性.这里用到了一些VBS自定义类,Application对象,XmlHttp对象,adodb.stream对象的一些东西,相关知识大家可以查资料了解.rnrn<%rnDim wawa,StarTime,EndTimernStarTime=Timer()rnSet wawa=new Cls_Cachernwawa.Reloadtime=0.5rnwawa.CacheName="wawa"rnwawa.Name="XmlInfoIndex"rnIf wawa.ObjIsEmpty() Then CacheXmlInfoIndex()rnResponse.Write wawa.valuernEndTime=Timer()rnResponse.Write " 执行时间:" & FormatNumber((Endtime-StarTime)*1000,5) & "毫秒。"rnrnSub CacheXmlInfoIndex()rn Dim BodyText, xmlrn Set xml = Server.CreateObject("Microsoft.XMLHTTP")rn '把下面的地址替换成你的首页的文件地址,一定要用http://开头的绝对路径,不能写相对路径rn xml.Open "GET", "http://onlytiancai/bak/vote/InfoIndex.asp", Falsern xml.Send rn BodyText=xml.ResponseBodyrn BodyText=BytesToBstr(BodyText,"gb2312")rn wawa.Value=BodyTextrnSet xml = NothingrnEnd SubrnFunction BytesToBstr(body,Cset)rn dim objstreamrn set objstream = Server.CreateObject("adodb.stream")rn objstream.Type = 1rn objstream.Mode =3rn objstream.Openrn objstream.Write bodyrn objstream.Position = 0rn objstream.Type = 2rn objstream.Charset = Csetrn BytesToBstr = objstream.ReadText rn objstream.Closern set objstream = nothingrnEnd Functionrn%>rn<%rn'下面这个类可以保存在单独的文件里,然后包含到此页rnClass Cls_Cachern Rem ==================使用说明=================================================================================rn Rem = 本类模块是动网先锋原创,作者:迷城浪子。如采用本类模块,请不要去掉这个说明。这段注释不会影响执行的速度。=rn Rem = 作用:缓存和缓存管理类 =rn Rem = 公有变量:Reloadtime 过期时间(单位为分钟)缺省值为14400, =rn Rem = MaxCount 缓存对象的最大值,超过则自动删除使用次数少的对象。缺省值为300 =rn Rem = CacheName 缓存组的总名称,缺省值为"Dvbbs",如果一个站点中有超过一个缓存组,则需要外部改变这个值。 =rn Rem = 属性:Name 定义缓存对象名称,只写属性。 =rn Rem = 属性:value 读取和写入缓存数据。 = rn Rem = 函数:ObjIsEmpty()判断当前缓存是否过期。 =rn Rem = 方法:DelCahe(MyCaheName)手工删除一个缓存对象,参数是缓存对象的名称。 =rn Rem ===========================================================================================================rn Public Reloadtime,MaxCount,CacheNamern Private LocalCacheName,CacheData,DelCountrn Private Sub Class_Initialize()rn Reloadtime=14400rn CacheName="Dvbbs"rn End Subrn Private Sub SetCache(SetName,NewValue)rn Application.Lockrn Application(SetName) = NewValuern Application.unLockrn End Sub rn Private Sub makeEmpty(SetName)rn Application.Lockrn Application(SetName) = Emptyrn Application.unLockrn End Sub rn Public Property Let Name(ByVal vNewValue)rn LocalCacheName=LCase(vNewValue)rn End Propertyrn Public Property Let Value(ByVal vNewValue)rn If LocalCacheName<>"" Then rn CacheData=Application(CacheName&"_"&LocalCacheName)rn If IsArray(CacheData) Thenrn CacheData(0)=vNewValuern CacheData(1)=Now()rn Elsern ReDim CacheData(2)rn CacheData(0)=vNewValuern CacheData(1)=Now()rn End Ifrn SetCache CacheName&"_"&LocalCacheName,CacheDatarn Elsern Err.Raise vbObjectError + 1, "DvbbsCacheServer", " please change the CacheName."rn End If rn End Propertyrn Public Property Get Value()rn If LocalCacheName<>"" Then rn CacheData=Application(CacheName&"_"&LocalCacheName) rn If IsArray(CacheData) Thenrn Value=CacheData(0)rn Elsern Err.Raise vbObjectError + 1, "DvbbsCacheServer", " The CacheData Is Empty."rn End Ifrn Elsern Err.Raise vbObjectError + 1, "DvbbsCacheServer", " please change the CacheName."rn End Ifrn End Propertyrn Public Function ObjIsEmpty()rn ObjIsEmpty=Truern CacheData=Application(CacheName&"_"&LocalCacheName)rn If Not IsArray(CacheData) Then Exit Functionrn If Not IsDate(CacheData(1)) Then Exit Functionrn If DateDiff("s",CDate(CacheData(1)),Now()) < 60*Reloadtime Thenrn ObjIsEmpty=Falsern End Ifrn End Functionrn Public Sub DelCahe(MyCaheName)rn makeEmpty(CacheName&"_"&MyCaheName)rn End SubrnEnd Classrn%>

蛙蛙请教:把一段c算法代码转换成c#代码。

04-10

蛙蛙请教:把一段c算法代码转换成c#代码。rn这是一段剪贴板的数据转换算法代码,请帮忙把UTF8ToHtml转换成c#代码,我转了一下,根本不能用。rn再帮忙写下注释,简单说一下原理,谢谢。rn原文如下:rnI was working on a project where I had to paste into textbox HTML, copied from the Browser. A quick search on "HTML Clipboard Format" in MSDN gives you an article that thoroughly explained how HTML is kept in the Clipboard. Unfortunately, this article tells you that it's kept in UTF-8 format without explaining how to convert from UTF-8 back to HTML. So I had do some research on my own.rnrnUTF-8 is the format that allows using Unicode characters in ASCII text by embedding a special token, &#code;, into the text, where the code is Unicode code (in decimal format) for the symbol. For some symbols there are special names. An example " " is " "... You can jump to the specification if you need more examples.rnrnHere is a UTF8ToHtml function, which converts from UTF-8 to HTML. The algorithm is not explained, but you can read more about it here.rnrn//utf8 - pointer to UTF8 formatted text. dwSize - size of UTF8 text; ptr is the pointer to Output buffer.rnrn//The OnClickedPastehtml is the handler for BN_CLICK event of the button in Dialog box. IDC_TEXT is the multiline text box.rnrnrnvoid UTF8ToHtml(BYTE *utf8, DWORD dwSize, CHAR *ptr )rnrn int code;rn BYTE *end = utf8 + dwSize;rn while( utf8 < end )rn rn code = 0;rn if( (*utf8 & 0xF0) == 0xF0 )rn rn code = (((*utf8)&0x0F) << 18) | (((*(utf8+1))rn & 0x7F)<<12) | (((*(utf8+2)) & 0x7F)<<6)rn | ((*(utf8+3)) & 0x7F );rn utf8+=3;rn rn elsern rn if( (*utf8 & 0xE0) == 0xE0 )rn rn code = (((*utf8)&0x1F) << 12) | (((*(utf8+1))rn & 0x7F)<<6 ) | ((*(utf8+2)) & 0x7F );rn utf8+=2;rn rn elsern rn if( (*utf8 & 0xC0) == 0xC0 )rn rn code = (((*utf8)&0x3F) << 6) | ((*(utf8+1)) & 0x7F) ;rn utf8+=1;rn rn rn rnrnrn if( code == 0 )rn rn *ptr = *utf8;rn rn elsern rn char s[10];rn switch(code)rn rn case 160:rn strcpy(s, "& ");rn break;rn case 34:rn strcpy(s, "&");rn break;rn case 36:rn strcpy( s, "&&");rn break;rn case 60:rn strcpy( s, "&<");rn break;rn case 62:rn strcpy( s, "&>");rn break;rn default:rn sprintf( s, "&#%d;", code );rn break;rn rn strcpy( ptr, s );rn ptr += strlen(s)-1;rn rn utf8++;rn ptr++;rn rn *ptr = 0;rnrnLRESULT CDialog::OnClickedPastehtml( WORD wNotifyCode,rn WORD wID,rn HWND hWndCtl,rn BOOL& bHandled)rnrn if (!OpenClipboard() )rn return 0;rn UINT uHtmlFormat = RegisterClipboardFormat("HTML Format");rn UINT uFormat = uHtmlFormat;rn if( IsClipboardFormatAvailable( uHtmlFormat ) == FALSE )rn rn if( IsClipboardFormatAvailable( CF_TEXT ) == FALSE )rn return 0;rn uFormat = CF_TEXT;rn rnrn HGLOBAL hglb;rn LPTSTR lptstr;rn hglb = GetClipboardData(uFormat);rn if (hglb != NULL)rn rn lptstr = (LPTSTR)GlobalLock(hglb);rn if (lptstr != NULL)rn rn char *ptr1 = strstr( lptstr, "");rn if( ptr1 != 0 )rn rn ptr1 += 20;rn char * ptr2 = strstr( lptstr, "");rn int iSize = (ptr2 - ptr1);rn char * tmp = (char*)_alloca( iSize *2);rn UTF8ToHtml((BYTE*)ptr1, iSize, tmp );rn //memcpy(tmp, ptr1, iSize );rn //tmp[iSize] = 0;rn SetDlgItemText(IDC_TEXT, tmp );rn rn elsern SetDlgItemText(IDC_TEXT, lptstr );rn GlobalUnlock(hglb);rn rn rn CloseClipboard();rn return 0;rnrn这是我转换的代码rnprivate void FTopMost_DragDrop(object sender, DragEventArgs e)rnrn strCont = e.Data.GetData(DataFormats.Html, true).ToString();rn int start = strCont.IndexOf("");rn int end = strCont.IndexOf("");rn string s = strCont.Substring(start + 20, end - start - 20);rnrn FNewPost f = new FNewPost();rn f.HTML = UTF8ToHtml(s);rn f.Show();rn f.Activate();rnrnstring UTF8ToHtml(string utf8)rnrn string ptr = null;rn int code;rn for (int i = 0; i < utf8.Length; i++ )rn rn code = 0;rn if ((utf8[i]) == 0xF0)rn rn code = (((utf8[i]) & 0x0F) << 18) | ((((utf8[i] + 1)) & 0x7F) << 12) | ((((utf8[i] + 2)) & 0x7F) << 6) | (((utf8[i] + 3)) & 0x7F);rn i += 3;rn rn elsern rn if ((utf8[i]) == 0xE0)rn rn code = (((utf8[i]) & 0x1F) << 12) | ((((utf8[i] + 1)) & 0x7F) << 6) | (((utf8[i] + 2)) & 0x7F);rn i += 2;rn rn elsern rn if ((utf8[i]) == 0xC0)rn rn code = (((utf8[i]) & 0x3F) << 6) | (((utf8[i] + 1)) & 0x7F);rn i += 1;rn rn rn rnrn if (code == 0)rn rn ptr += utf8[i];rn rn elsern rn string s = null;rn switch (code)rn rn case 160:rn s = "& ";rn break;rn case 34:rn s = "&";rn break;rn case 36:rn s = "&&";rn break;rn case 60:rn s = "&<";rn break;rn case 62:rn s = "&>";rn break;rn default:rn //sprintf( s, "&#%d;", code );rn s = "&" + code.ToString();rn break;rn rn ptr += s;rn rn rn return ptr;rnrn这是一段剪贴板数据,当然这里没有中文rnVersion:0.9rnStartHTML:71rnEndHTML:170rnStartFragment:140rnEndFragment:160rnStartSelection:140rnEndSelection:160rnrnrnrn The HTML Clipboardrn rnrnrnrn The Fragmentrnrnrn

蛙蛙请教:c#面向对象编程的有些抉择问题?

07-21

蛙蛙请教:c#面向对象编程的有些抉择问题?rn什么情况下用class,什么情况下用struct?rn如果要创建的对象有行为的话用class,如果只是保存数据的话用struct,但是尽量用class。rn关于明辨值类型和引用类型的使用场合请参考下面的链接。rnhttp://www.microsoft.com/china/msdn/library/langtool/vcsharp/EffectiveCsharp.mspxrn关于值类型使用场合请参考以下链接rnhttp://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/cpgenref/html/cpconvaluetypeusageguidelines.asprnrn什么情况下用const,什么情况下用readonly?rnconst是在编译的时候确定的,而readonly是在执行的时候确定的,如果是全局不变的数据,并且是值类型可以用const声明。而readonly有个在构造函数里再进行初始化的机会,所以如果在web.config里提取的配置信息可以用readonly来声明。rn什么情况下用inteface,什么情况下用 base class,什么情况下用delegate?rn如果确定以后不会更改可以用interface,如果以后可能要更改就用基类,基类还可以完成一些默认的操作,而且修改后,派生类会自动继承新成员,如果是接口修改的话,实现接口的类就不能运行了。如果单独定义一种行为或者一组结合不紧密的行为可以用接口,如果定义的一系列结合紧密的行为可以用基类。因为这些行为结合紧密的话,以后可能还要改动,如果结合不紧密的话,就可能很少修改,即便修改也不一定要动封装好的东西。所以我感觉基本的东西用基类来定义,而扩展的东西用接口来定义。把基类和接口结合起来使用,比如说定义生物,动物,鸟,人这几个对象吧。把生物定义成一个基类,在这个基类里把“有生命的”,“由有机物组成”这些放到这个基类里,而定义一系列接口,比如“能动的”,“没有细胞壁”,“有羽毛的”,“会使用工具的”。由这个基类派生并且实现了“能动的”接口就是“生物”,再把这个作为基类,再实现一个“有羽毛的”接口,就是“鸟”,而再实现“会使用工具的”接口,就是“人”了,不知道我这样打比喻对不对。rn而委托相当于一个方法的指针,不能向接口那样可以声明类的任何成员,在使用回调和实现观察者模式的时候再用委托。rn关于动物和植物的区别,参考下面的链接rnhttp://www.czkp.org.cn/kxwx/smam-04.asprn什么情况下用enum?什么情况下用hashtable?什么情况下用位域枚举?rnenum的成员的值都是整形的,是值类型,保存再堆栈上,而hashtable是引用类型,保存在托管堆上。如果声明的东西是不便的,而且是一组常量,可以用枚举,而是键/值对儿的话就用哈希表。rn表示业务实体时,什么情况下用自定义实体类,什么情况下用dataset?rn如果只传递单个实体,并且大多是本地部署,就用自定义实体类表示业务数据,但是如果是一组数据,而且还要排序什么的,就考虑用dataset,虽然自定义实体类也可以放在数组里,但是还要实现某些接口才能够排序,而且好多dataset提供的优势他都没有。再一个自定义实体在序列华方面还得手动执行,而dataset直接就可以了。rn我有个想法,把业务实体的架构用Schema公开出去,向biztalk那样,然后业务实体在传输中使用二进制流传输,传输过去后根据公开的Schema把解序列化的数据转换成所需要的实体,这样如果业务实体的架构改变了,改个Schema就OK了,维护也方便,rn什么情况下用数组,什么情况下用集合?rn能确定成员数量的情况下用数组,不能确定的情况下用集合,.NET下的好多关于集合和数组的接口和类我还没仔细研究过,不知道.NET类库开发者是怎么考虑的。rn什么情况下用静态成员,什么情况下用非静态成员?rn我总感觉要实现全局变量的时候就用静态成员,或者是实现面向服务(SO)的时候用。而如果要实现数据封装和面向对象(00)的时候就应该用非静态成员。rn什么情况下用静态类?什么情况下用非静态类?rn到现在不知道啥是静态类,和VB下的模块差不多?是不是只包含静态成员的类就叫做静态类呀。rn暴露数据的时候什么情况下用公有字段,什么情况下用属性?rn如果是只读字段或者常量字段,就做成公有字段就行了,其它情况下都用属性来包装数据,因为属性可以被派生类继承,而且在SET访问器类还可以做数据完整性的测试,而且还可以在他里面抛出异常,或引发事件等,这就是属性的好处。rn关于字段和属性的使用场合请参考以下链接rnhttp://www.microsoft.com/china/msdn/library/langtool/vcsharp/EffectiveCsharp.mspxrn什么情况下用重写,什么情况下用隐藏?rn虚拟的方法重写,没有声明虚拟的方法隐藏呗。但是JAVA里好像方法默认是可以被重写的,也就是虚拟的,不知道C#怎么考虑的。rnrn对于c#面向对象偶有好多不明白的地方,以上是偶的一些理解,肯定有误解的地方,恳请大家指点补充。rn嫌分少的朋友就不用回贴指正了,想打好基础的朋友和不忍心看偶因为误解OO而误入企图的朋友还请回一下帖子,谢谢了,以前在CSDN发的帖子好多都没人看,唉..谁让哥们没分儿呢.多牢骚了两句,8好意思

蛙蛙请教:CreateParameter的type选择问题

07-22

蛙蛙请教:CreateParameter的type选择问题rn在创建参数的时候要指定参数的类型,可是我发现adovbs.inc里定义的数据类型和sqlserver里定义的数据类型并不是一一对应的,请问一下下面rnrn的类型各对应adovbs.inc里的哪个编号呀rnbitrncharrndatetimernintrnbigintrnsmallintrncharrnvarcharrnncharrnnvarcharrnmoneyrntextrnntextrnbinaryrnvarbinaryrn有的类型我没有列出来,我只列了几个常用的,大家谁要是知道的话,请在我上面列出的类型的后面写上它的序号,谢谢大家了.rn下面是adovbs.inc里定义参数类型的部分,你可以参照一下下面的东西我现在只用过varchar,我用的代码是202,有人说用200,晕,还是大家给偶rnrn个正确答案吧,是不是有varchar既可以用200(adVarChar)也可以用202(adVarWChar)呀,这里有没有什么说法呀?rnrnConst adEmpty = 0rnConst adTinyInt = 16rnConst adSmallInt = 2rnConst adInteger = 3rnConst adBigInt = 20rnConst adUnsignedTinyInt = 17rnConst adUnsignedSmallInt = 18rnConst adUnsignedInt = 19rnConst adUnsignedBigInt = 21rnConst adSingle = 4rnConst adDouble = 5rnConst adCurrency = 6rnConst adDecimal = 14rnConst adNumeric = 131rnConst adBoolean = 11rnConst adError = 10rnConst adUserDefined = 132rnConst adVariant = 12rnConst adIDispatch = 9rnConst adIUnknown = 13rnConst adGUID = 72rnConst adDate = 7rnConst adDBDate = 133rnConst adDBTime = 134rnConst adDBTimeStamp = 135rnConst adBSTR = 8rnConst adChar = 129rnConst adVarChar = 200rnConst adLongVarChar = 201rnConst adWChar = 130rnConst adVarWChar = 202rnConst adLongVarWChar = 203rnConst adBinary = 128rnConst adVarBinary = 204rnConst adLongVarBinary = 205rnConst adChapter = 136rnConst adFileTime = 64rnConst adPropVariant = 138rnConst adVarNumeric = 139rnConst adArray = &H2000

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试